Commit e1d5da20a5

Andrew Kelley <superjoe30@gmail.com>
2017-09-02 10:11:23
rewrite parseh to use AST instead of direct types
some tests still failing
1 parent 0f38955
src/all_types.hpp
@@ -751,6 +751,7 @@ struct AstNodeContainerDecl {
     ZigList<AstNode *> fields;
     ZigList<AstNode *> decls;
     ContainerLayout layout;
+    AstNode *init_arg_expr; // enum(T) or struct(endianness)
 };
 
 struct AstNodeStructField {
src/ast_render.cpp
@@ -112,16 +112,16 @@ static const char *extern_string(bool is_extern) {
     return is_extern ? "extern " : "";
 }
 
-static const char *calling_convention_string(CallingConvention cc) {
-    switch (cc) {
-        case CallingConventionUnspecified: return "";
-        case CallingConventionC: return "extern ";
-        case CallingConventionCold: return "coldcc ";
-        case CallingConventionNaked: return "nakedcc ";
-        case CallingConventionStdcall: return "stdcallcc ";
-    }
-    zig_unreachable();
-}
+//static const char *calling_convention_string(CallingConvention cc) {
+//    switch (cc) {
+//        case CallingConventionUnspecified: return "";
+//        case CallingConventionC: return "extern ";
+//        case CallingConventionCold: return "coldcc ";
+//        case CallingConventionNaked: return "nakedcc ";
+//        case CallingConventionStdcall: return "stdcallcc ";
+//    }
+//    zig_unreachable();
+//}
 
 static const char *inline_string(bool is_inline) {
     return is_inline ? "inline " : "";
@@ -439,8 +439,10 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 fprintf(ar->f, ")");
 
                 AstNode *return_type_node = node->data.fn_proto.return_type;
-                fprintf(ar->f, " -> ");
-                render_node_grouped(ar, return_type_node);
+                if (return_type_node != nullptr) {
+                    fprintf(ar->f, " -> ");
+                    render_node_grouped(ar, return_type_node);
+                }
                 break;
             }
         case NodeTypeFnDef:
@@ -651,16 +653,19 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
             break;
         case NodeTypeContainerDecl:
             {
+                const char *layout_str = layout_string(node->data.container_decl.layout);
                 const char *container_str = container_string(node->data.container_decl.kind);
-                fprintf(ar->f, "%s {\n", container_str);
+                fprintf(ar->f, "%s%s {\n", layout_str, container_str);
                 ar->indent += ar->indent_size;
                 for (size_t field_i = 0; field_i < node->data.container_decl.fields.length; field_i += 1) {
                     AstNode *field_node = node->data.container_decl.fields.at(field_i);
                     assert(field_node->type == NodeTypeStructField);
                     print_indent(ar);
                     print_symbol(ar, field_node->data.struct_field.name);
-                    fprintf(ar->f, ": ");
-                    render_node_grouped(ar, field_node->data.struct_field.type);
+                    if (field_node->data.struct_field.type != nullptr) {
+                        fprintf(ar->f, ": ");
+                        render_node_grouped(ar, field_node->data.struct_field.type);
+                    }
                     fprintf(ar->f, ",\n");
                 }
 
@@ -989,146 +994,3 @@ void ast_render(CodeGen *codegen, FILE *f, AstNode *node, int indent_size) {
 
     render_node_grouped(&ar, node);
 }
-
-static void ast_render_tld_fn(AstRender *ar, Buf *name, TldFn *tld_fn) {
-    FnTableEntry *fn_entry = tld_fn->fn_entry;
-    FnTypeId *fn_type_id = &fn_entry->type_entry->data.fn.fn_type_id;
-    const char *visib_mod_str = visib_mod_string(tld_fn->base.visib_mod);
-    const char *cc_str = calling_convention_string(fn_type_id->cc);
-    fprintf(ar->f, "%s%sfn %s(", visib_mod_str, cc_str, buf_ptr(&fn_entry->symbol_name));
-    for (size_t i = 0; i < fn_type_id->param_count; i += 1) {
-        FnTypeParamInfo *param_info = &fn_type_id->param_info[i];
-        if (i != 0) {
-            fprintf(ar->f, ", ");
-        }
-        if (param_info->is_noalias) {
-            fprintf(ar->f, "noalias ");
-        }
-        Buf *param_name = tld_fn->fn_entry->param_names ? tld_fn->fn_entry->param_names[i] : buf_sprintf("arg%" ZIG_PRI_usize "", i);
-        fprintf(ar->f, "%s: %s", buf_ptr(param_name), buf_ptr(&param_info->type->name));
-    }
-    if (fn_type_id->return_type->id == TypeTableEntryIdVoid) {
-        fprintf(ar->f, ");\n");
-    } else {
-        fprintf(ar->f, ") -> %s;\n", buf_ptr(&fn_type_id->return_type->name));
-    }
-}
-
-static void ast_render_tld_var(AstRender *ar, Buf *name, TldVar *tld_var) {
-    VariableTableEntry *var = tld_var->var;
-    const char *visib_mod_str = visib_mod_string(tld_var->base.visib_mod);
-    const char *const_or_var = const_or_var_string(var->src_is_const);
-    const char *extern_str = extern_string(var->linkage == VarLinkageExternal);
-    fprintf(ar->f, "%s%s%s %s", visib_mod_str, extern_str, const_or_var, buf_ptr(name));
-
-    if (var->value->type->id == TypeTableEntryIdNumLitFloat ||
-        var->value->type->id == TypeTableEntryIdNumLitInt ||
-        var->value->type->id == TypeTableEntryIdMetaType)
-    {
-        // skip type
-    } else {
-        fprintf(ar->f, ": %s", buf_ptr(&var->value->type->name));
-    }
-
-    if (var->value->special == ConstValSpecialRuntime) {
-        fprintf(ar->f, ";\n");
-        return;
-    }
-
-    fprintf(ar->f, " = ");
-
-    if (var->value->special == ConstValSpecialStatic &&
-        var->value->type->id == TypeTableEntryIdMetaType)
-    {
-        TypeTableEntry *type_entry = var->value->data.x_type;
-        if (type_entry->id == TypeTableEntryIdStruct) {
-            const char *layout_str = layout_string(type_entry->data.structure.layout);
-            fprintf(ar->f, "%sstruct {\n", layout_str);
-            if (type_entry->data.structure.complete) {
-                for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
-                    TypeStructField *field = &type_entry->data.structure.fields[i];
-                    fprintf(ar->f, "    ");
-                    print_symbol(ar, field->name);
-                    fprintf(ar->f, ": %s,\n", buf_ptr(&field->type_entry->name));
-                }
-            }
-            fprintf(ar->f, "}");
-        } else if (type_entry->id == TypeTableEntryIdEnum) {
-            const char *layout_str = layout_string(type_entry->data.enumeration.layout);
-            fprintf(ar->f, "%senum {\n", layout_str);
-            if (type_entry->data.enumeration.complete) {
-                for (size_t i = 0; i < type_entry->data.enumeration.src_field_count; i += 1) {
-                    TypeEnumField *field = &type_entry->data.enumeration.fields[i];
-                    fprintf(ar->f, "    ");
-                    print_symbol(ar, field->name);
-                    if (field->type_entry->id == TypeTableEntryIdVoid) {
-                        fprintf(ar->f, ",\n");
-                    } else {
-                        fprintf(ar->f, ": %s,\n", buf_ptr(&field->type_entry->name));
-                    }
-                }
-            }
-            fprintf(ar->f, "}");
-        } else if (type_entry->id == TypeTableEntryIdUnion) {
-            fprintf(ar->f, "union {");
-            fprintf(ar->f, "TODO");
-            fprintf(ar->f, "}");
-        } else if (type_entry->id == TypeTableEntryIdOpaque) {
-            if (buf_eql_buf(&type_entry->name, name)) {
-                fprintf(ar->f, "@OpaqueType()");
-            } else {
-                fprintf(ar->f, "%s", buf_ptr(&type_entry->name));
-            }
-        } else {
-            fprintf(ar->f, "%s", buf_ptr(&type_entry->name));
-        }
-    } else {
-        Buf buf = BUF_INIT;
-        buf_resize(&buf, 0);
-        render_const_value(ar->codegen, &buf, var->value);
-        fprintf(ar->f, "%s", buf_ptr(&buf));
-    }
-
-    fprintf(ar->f, ";\n");
-}
-
-void ast_render_decls(CodeGen *codegen, FILE *f, int indent_size, ImportTableEntry *import) {
-    AstRender ar = {0};
-    ar.codegen = codegen;
-    ar.f = f;
-    ar.indent_size = indent_size;
-    ar.indent = 0;
-
-    auto it = import->decls_scope->decl_table.entry_iterator();
-    for (;;) {
-        auto *entry = it.next();
-        if (!entry)
-            break;
-
-        Tld *tld = entry->value;
-
-        if (tld->name != nullptr && !buf_eql_buf(entry->key, tld->name)) {
-            fprintf(ar.f, "pub const ");
-            print_symbol(&ar, entry->key);
-            fprintf(ar.f, " = %s;\n", buf_ptr(tld->name));
-            continue;
-        }
-
-        switch (tld->id) {
-            case TldIdVar:
-                ast_render_tld_var(&ar, entry->key, (TldVar *)tld);
-                break;
-            case TldIdFn:
-                ast_render_tld_fn(&ar, entry->key, (TldFn *)tld);
-                break;
-            case TldIdContainer:
-                fprintf(stdout, "container\n");
-                break;
-            case TldIdCompTime:
-                fprintf(stdout, "comptime\n");
-                break;
-        }
-    }
-}
-
-
src/ast_render.hpp
@@ -19,7 +19,5 @@ void ast_render(CodeGen *codegen, FILE *f, AstNode *node, int indent_size);
 
 const char *container_string(ContainerKind kind);
 
-void ast_render_decls(CodeGen *codegen, FILE *f, int indent_size, ImportTableEntry *import);
-
 #endif
 
src/bigint.cpp
@@ -165,6 +165,25 @@ void bigint_init_signed(BigInt *dest, int64_t x) {
     dest->data.digit = ((uint64_t)(-(x + 1))) + 1;
 }
 
+void bigint_init_data(BigInt *dest, const uint64_t *digits, size_t digit_count, bool is_negative) {
+    if (digit_count == 0) {
+        return bigint_init_unsigned(dest, 0);
+    } else if (digit_count == 1) {
+        dest->digit_count = 1;
+        dest->data.digit = digits[0];
+        dest->is_negative = is_negative;
+        bigint_normalize(dest);
+        return;
+    }
+
+    dest->digit_count = digit_count;
+    dest->is_negative = is_negative;
+    dest->data.digits = allocate_nonzero<uint64_t>(digit_count);
+    memcpy(dest->data.digits, digits, sizeof(uint64_t) * digit_count);
+
+    bigint_normalize(dest);
+}
+
 void bigint_init_bigint(BigInt *dest, const BigInt *src) {
     if (src->digit_count == 0) {
         return bigint_init_unsigned(dest, 0);
src/bigint.hpp
@@ -34,6 +34,7 @@ void bigint_init_u128(BigInt *dest, unsigned __int128 x);
 void bigint_init_signed(BigInt *dest, int64_t x);
 void bigint_init_bigint(BigInt *dest, const BigInt *src);
 void bigint_init_bigfloat(BigInt *dest, const BigFloat *op);
+void bigint_init_data(BigInt *dest, const uint64_t *digits, size_t digit_count, bool is_negative);
 
 // panics if number won't fit
 uint64_t bigint_as_unsigned(const BigInt *bigint);
src/codegen.hpp
@@ -55,7 +55,6 @@ void codegen_add_assembly(CodeGen *g, Buf *path);
 void codegen_add_object(CodeGen *g, Buf *object_path);
 
 void codegen_parseh(CodeGen *g, Buf *path);
-void codegen_render_ast(CodeGen *g, FILE *f, int indent_size);
 
 
 #endif
src/ir.cpp
@@ -6114,9 +6114,14 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
             return irb->codegen->invalid_instruction;
     }
 
-    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;
+    IrInstruction *return_type;
+    if (node->data.fn_proto.return_type == nullptr) {
+        return_type = ir_build_const_void(irb, parent_scope, node);
+    } else {
+        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, align_value, return_type, is_var_args);
 }
@@ -13358,9 +13363,11 @@ static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruc
     if (ira->codegen->verbose) {
         fprintf(stderr, "\nC imports:\n");
         fprintf(stderr, "-----------\n");
-        ast_render_decls(ira->codegen, stderr, 4, child_import);
+        ast_render(ira->codegen, stderr, child_import->root, 4);
     }
 
+    scan_decls(ira->codegen, child_import->decls_scope, child_import->root);
+
     ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
     out_val->data.x_import = child_import;
     return ira->codegen->builtin_types.entry_namespace;
@@ -15515,65 +15522,3 @@ bool ir_has_side_effects(IrInstruction *instruction) {
     }
     zig_unreachable();
 }
-
-FnTableEntry *ir_create_inline_fn(CodeGen *codegen, Buf *fn_name, VariableTableEntry *var, Scope *parent_scope) {
-    FnTableEntry *fn_entry = create_fn_raw(FnInlineAuto, GlobalLinkageIdInternal);
-    buf_init_from_buf(&fn_entry->symbol_name, fn_name);
-
-    fn_entry->fndef_scope = create_fndef_scope(nullptr, parent_scope, fn_entry);
-    fn_entry->child_scope = &fn_entry->fndef_scope->base;
-
-    assert(var->value->type->id == TypeTableEntryIdMaybe);
-    TypeTableEntry *src_fn_type = var->value->type->data.maybe.child_type;
-    assert(src_fn_type->id == TypeTableEntryIdFn);
-
-    FnTypeId new_fn_type = src_fn_type->data.fn.fn_type_id;
-    new_fn_type.cc = CallingConventionUnspecified;
-
-    fn_entry->type_entry = get_fn_type(codegen, &new_fn_type);
-
-    IrBuilder ir_builder = {0};
-    IrBuilder *irb = &ir_builder;
-
-    irb->codegen = codegen;
-    irb->exec = &fn_entry->ir_executable;
-
-    AstNode *source_node = parent_scope->source_node;
-
-    size_t arg_count = fn_entry->type_entry->data.fn.fn_type_id.param_count;
-    IrInstruction **args = allocate<IrInstruction *>(arg_count);
-    VariableTableEntry **arg_vars = allocate<VariableTableEntry *>(arg_count);
-
-    define_local_param_variables(codegen, fn_entry, arg_vars);
-    Scope *scope = fn_entry->child_scope;
-
-    irb->current_basic_block = ir_build_basic_block(irb, scope, "Entry");
-    // Entry block gets a reference because we enter it to begin.
-    ir_ref_bb(irb->current_basic_block);
-
-    IrInstruction *maybe_fn_ptr = ir_build_var_ptr(irb, scope, source_node, var, true, false);
-    IrInstruction *unwrapped_fn_ptr = ir_build_unwrap_maybe(irb, scope, source_node, maybe_fn_ptr, true);
-    IrInstruction *fn_ref_instruction = ir_build_load_ptr(irb, scope, source_node, unwrapped_fn_ptr);
-
-    for (size_t i = 0; i < arg_count; i += 1) {
-        IrInstruction *var_ptr_instruction = ir_build_var_ptr(irb, scope, source_node, arg_vars[i], true, false);
-        args[i] = ir_build_load_ptr(irb, scope, source_node, var_ptr_instruction);
-    }
-
-    IrInstruction *call_instruction = ir_build_call(irb, scope, source_node, nullptr, fn_ref_instruction,
-            arg_count, args, false, false);
-    ir_build_return(irb, scope, source_node, call_instruction);
-
-    if (codegen->verbose) {
-        fprintf(stderr, "{\n");
-        ir_print(codegen, stderr, &fn_entry->ir_executable, 4);
-        fprintf(stderr, "}\n");
-    }
-
-    analyze_fn_ir(codegen, fn_entry, nullptr);
-
-    codegen->fn_defs.append(fn_entry);
-
-    return fn_entry;
-}
-
src/ir.hpp
@@ -24,6 +24,4 @@ TypeTableEntry *ir_analyze(CodeGen *g, IrExecutable *old_executable, IrExecutabl
 bool ir_has_side_effects(IrInstruction *instruction);
 ConstExprValue *const_ptr_pointee(CodeGen *codegen, ConstExprValue *const_val);
 
-FnTableEntry *ir_create_inline_fn(CodeGen *codegen, Buf *fn_name, VariableTableEntry *var, Scope *parent_scope);
-
 #endif
src/main.cpp
@@ -670,7 +670,7 @@ int main(int argc, char **argv) {
                 return EXIT_SUCCESS;
             } else if (cmd == CmdParseH) {
                 codegen_parseh(g, in_file_buf);
-                ast_render_decls(g, stdout, 4, g->root_import);
+                ast_render(g, stdout, g->root_import->root, 4);
                 if (timing_info)
                     codegen_print_timing_report(g, stdout);
                 return EXIT_SUCCESS;
src/parseh.cpp
@@ -15,6 +15,7 @@
 #include "parseh.hpp"
 #include "parser.hpp"
 
+
 #include <clang/Frontend/ASTUnit.h>
 #include <clang/Frontend/CompilerInstance.h>
 #include <clang/AST/Expr.h>
@@ -28,14 +29,9 @@ struct MacroSymbol {
     Buf *value;
 };
 
-struct GlobalValue {
-    TypeTableEntry *type;
-    bool is_const;
-};
-
 struct Alias {
     Buf *name;
-    Tld *tld;
+    AstNode *node;
 };
 
 struct Context {
@@ -43,30 +39,26 @@ struct Context {
     ZigList<ErrorMsg *> *errors;
     bool warnings_on;
     VisibMod visib_mod;
-
-    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> global_type_table;
-    HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> global_type_table2;
-
-    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> struct_type_table;
-    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> enum_type_table;
-    HashMap<const void *, TypeTableEntry *, ptr_hash, ptr_eq> decl_table;
-    HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> macro_table;
+    AstNode *root;
+    HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> global_type_table;
+    HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> struct_type_table;
+    HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> enum_type_table;
+    HashMap<const void *, AstNode *, ptr_hash, ptr_eq> decl_table;
+    HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> macro_table;
     SourceManager *source_manager;
     ZigList<Alias> aliases;
     ZigList<MacroSymbol> macro_symbols;
     AstNode *source_node;
-    uint32_t next_anon_index;
 
     CodeGen *codegen;
     ASTContext *ctx;
 };
 
-static TypeTableEntry *resolve_qual_type_with_table(Context *c, QualType qt, const Decl *decl,
-    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> *type_table);
-
-static TypeTableEntry *resolve_qual_type(Context *c, QualType qt, const Decl *decl);
-static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_decl);
-static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);
+static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl);
+static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);
+static AstNode * trans_qual_type_with_table(Context *c, QualType qt, const SourceLocation &source_loc,
+        HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> *type_table);
+static AstNode * trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc);
 
 
 __attribute__ ((format (printf, 3, 4)))
@@ -93,522 +85,226 @@ static void emit_warning(Context *c, const SourceLocation &sl, const char *forma
     fprintf(stderr, "%s:%u:%u: warning: %s\n", buf_ptr(path), line, column, buf_ptr(msg));
 }
 
-static uint32_t get_next_anon_index(Context *c) {
-    uint32_t result = c->next_anon_index;
-    c->next_anon_index += 1;
-    return result;
-}
-
-static void add_global_alias(Context *c, Buf *name, Tld *tld) {
-    c->import->decls_scope->decl_table.put(name, tld);
-}
-
-static void add_global_weak_alias(Context *c, Buf *name, Tld *tld) {
+static void add_global_weak_alias(Context *c, Buf *name, AstNode *node) {
     Alias *alias = c->aliases.add_one();
     alias->name = name;
-    alias->tld = tld;
+    alias->node = node;
 }
 
-static void add_global(Context *c, Tld *tld) {
-    return add_global_alias(c, tld->name, tld);
+static AstNode * trans_create_node(Context *c, NodeType id) {
+    AstNode *node = allocate<AstNode>(1);
+    node->type = id;
+    node->owner = c->import;
+    // TODO line/column. mapping to C file??
+    return node;
 }
 
-static Tld *get_global(Context *c, Buf *name) {
-    {
-        auto entry = c->import->decls_scope->decl_table.maybe_get(name);
-        if (entry)
-            return entry->value;
-    }
-    {
-        auto entry = c->macro_table.maybe_get(name);
-        if (entry)
-            return entry->value;
-    }
-    return nullptr;
+static AstNode *trans_create_node_float_lit(Context *c, double value) {
+    AstNode *node = trans_create_node(c, NodeTypeFloatLiteral);
+    node->data.float_literal.bigfloat = allocate<BigFloat>(1);
+    bigfloat_init_64(node->data.float_literal.bigfloat, value);
+    return node;
 }
 
-static const char *decl_name(const Decl *decl) {
-    const NamedDecl *named_decl = static_cast<const NamedDecl *>(decl);
-    return (const char *)named_decl->getName().bytes_begin();
+static AstNode *trans_create_node_symbol(Context *c, Buf *name) {
+    AstNode *node = trans_create_node(c, NodeTypeSymbol);
+    node->data.symbol_expr.symbol = name;
+    return node;
 }
 
-static void parseh_init_tld(Context *c, Tld *tld, TldId id, Buf *name) {
-    init_tld(tld, id, name, c->visib_mod, c->source_node, &c->import->decls_scope->base);
-    tld->resolution = TldResolutionOk;
-    tld->import = c->import;
+static AstNode *trans_create_node_symbol_str(Context *c, const char *name) {
+    return trans_create_node_symbol(c, buf_create_from_str(name));
 }
 
-static Tld *create_inline_fn_tld(Context *c, Buf *fn_name, TldVar *tld_var) {
-    TldFn *tld_fn = allocate<TldFn>(1);
-    parseh_init_tld(c, &tld_fn->base, TldIdFn, fn_name);
-    tld_fn->fn_entry = ir_create_inline_fn(c->codegen, fn_name, tld_var->var, &c->import->decls_scope->base);
-    return &tld_fn->base;
+static AstNode *trans_create_node_builtin_fn_call(Context *c, Buf *name) {
+    AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
+    node->data.fn_call_expr.fn_ref_expr = trans_create_node_symbol(c, name);
+    node->data.fn_call_expr.is_builtin = true;
+    return node;
 }
 
-static TldVar *create_global_var(Context *c, Buf *name, ConstExprValue *var_value, bool is_const) {
-    auto entry = c->import->decls_scope->decl_table.maybe_get(name);
-    if (entry) {
-        Tld *existing_tld = entry->value;
-        assert(existing_tld->id == TldIdVar);
-        return (TldVar *)existing_tld;
-    }
-    TldVar *tld_var = allocate<TldVar>(1);
-    parseh_init_tld(c, &tld_var->base, TldIdVar, name);
-    tld_var->var = add_variable(c->codegen, c->source_node, &c->import->decls_scope->base,
-            name, is_const, var_value, &tld_var->base);
-    c->codegen->global_vars.append(tld_var);
-    return tld_var;
+static AstNode *trans_create_node_builtin_fn_call_str(Context *c, const char *name) {
+    return trans_create_node_builtin_fn_call(c, buf_create_from_str(name));
 }
 
-static Tld *create_global_str_lit_var(Context *c, Buf *name, Buf *value) {
-    TldVar *tld_var = create_global_var(c, name, create_const_c_str_lit(c->codegen, value), true);
-    return &tld_var->base;
+static AstNode *trans_create_node_opaque(Context *c) {
+    return trans_create_node_builtin_fn_call_str(c, "opaque");
 }
 
-static Tld *create_global_num_lit_unsigned_negative_type(Context *c, Buf *name, uint64_t x, bool negative, TypeTableEntry *type_entry) {
-    ConstExprValue *var_val = create_const_unsigned_negative(type_entry, x, negative);
-    TldVar *tld_var = create_global_var(c, name, var_val, true);
-    return &tld_var->base;
+static AstNode *trans_create_node_field_access(Context *c, AstNode *container, Buf *field_name) {
+    AstNode *node = trans_create_node(c, NodeTypeFieldAccessExpr);
+    node->data.field_access_expr.struct_expr = container;
+    node->data.field_access_expr.field_name = field_name;
+    return node;
 }
 
-static Tld *create_global_num_lit_unsigned_negative(Context *c, Buf *name, uint64_t x, bool negative) {
-    return create_global_num_lit_unsigned_negative_type(c, name, x, negative, c->codegen->builtin_types.entry_num_lit_int);
+static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *child_node) {
+    AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr);
+    node->data.prefix_op_expr.prefix_op = op;
+    node->data.prefix_op_expr.primary_expr = child_node;
+    return node;
 }
 
-static Tld *create_global_num_lit_float(Context *c, Buf *name, double value) {
-    ConstExprValue *var_val = create_const_float(c->codegen->builtin_types.entry_num_lit_float, value);
-    TldVar *tld_var = create_global_var(c, name, var_val, true);
-    return &tld_var->base;
+static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_volatile, AstNode *child_node) {
+    AstNode *node = trans_create_node(c, NodeTypeAddrOfExpr);
+    node->data.addr_of_expr.is_const = is_const;
+    node->data.addr_of_expr.is_volatile = is_volatile;
+    node->data.addr_of_expr.op_expr = child_node;
+    return node;
 }
 
-static ConstExprValue *create_const_int_ap(Context *c, TypeTableEntry *type, const Decl *source_decl,
-        const llvm::APSInt &aps_int)
-{
-    if (aps_int.isSigned()) {
-        if (aps_int > INT64_MAX || aps_int < INT64_MIN) {
-            emit_warning(c, source_decl->getLocation(), "integer overflow\n");
-            return nullptr;
-        } else {
-            return create_const_signed(type, aps_int.getExtValue());
-        }
-    } else {
-        if (aps_int > INT64_MAX) {
-            emit_warning(c, source_decl->getLocation(), "integer overflow\n");
-            return nullptr;
-        } else {
-            return create_const_unsigned_negative(type, aps_int.getExtValue(), false);
-        }
-    }
+static AstNode *trans_create_node_str_lit_c(Context *c, Buf *buf) {
+    AstNode *node = trans_create_node(c, NodeTypeStringLiteral);
+    node->data.string_literal.buf = buf;
+    node->data.string_literal.c = true;
+    return node;
 }
 
-static Tld *create_global_num_lit_ap(Context *c, const Decl *source_decl, Buf *name,
-        const llvm::APSInt &aps_int)
-{
-    ConstExprValue *const_value = create_const_int_ap(c, c->codegen->builtin_types.entry_num_lit_int,
-            source_decl, aps_int);
-    if (!const_value)
-        return nullptr;
-    TldVar *tld_var = create_global_var(c, name, const_value, true);
-    return &tld_var->base;
+static AstNode *trans_create_node_unsigned_negative(Context *c, uint64_t x, bool is_negative) {
+    AstNode *node = trans_create_node(c, NodeTypeIntLiteral);
+    node->data.int_literal.bigint = allocate<BigInt>(1);
+    bigint_init_data(node->data.int_literal.bigint, &x, 1, is_negative);
+    return node;
 }
 
-
-static Tld *add_const_type(Context *c, Buf *name, TypeTableEntry *type_entry) {
-    ConstExprValue *var_value = create_const_type(c->codegen, type_entry);
-    TldVar *tld_var = create_global_var(c, name, var_value, true);
-    add_global(c, &tld_var->base);
-
-    c->global_type_table.put(name, type_entry);
-    return &tld_var->base;
+static AstNode *trans_create_node_unsigned(Context *c, uint64_t x) {
+    return trans_create_node_unsigned_negative(c, x, false);
 }
 
-static Tld *add_container_tld(Context *c, TypeTableEntry *type_entry) {
-    return add_const_type(c, &type_entry->name, type_entry);
+static AstNode *trans_create_node_cast(Context *c, AstNode *dest, AstNode *src) {
+    AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
+    node->data.fn_call_expr.fn_ref_expr = dest;
+    node->data.fn_call_expr.params.resize(1);
+    node->data.fn_call_expr.params.items[0] = src;
+    return node;
 }
 
-static bool is_c_void_type(Context *c, TypeTableEntry *type_entry) {
-    return (type_entry == c->codegen->builtin_types.entry_c_void);
+static AstNode *trans_create_node_unsigned_negative_type(Context *c, uint64_t x, bool is_negative,
+        const char *type_name)
+{
+    AstNode *lit_node = trans_create_node_unsigned_negative(c, x, is_negative);
+    return trans_create_node_cast(c, trans_create_node_symbol_str(c, type_name), lit_node);
 }
 
-static bool qual_type_child_is_fn_proto(const QualType &qt) {
-    if (qt.getTypePtr()->getTypeClass() == Type::Paren) {
-        const ParenType *paren_type = static_cast<const ParenType *>(qt.getTypePtr());
-        if (paren_type->getInnerType()->getTypeClass() == Type::FunctionProto) {
-            return true;
-        }
-    } else if (qt.getTypePtr()->getTypeClass() == Type::Attributed) {
-        const AttributedType *attr_type = static_cast<const AttributedType *>(qt.getTypePtr());
-        return qual_type_child_is_fn_proto(attr_type->getEquivalentType());
-    }
-    return false;
+static AstNode *trans_create_node_array_type(Context *c, AstNode *size_node, AstNode *child_type_node) {
+    AstNode *node = trans_create_node(c, NodeTypeArrayType);
+    node->data.array_type.size = size_node;
+    node->data.array_type.child_type = child_type_node;
+    return node;
 }
 
-static TypeTableEntry *resolve_type_with_table(Context *c, const Type *ty, const Decl *decl,
-    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> *type_table)
+static AstNode *trans_create_node_var_decl(Context *c, bool is_const, Buf *var_name, AstNode *type_node,
+        AstNode *init_node)
 {
-    switch (ty->getTypeClass()) {
-        case Type::Builtin:
-            {
-                const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
-                switch (builtin_ty->getKind()) {
-                    case BuiltinType::Void:
-                        return c->codegen->builtin_types.entry_c_void;
-                    case BuiltinType::Bool:
-                        return c->codegen->builtin_types.entry_bool;
-                    case BuiltinType::Char_U:
-                    case BuiltinType::UChar:
-                    case BuiltinType::Char_S:
-                        return c->codegen->builtin_types.entry_u8;
-                    case BuiltinType::SChar:
-                        return c->codegen->builtin_types.entry_i8;
-                    case BuiltinType::UShort:
-                        return get_c_int_type(c->codegen, CIntTypeUShort);
-                    case BuiltinType::UInt:
-                        return get_c_int_type(c->codegen, CIntTypeUInt);
-                    case BuiltinType::ULong:
-                        return get_c_int_type(c->codegen, CIntTypeULong);
-                    case BuiltinType::ULongLong:
-                        return get_c_int_type(c->codegen, CIntTypeULongLong);
-                    case BuiltinType::Short:
-                        return get_c_int_type(c->codegen, CIntTypeShort);
-                    case BuiltinType::Int:
-                        return get_c_int_type(c->codegen, CIntTypeInt);
-                    case BuiltinType::Long:
-                        return get_c_int_type(c->codegen, CIntTypeLong);
-                    case BuiltinType::LongLong:
-                        return get_c_int_type(c->codegen, CIntTypeLongLong);
-                    case BuiltinType::UInt128:
-                        return c->codegen->builtin_types.entry_u128;
-                    case BuiltinType::Int128:
-                        return c->codegen->builtin_types.entry_i128;
-                    case BuiltinType::Float:
-                        return c->codegen->builtin_types.entry_f32;
-                    case BuiltinType::Double:
-                        return c->codegen->builtin_types.entry_f64;
-                    case BuiltinType::Float128:
-                        return c->codegen->builtin_types.entry_f128;
-                    case BuiltinType::LongDouble:
-                        return c->codegen->builtin_types.entry_c_longdouble;
-                    case BuiltinType::WChar_U:
-                    case BuiltinType::Char16:
-                    case BuiltinType::Char32:
-                    case BuiltinType::WChar_S:
-                    case BuiltinType::Half:
-                    case BuiltinType::NullPtr:
-                    case BuiltinType::ObjCId:
-                    case BuiltinType::ObjCClass:
-                    case BuiltinType::ObjCSel:
-                    case BuiltinType::OMPArraySection:
-                    case BuiltinType::Dependent:
-                    case BuiltinType::Overload:
-                    case BuiltinType::BoundMember:
-                    case BuiltinType::PseudoObject:
-                    case BuiltinType::UnknownAny:
-                    case BuiltinType::BuiltinFn:
-                    case BuiltinType::ARCUnbridgedCast:
+    AstNode *node = trans_create_node(c, NodeTypeVariableDeclaration);
+    node->data.variable_declaration.visib_mod = c->visib_mod;
+    node->data.variable_declaration.symbol = var_name;
+    node->data.variable_declaration.is_const = is_const;
+    node->data.variable_declaration.type = type_node;
+    node->data.variable_declaration.expr = init_node;
+    return node;
+}
 
-                    case BuiltinType::OCLImage1dRO:
-                    case BuiltinType::OCLImage1dArrayRO:
-                    case BuiltinType::OCLImage1dBufferRO:
-                    case BuiltinType::OCLImage2dRO:
-                    case BuiltinType::OCLImage2dArrayRO:
-                    case BuiltinType::OCLImage2dDepthRO:
-                    case BuiltinType::OCLImage2dArrayDepthRO:
-                    case BuiltinType::OCLImage2dMSAARO:
-                    case BuiltinType::OCLImage2dArrayMSAARO:
-                    case BuiltinType::OCLImage2dMSAADepthRO:
-                    case BuiltinType::OCLImage2dArrayMSAADepthRO:
-                    case BuiltinType::OCLImage3dRO:
-                    case BuiltinType::OCLImage1dWO:
-                    case BuiltinType::OCLImage1dArrayWO:
-                    case BuiltinType::OCLImage1dBufferWO:
-                    case BuiltinType::OCLImage2dWO:
-                    case BuiltinType::OCLImage2dArrayWO:
-                    case BuiltinType::OCLImage2dDepthWO:
-                    case BuiltinType::OCLImage2dArrayDepthWO:
-                    case BuiltinType::OCLImage2dMSAAWO:
-                    case BuiltinType::OCLImage2dArrayMSAAWO:
-                    case BuiltinType::OCLImage2dMSAADepthWO:
-                    case BuiltinType::OCLImage2dArrayMSAADepthWO:
-                    case BuiltinType::OCLImage3dWO:
-                    case BuiltinType::OCLImage1dRW:
-                    case BuiltinType::OCLImage1dArrayRW:
-                    case BuiltinType::OCLImage1dBufferRW:
-                    case BuiltinType::OCLImage2dRW:
-                    case BuiltinType::OCLImage2dArrayRW:
-                    case BuiltinType::OCLImage2dDepthRW:
-                    case BuiltinType::OCLImage2dArrayDepthRW:
-                    case BuiltinType::OCLImage2dMSAARW:
-                    case BuiltinType::OCLImage2dArrayMSAARW:
-                    case BuiltinType::OCLImage2dMSAADepthRW:
-                    case BuiltinType::OCLImage2dArrayMSAADepthRW:
-                    case BuiltinType::OCLImage3dRW:
-                    case BuiltinType::OCLSampler:
-                    case BuiltinType::OCLEvent:
-                    case BuiltinType::OCLClkEvent:
-                    case BuiltinType::OCLQueue:
-                    case BuiltinType::OCLReserveID:
-                        emit_warning(c, decl->getLocation(), "missed a builtin type");
-                        return c->codegen->builtin_types.entry_invalid;
-                }
-                break;
-            }
-        case Type::Pointer:
-            {
-                const PointerType *pointer_ty = static_cast<const PointerType*>(ty);
-                QualType child_qt = pointer_ty->getPointeeType();
-                TypeTableEntry *child_type = resolve_qual_type(c, child_qt, decl);
-                if (type_is_invalid(child_type)) {
-                    emit_warning(c, decl->getLocation(), "pointer to unresolved type");
-                    return c->codegen->builtin_types.entry_invalid;
-                }
 
-                if (qual_type_child_is_fn_proto(child_qt)) {
-                    return get_maybe_type(c->codegen, child_type);
-                }
-                bool is_const = child_qt.isConstQualified();
+static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, Buf *var_name, AstNode *src_proto_node) {
+    AstNode *fn_def = trans_create_node(c, NodeTypeFnDef);
+    AstNode *fn_proto = trans_create_node(c, NodeTypeFnProto);
+    fn_proto->data.fn_proto.visib_mod = c->visib_mod;;
+    fn_proto->data.fn_proto.name = fn_name;
+    fn_proto->data.fn_proto.is_inline = true;
+    fn_proto->data.fn_proto.return_type = src_proto_node->data.fn_proto.return_type; // TODO ok for these to alias?
 
-                TypeTableEntry *non_null_pointer_type = get_pointer_to_type(c->codegen, child_type, is_const);
-                return get_maybe_type(c->codegen, non_null_pointer_type);
-            }
-        case Type::Typedef:
-            {
-                const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
-                const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
-                Buf *type_name = buf_create_from_str(decl_name(typedef_decl));
-                if (buf_eql_str(type_name, "uint8_t")) {
-                    return c->codegen->builtin_types.entry_u8;
-                } else if (buf_eql_str(type_name, "int8_t")) {
-                    return c->codegen->builtin_types.entry_i8;
-                } else if (buf_eql_str(type_name, "uint16_t")) {
-                    return c->codegen->builtin_types.entry_u16;
-                } else if (buf_eql_str(type_name, "int16_t")) {
-                    return c->codegen->builtin_types.entry_i16;
-                } else if (buf_eql_str(type_name, "uint32_t")) {
-                    return c->codegen->builtin_types.entry_u32;
-                } else if (buf_eql_str(type_name, "int32_t")) {
-                    return c->codegen->builtin_types.entry_i32;
-                } else if (buf_eql_str(type_name, "uint64_t")) {
-                    return c->codegen->builtin_types.entry_u64;
-                } else if (buf_eql_str(type_name, "int64_t")) {
-                    return c->codegen->builtin_types.entry_i64;
-                } else if (buf_eql_str(type_name, "intptr_t")) {
-                    return c->codegen->builtin_types.entry_isize;
-                } else if (buf_eql_str(type_name, "uintptr_t")) {
-                    return c->codegen->builtin_types.entry_usize;
-                } else {
-                    auto entry = type_table->maybe_get(type_name);
-                    if (entry) {
-                        if (type_is_invalid(entry->value)) {
-                            return c->codegen->builtin_types.entry_invalid;
-                        } else {
-                            return entry->value;
-                        }
-                    } else {
-                        return c->codegen->builtin_types.entry_invalid;
-                    }
-                }
-            }
-        case Type::Elaborated:
-            {
-                const ElaboratedType *elaborated_ty = static_cast<const ElaboratedType*>(ty);
-                switch (elaborated_ty->getKeyword()) {
-                    case ETK_Struct:
-                        return resolve_qual_type_with_table(c, elaborated_ty->getNamedType(),
-                                decl, &c->struct_type_table);
-                    case ETK_Enum:
-                        return resolve_qual_type_with_table(c, elaborated_ty->getNamedType(),
-                                decl, &c->enum_type_table);
-                    case ETK_Interface:
-                    case ETK_Union:
-                    case ETK_Class:
-                    case ETK_Typename:
-                    case ETK_None:
-                        emit_warning(c, decl->getLocation(), "unsupported elaborated type");
-                        return c->codegen->builtin_types.entry_invalid;
-                }
-            }
-        case Type::FunctionProto:
-            {
-                const FunctionProtoType *fn_proto_ty = static_cast<const FunctionProtoType*>(ty);
+    fn_def->data.fn_def.fn_proto = fn_proto;
+    fn_proto->data.fn_proto.fn_def_node = fn_def;
 
-                switch (fn_proto_ty->getCallConv()) {
-                    case CC_C:           // __attribute__((cdecl))
-                        break;
-                    case CC_X86StdCall:  // __attribute__((stdcall))
-                        emit_warning(c, decl->getLocation(), "function type has x86 stdcall calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_X86FastCall: // __attribute__((fastcall))
-                        emit_warning(c, decl->getLocation(), "function type has x86 fastcall calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_X86ThisCall: // __attribute__((thiscall))
-                        emit_warning(c, decl->getLocation(), "function type has x86 thiscall calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_X86VectorCall: // __attribute__((vectorcall))
-                        emit_warning(c, decl->getLocation(), "function type has x86 vectorcall calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_X86Pascal:   // __attribute__((pascal))
-                        emit_warning(c, decl->getLocation(), "function type has x86 pascal calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_Win64: // __attribute__((ms_abi))
-                        emit_warning(c, decl->getLocation(), "function type has win64 calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_X86_64SysV:  // __attribute__((sysv_abi))
-                        emit_warning(c, decl->getLocation(), "function type has x86 64sysv calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_X86RegCall:
-                        emit_warning(c, decl->getLocation(), "function type has x86 reg calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_AAPCS:       // __attribute__((pcs("aapcs")))
-                        emit_warning(c, decl->getLocation(), "function type has aapcs calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_AAPCS_VFP:   // __attribute__((pcs("aapcs-vfp")))
-                        emit_warning(c, decl->getLocation(), "function type has aapcs-vfp calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_IntelOclBicc: // __attribute__((intel_ocl_bicc))
-                        emit_warning(c, decl->getLocation(), "function type has intel_ocl_bicc calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_SpirFunction: // default for OpenCL functions on SPIR target
-                        emit_warning(c, decl->getLocation(), "function type has SPIR function calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_OpenCLKernel:
-                        emit_warning(c, decl->getLocation(), "function type has OpenCLKernel calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_Swift:
-                        emit_warning(c, decl->getLocation(), "function type has Swift calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_PreserveMost:
-                        emit_warning(c, decl->getLocation(), "function type has PreserveMost calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                    case CC_PreserveAll:
-                        emit_warning(c, decl->getLocation(), "function type has PreserveAll calling convention");
-                        return c->codegen->builtin_types.entry_invalid;
-                }
+    AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, trans_create_node_symbol(c, var_name));
+    AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr);
+    fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node;
 
-                FnTypeId fn_type_id = {0};
-                fn_type_id.cc = CallingConventionC;
-                fn_type_id.is_var_args = fn_proto_ty->isVariadic();
-                fn_type_id.param_count = fn_proto_ty->getNumParams();
+    for (size_t i = 0; i < src_proto_node->data.fn_proto.params.length; i += 1) {
+        AstNode *src_param_node = src_proto_node->data.fn_proto.params.at(i);
+        Buf *param_name = src_param_node->data.param_decl.name;
 
+        AstNode *dest_param_node = trans_create_node(c, NodeTypeParamDecl);
+        dest_param_node->data.param_decl.name = param_name;
+        dest_param_node->data.param_decl.type = src_param_node->data.param_decl.type;
+        dest_param_node->data.param_decl.is_noalias = src_param_node->data.param_decl.is_noalias;
+        fn_proto->data.fn_proto.params.append(dest_param_node);
 
-                if (fn_proto_ty->getNoReturnAttr()) {
-                    fn_type_id.return_type = c->codegen->builtin_types.entry_unreachable;
-                } else {
-                    fn_type_id.return_type = resolve_qual_type(c, fn_proto_ty->getReturnType(), decl);
-                    if (type_is_invalid(fn_type_id.return_type)) {
-                        emit_warning(c, decl->getLocation(), "unresolved function proto return type");
-                        return c->codegen->builtin_types.entry_invalid;
-                    }
-                    // convert c_void to actual void (only for return type)
-                    if (is_c_void_type(c, fn_type_id.return_type)) {
-                        fn_type_id.return_type = c->codegen->builtin_types.entry_void;
-                    }
-                }
+        fn_call_node->data.fn_call_expr.params.append(trans_create_node_symbol(c, param_name));
 
-                fn_type_id.param_info = allocate_nonzero<FnTypeParamInfo>(fn_type_id.param_count);
-                for (size_t i = 0; i < fn_type_id.param_count; i += 1) {
-                    QualType qt = fn_proto_ty->getParamType(i);
-                    TypeTableEntry *param_type = resolve_qual_type(c, qt, decl);
+    }
 
-                    if (type_is_invalid(param_type)) {
-                        emit_warning(c, decl->getLocation(), "unresolved function proto parameter type");
-                        return c->codegen->builtin_types.entry_invalid;
-                    }
+    AstNode *block = trans_create_node(c, NodeTypeBlock);
+    block->data.block.statements.resize(1);
+    block->data.block.statements.items[0] = fn_call_node;
 
-                    FnTypeParamInfo *param_info = &fn_type_id.param_info[i];
-                    param_info->type = param_type;
-                    param_info->is_noalias = qt.isRestrictQualified();
-                }
+    fn_def->data.fn_def.body = block;
+    return fn_def;
+}
 
-                return get_fn_type(c->codegen, &fn_type_id);
-            }
-        case Type::Record:
-            {
-                const RecordType *record_ty = static_cast<const RecordType*>(ty);
-                return resolve_record_decl(c, record_ty->getDecl());
-            }
-        case Type::Enum:
-            {
-                const EnumType *enum_ty = static_cast<const EnumType*>(ty);
-                return resolve_enum_decl(c, enum_ty->getDecl());
+static AstNode *get_global(Context *c, Buf *name) {
+    for (size_t i = 0; i < c->root->data.root.top_level_decls.length; i += 1) {
+        AstNode *decl_node = c->root->data.root.top_level_decls.items[i];
+        if (decl_node->type == NodeTypeVariableDeclaration) {
+            if (buf_eql_buf(decl_node->data.variable_declaration.symbol, name)) {
+                return decl_node;
             }
-        case Type::ConstantArray:
-            {
-                const ConstantArrayType *const_arr_ty = static_cast<const ConstantArrayType *>(ty);
-                TypeTableEntry *child_type = resolve_qual_type(c, const_arr_ty->getElementType(), decl);
-                if (child_type->id == TypeTableEntryIdInvalid) {
-                    emit_warning(c, decl->getLocation(), "unresolved array element type");
-                    return child_type;
-                }
-                uint64_t size = const_arr_ty->getSize().getLimitedValue();
-                return get_array_type(c->codegen, child_type, size);
+        } else if (decl_node->type == NodeTypeFnDef) {
+            if (buf_eql_buf(decl_node->data.fn_def.fn_proto->data.fn_proto.name, name)) {
+                return decl_node;
             }
-        case Type::Paren:
-            {
-                const ParenType *paren_ty = static_cast<const ParenType *>(ty);
-                return resolve_qual_type(c, paren_ty->getInnerType(), decl);
+        } else if (decl_node->type == NodeTypeFnProto) {
+            if (buf_eql_buf(decl_node->data.fn_proto.name, name)) {
+                return decl_node;
             }
-        case Type::Decayed:
-            {
-                const DecayedType *decayed_ty = static_cast<const DecayedType *>(ty);
-                return resolve_qual_type(c, decayed_ty->getDecayedType(), decl);
-            }
-        case Type::Attributed:
-            {
-                const AttributedType *attributed_ty = static_cast<const AttributedType *>(ty);
-                return resolve_qual_type(c, attributed_ty->getEquivalentType(), decl);
-            }
-        case Type::BlockPointer:
-        case Type::LValueReference:
-        case Type::RValueReference:
-        case Type::MemberPointer:
-        case Type::IncompleteArray:
-        case Type::VariableArray:
-        case Type::DependentSizedArray:
-        case Type::DependentSizedExtVector:
-        case Type::Vector:
-        case Type::ExtVector:
-        case Type::FunctionNoProto:
-        case Type::UnresolvedUsing:
-        case Type::Adjusted:
-        case Type::TypeOfExpr:
-        case Type::TypeOf:
-        case Type::Decltype:
-        case Type::UnaryTransform:
-        case Type::TemplateTypeParm:
-        case Type::SubstTemplateTypeParm:
-        case Type::SubstTemplateTypeParmPack:
-        case Type::TemplateSpecialization:
-        case Type::Auto:
-        case Type::InjectedClassName:
-        case Type::DependentName:
-        case Type::DependentTemplateSpecialization:
-        case Type::PackExpansion:
-        case Type::ObjCObject:
-        case Type::ObjCInterface:
-        case Type::Complex:
-        case Type::ObjCObjectPointer:
-        case Type::Atomic:
-        case Type::Pipe:
-        case Type::ObjCTypeParam:
-        case Type::DeducedTemplateSpecialization:
-            emit_warning(c, decl->getLocation(), "missed a '%s' type", ty->getTypeClassName());
-            return c->codegen->builtin_types.entry_invalid;
+        }
     }
-    zig_unreachable();
+    {
+        auto entry = c->macro_table.maybe_get(name);
+        if (entry)
+            return entry->value;
+    }
+    return nullptr;
+}
+
+static AstNode *add_global_var(Context *c, Buf *var_name, AstNode *value_node) {
+    bool is_const = true;
+    AstNode *type_node = nullptr;
+    AstNode *node = trans_create_node_var_decl(c, is_const, var_name, type_node, value_node);
+    c->root->data.root.top_level_decls.append(node);
+    return node;
+}
+
+static const char *decl_name(const Decl *decl) {
+    const NamedDecl *named_decl = static_cast<const NamedDecl *>(decl);
+    return (const char *)named_decl->getName().bytes_begin();
 }
 
-static TypeTableEntry *resolve_qual_type_with_table(Context *c, QualType qt, const Decl *decl,
-    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> *type_table)
-{
-    return resolve_type_with_table(c, qt.getTypePtr(), decl, type_table);
+static AstNode *trans_create_node_apint(Context *c, const llvm::APSInt &aps_int) {
+    AstNode *node = trans_create_node(c, NodeTypeIntLiteral);
+    node->data.int_literal.bigint = allocate<BigInt>(1);
+    bigint_init_data(node->data.int_literal.bigint, aps_int.getRawData(), aps_int.getNumWords(), aps_int.isNegative());
+    return node;
+
 }
 
-static TypeTableEntry *resolve_qual_type(Context *c, QualType qt, const Decl *decl) {
-    return resolve_qual_type_with_table(c, qt, decl, &c->global_type_table);
+static bool is_c_void_type(AstNode *node) {
+    return (node->type == NodeTypeSymbol && buf_eql_str(node->data.symbol_expr.symbol, "c_void"));
 }
 
-#include "ast_render.hpp"
+static bool qual_type_child_is_fn_proto(const QualType &qt) {
+    if (qt.getTypePtr()->getTypeClass() == Type::Paren) {
+        const ParenType *paren_type = static_cast<const ParenType *>(qt.getTypePtr());
+        if (paren_type->getInnerType()->getTypeClass() == Type::FunctionProto) {
+            return true;
+        }
+    } else if (qt.getTypePtr()->getTypeClass() == Type::Attributed) {
+        const AttributedType *attr_type = static_cast<const AttributedType *>(qt.getTypePtr());
+        return qual_type_child_is_fn_proto(attr_type->getEquivalentType());
+    }
+    return false;
+}
 
 static bool c_is_signed_integer(Context *c, QualType qt) {
     const Type *c_type = qt.getTypePtr();
@@ -668,19 +364,12 @@ static bool c_is_float(Context *c, QualType qt) {
 }
 
 static AstNode * trans_stmt(Context *c, AstNode *block, Stmt *stmt);
-static AstNode * trans_create_node(Context *c, NodeType id);
 static AstNode * trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc);
 
 static AstNode * trans_expr(Context *c, AstNode *block, Expr *expr) {
     return trans_stmt(c, block, expr);
 }
 
-static AstNode *trans_create_symbol_node(Context *c, const char * name) {
-    AstNode *node = trans_create_node(c, NodeTypeSymbol);
-    node->data.symbol_expr.symbol = buf_create_from_str(name);
-    return node;
-}
-
 static AstNode *trans_type_with_table(Context *c, const Type *ty, const SourceLocation &source_loc,
     HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> *type_table)
 {
@@ -690,43 +379,43 @@ static AstNode *trans_type_with_table(Context *c, const Type *ty, const SourceLo
                 const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
                 switch (builtin_ty->getKind()) {
                     case BuiltinType::Void:
-                        return trans_create_symbol_node(c, "c_void");
+                        return trans_create_node_symbol_str(c, "c_void");
                     case BuiltinType::Bool:
-                        return trans_create_symbol_node(c, "bool");
+                        return trans_create_node_symbol_str(c, "bool");
                     case BuiltinType::Char_U:
                     case BuiltinType::UChar:
                     case BuiltinType::Char_S:
-                        return trans_create_symbol_node(c, "u8");
+                        return trans_create_node_symbol_str(c, "u8");
                     case BuiltinType::SChar:
-                        return trans_create_symbol_node(c, "i8");
+                        return trans_create_node_symbol_str(c, "i8");
                     case BuiltinType::UShort:
-                        return trans_create_symbol_node(c, "c_ushort");
+                        return trans_create_node_symbol_str(c, "c_ushort");
                     case BuiltinType::UInt:
-                        return trans_create_symbol_node(c, "c_uint");
+                        return trans_create_node_symbol_str(c, "c_uint");
                     case BuiltinType::ULong:
-                        return trans_create_symbol_node(c, "c_ulong");
+                        return trans_create_node_symbol_str(c, "c_ulong");
                     case BuiltinType::ULongLong:
-                        return trans_create_symbol_node(c, "c_ulonglong");
+                        return trans_create_node_symbol_str(c, "c_ulonglong");
                     case BuiltinType::Short:
-                        return trans_create_symbol_node(c, "c_short");
+                        return trans_create_node_symbol_str(c, "c_short");
                     case BuiltinType::Int:
-                        return trans_create_symbol_node(c, "c_int");
+                        return trans_create_node_symbol_str(c, "c_int");
                     case BuiltinType::Long:
-                        return trans_create_symbol_node(c, "c_long");
+                        return trans_create_node_symbol_str(c, "c_long");
                     case BuiltinType::LongLong:
-                        return trans_create_symbol_node(c, "c_longlong");
+                        return trans_create_node_symbol_str(c, "c_longlong");
                     case BuiltinType::UInt128:
-                        return trans_create_symbol_node(c, "u128");
+                        return trans_create_node_symbol_str(c, "u128");
                     case BuiltinType::Int128:
-                        return trans_create_symbol_node(c, "i128");
+                        return trans_create_node_symbol_str(c, "i128");
                     case BuiltinType::Float:
-                        return trans_create_symbol_node(c, "f32");
+                        return trans_create_node_symbol_str(c, "f32");
                     case BuiltinType::Double:
-                        return trans_create_symbol_node(c, "f64");
+                        return trans_create_node_symbol_str(c, "f64");
                     case BuiltinType::Float128:
-                        return trans_create_symbol_node(c, "f128");
+                        return trans_create_node_symbol_str(c, "f128");
                     case BuiltinType::LongDouble:
-                        return trans_create_symbol_node(c, "c_longdouble");
+                        return trans_create_node_symbol_str(c, "c_longdouble");
                     case BuiltinType::WChar_U:
                     case BuiltinType::Char16:
                     case BuiltinType::Char32:
@@ -786,7 +475,8 @@ static AstNode *trans_type_with_table(Context *c, const Type *ty, const SourceLo
                     case BuiltinType::OCLClkEvent:
                     case BuiltinType::OCLQueue:
                     case BuiltinType::OCLReserveID:
-                        zig_panic("TODO more c type");
+                        emit_warning(c, source_loc, "unsupported builtin type");
+                        return nullptr;
                 }
                 break;
             }
@@ -795,40 +485,218 @@ static AstNode *trans_type_with_table(Context *c, const Type *ty, const SourceLo
                 const PointerType *pointer_ty = static_cast<const PointerType*>(ty);
                 QualType child_qt = pointer_ty->getPointeeType();
                 AstNode *child_node = trans_qual_type(c, child_qt, source_loc);
-                if (child_node == nullptr) return nullptr;
+                if (child_node == nullptr) {
+                    emit_warning(c, source_loc, "pointer to unsupported type");
+                    return nullptr;
+                }
 
                 if (qual_type_child_is_fn_proto(child_qt)) {
-                    zig_panic("TODO pointer to function proto");
+                    return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node);
                 }
 
-                AstNode *pointer_node = trans_create_node(c, NodeTypeAddrOfExpr);
-                pointer_node->data.addr_of_expr.is_const = child_qt.isConstQualified();
-                pointer_node->data.addr_of_expr.is_volatile = child_qt.isVolatileQualified();
-                pointer_node->data.addr_of_expr.op_expr = child_node;
-
-                AstNode *maybe_node = trans_create_node(c, NodeTypePrefixOpExpr);
-                maybe_node->data.prefix_op_expr.prefix_op = PrefixOpMaybe;
-                maybe_node->data.prefix_op_expr.primary_expr = pointer_node;
-                return maybe_node;
+                AstNode *pointer_node = trans_create_node_addr_of(c, child_qt.isConstQualified(),
+                        child_qt.isVolatileQualified(), child_node);
+                return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node);
             }
         case Type::Typedef:
-            zig_panic("TODO typedef");
+            {
+                const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
+                const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
+                Buf *type_name = buf_create_from_str(decl_name(typedef_decl));
+                if (buf_eql_str(type_name, "uint8_t")) {
+                    return trans_create_node_symbol_str(c, "u8");
+                } else if (buf_eql_str(type_name, "int8_t")) {
+                    return trans_create_node_symbol_str(c, "i8");
+                } else if (buf_eql_str(type_name, "uint16_t")) {
+                    return trans_create_node_symbol_str(c, "u16");
+                } else if (buf_eql_str(type_name, "int16_t")) {
+                    return trans_create_node_symbol_str(c, "i16");
+                } else if (buf_eql_str(type_name, "uint32_t")) {
+                    return trans_create_node_symbol_str(c, "u32");
+                } else if (buf_eql_str(type_name, "int32_t")) {
+                    return trans_create_node_symbol_str(c, "i32");
+                } else if (buf_eql_str(type_name, "uint64_t")) {
+                    return trans_create_node_symbol_str(c, "u64");
+                } else if (buf_eql_str(type_name, "int64_t")) {
+                    return trans_create_node_symbol_str(c, "i64");
+                } else if (buf_eql_str(type_name, "intptr_t")) {
+                    return trans_create_node_symbol_str(c, "isize");
+                } else if (buf_eql_str(type_name, "uintptr_t")) {
+                    return trans_create_node_symbol_str(c, "usize");
+                } else {
+                    auto entry = type_table->maybe_get(type_name);
+                    if (entry == nullptr || entry->value == nullptr) {
+                        return nullptr;
+                    } else {
+                        return entry->value;
+                    }
+                }
+            }
         case Type::Elaborated:
-            zig_panic("TODO elaborated");
+            {
+                const ElaboratedType *elaborated_ty = static_cast<const ElaboratedType*>(ty);
+                switch (elaborated_ty->getKeyword()) {
+                    case ETK_Struct:
+                        return trans_qual_type_with_table(c, elaborated_ty->getNamedType(),
+                                source_loc, &c->struct_type_table);
+                    case ETK_Enum:
+                        return trans_qual_type_with_table(c, elaborated_ty->getNamedType(),
+                                source_loc, &c->enum_type_table);
+                    case ETK_Interface:
+                    case ETK_Union:
+                    case ETK_Class:
+                    case ETK_Typename:
+                    case ETK_None:
+                        emit_warning(c, source_loc, "unsupported elaborated type");
+                        return nullptr;
+                }
+            }
         case Type::FunctionProto:
-            zig_panic("TODO FunctionProto");
+            {
+                const FunctionProtoType *fn_proto_ty = static_cast<const FunctionProtoType*>(ty);
+
+                AstNode *proto_node = trans_create_node(c, NodeTypeFnProto);
+                switch (fn_proto_ty->getCallConv()) {
+                    case CC_C:           // __attribute__((cdecl))
+                        proto_node->data.fn_proto.cc = CallingConventionC;
+                        break;
+                    case CC_X86StdCall:  // __attribute__((stdcall))
+                        proto_node->data.fn_proto.cc = CallingConventionStdcall;
+                        break;
+                    case CC_X86FastCall: // __attribute__((fastcall))
+                        emit_warning(c, source_loc, "unsupported calling convention: x86 fastcall");
+                        return nullptr;
+                    case CC_X86ThisCall: // __attribute__((thiscall))
+                        emit_warning(c, source_loc, "unsupported calling convention: x86 thiscall");
+                        return nullptr;
+                    case CC_X86VectorCall: // __attribute__((vectorcall))
+                        emit_warning(c, source_loc, "unsupported calling convention: x86 vectorcall");
+                        return nullptr;
+                    case CC_X86Pascal:   // __attribute__((pascal))
+                        emit_warning(c, source_loc, "unsupported calling convention: x86 pascal");
+                        return nullptr;
+                    case CC_Win64: // __attribute__((ms_abi))
+                        emit_warning(c, source_loc, "unsupported calling convention: win64");
+                        return nullptr;
+                    case CC_X86_64SysV:  // __attribute__((sysv_abi))
+                        emit_warning(c, source_loc, "unsupported calling convention: x86 64sysv");
+                        return nullptr;
+                    case CC_X86RegCall:
+                        emit_warning(c, source_loc, "unsupported calling convention: x86 reg");
+                        return nullptr;
+                    case CC_AAPCS:       // __attribute__((pcs("aapcs")))
+                        emit_warning(c, source_loc, "unsupported calling convention: aapcs");
+                        return nullptr;
+                    case CC_AAPCS_VFP:   // __attribute__((pcs("aapcs-vfp")))
+                        emit_warning(c, source_loc, "unsupported calling convention: aapcs-vfp");
+                        return nullptr;
+                    case CC_IntelOclBicc: // __attribute__((intel_ocl_bicc))
+                        emit_warning(c, source_loc, "unsupported calling convention: intel_ocl_bicc");
+                        return nullptr;
+                    case CC_SpirFunction: // default for OpenCL functions on SPIR target
+                        emit_warning(c, source_loc, "unsupported calling convention: SPIR function");
+                        return nullptr;
+                    case CC_OpenCLKernel:
+                        emit_warning(c, source_loc, "unsupported calling convention: OpenCLKernel");
+                        return nullptr;
+                    case CC_Swift:
+                        emit_warning(c, source_loc, "unsupported calling convention: Swift");
+                        return nullptr;
+                    case CC_PreserveMost:
+                        emit_warning(c, source_loc, "unsupported calling convention: PreserveMost");
+                        return nullptr;
+                    case CC_PreserveAll:
+                        emit_warning(c, source_loc, "unsupported calling convention: PreserveAll");
+                        return nullptr;
+                }
+
+                proto_node->data.fn_proto.is_var_args = fn_proto_ty->isVariadic();
+                size_t param_count = fn_proto_ty->getNumParams();
+
+                if (fn_proto_ty->getNoReturnAttr()) {
+                    proto_node->data.fn_proto.return_type = trans_create_node_symbol_str(c, "noreturn");
+                } else {
+                    proto_node->data.fn_proto.return_type = trans_qual_type(c, fn_proto_ty->getReturnType(),
+                            source_loc);
+                    if (proto_node->data.fn_proto.return_type == nullptr) {
+                        emit_warning(c, source_loc, "unsupported function proto return type");
+                        return nullptr;
+                    }
+                    // convert c_void to actual void (only for return type)
+                    if (is_c_void_type(proto_node->data.fn_proto.return_type)) {
+                        proto_node->data.fn_proto.return_type = nullptr;
+                    }
+                }
+
+                //emit_warning(c, source_loc, "TODO figure out fn prototype fn name");
+                const char *fn_name = nullptr;
+                if (fn_name != nullptr) {
+                    proto_node->data.fn_proto.name = buf_create_from_str(fn_name);
+                }
+
+                for (size_t i = 0; i < param_count; i += 1) {
+                    QualType qt = fn_proto_ty->getParamType(i);
+                    AstNode *param_type_node = trans_qual_type(c, qt, source_loc);
+
+                    if (param_type_node == nullptr) {
+                        emit_warning(c, source_loc, "unresolved function proto parameter type");
+                        return nullptr;
+                    }
+
+                    AstNode *param_node = trans_create_node(c, NodeTypeParamDecl);
+                    //emit_warning(c, source_loc, "TODO figure out fn prototype param name");
+                    const char *param_name = nullptr;
+                    if (param_name == nullptr) {
+                        param_node->data.param_decl.name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
+                    } else {
+                        param_node->data.param_decl.name = buf_create_from_str(param_name);
+                    }
+                    param_node->data.param_decl.is_noalias = qt.isRestrictQualified();
+                    param_node->data.param_decl.type = param_type_node;
+                    proto_node->data.fn_proto.params.append(param_node);
+                }
+                // TODO check for always_inline attribute
+                // TODO check for align attribute
+
+                return proto_node;
+            }
         case Type::Record:
-            zig_panic("TODO Record");
+            {
+                const RecordType *record_ty = static_cast<const RecordType*>(ty);
+                return resolve_record_decl(c, record_ty->getDecl());
+            }
         case Type::Enum:
-            zig_panic("TODO Enum");
+            {
+                const EnumType *enum_ty = static_cast<const EnumType*>(ty);
+                return resolve_enum_decl(c, enum_ty->getDecl());
+            }
         case Type::ConstantArray:
-            zig_panic("TODO ConstantArray");
+            {
+                const ConstantArrayType *const_arr_ty = static_cast<const ConstantArrayType *>(ty);
+                AstNode *child_type_node = trans_qual_type(c, const_arr_ty->getElementType(), source_loc);
+                if (child_type_node == nullptr) {
+                    emit_warning(c, source_loc, "unresolved array element type");
+                    return nullptr;
+                }
+                uint64_t size = const_arr_ty->getSize().getLimitedValue();
+                AstNode *size_node = trans_create_node_unsigned(c, size);
+                return trans_create_node_array_type(c, size_node, child_type_node);
+            }
         case Type::Paren:
-            zig_panic("TODO Paren");
+            {
+                const ParenType *paren_ty = static_cast<const ParenType *>(ty);
+                return trans_qual_type(c, paren_ty->getInnerType(), source_loc);
+            }
         case Type::Decayed:
-            zig_panic("TODO Decayed");
+            {
+                const DecayedType *decayed_ty = static_cast<const DecayedType *>(ty);
+                return trans_qual_type(c, decayed_ty->getDecayedType(), source_loc);
+            }
         case Type::Attributed:
-            zig_panic("TODO Attributed");
+            {
+                const AttributedType *attributed_ty = static_cast<const AttributedType *>(ty);
+                return trans_qual_type(c, attributed_ty->getEquivalentType(), source_loc);
+            }
         case Type::BlockPointer:
         case Type::LValueReference:
         case Type::RValueReference:
@@ -863,7 +731,8 @@ static AstNode *trans_type_with_table(Context *c, const Type *ty, const SourceLo
         case Type::Pipe:
         case Type::ObjCTypeParam:
         case Type::DeducedTemplateSpecialization:
-            zig_panic("TODO more c type aoeu");
+            emit_warning(c, source_loc, "unsupported type: '%s'", ty->getTypeClassName());
+            return nullptr;
     }
     zig_unreachable();
 }
@@ -875,15 +744,7 @@ static AstNode * trans_qual_type_with_table(Context *c, QualType qt, const Sourc
 }
 
 static AstNode * trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc) {
-    return trans_qual_type_with_table(c, qt, source_loc, &c->global_type_table2);
-}
-
-static AstNode * trans_create_node(Context *c, NodeType id) {
-    AstNode *node = allocate<AstNode>(1);
-    node->type = id;
-    node->owner = c->import;
-    // TODO line/column. mapping to C file??
-    return node;
+    return trans_qual_type_with_table(c, qt, source_loc, &c->global_type_table);
 }
 
 static AstNode * trans_compound_stmt(Context *c, AstNode *parent, CompoundStmt *stmt) {
@@ -907,35 +768,15 @@ static AstNode *trans_return_stmt(Context *c, AstNode *block, ReturnStmt *stmt)
     }
 }
 
-static void aps_int_to_bigint(Context *c, const llvm::APSInt &aps_int, BigInt *bigint) {
-    // TODO respect actually big integers
-    if (aps_int.isSigned()) {
-        if (aps_int > INT64_MAX || aps_int < INT64_MIN) {
-            zig_panic("TODO actually bigint in C");
-        } else {
-            bigint_init_signed(bigint, aps_int.getExtValue());
-        }
-    } else {
-        if (aps_int > INT64_MAX) {
-            zig_panic("TODO actually bigint in C");
-        } else {
-            bigint_init_unsigned(bigint, aps_int.getExtValue());
-        }
-    }
-}
-
-static AstNode * trans_integer_literal(Context *c, IntegerLiteral *stmt) {
-    AstNode *node = trans_create_node(c, NodeTypeIntLiteral);
+static AstNode *trans_integer_literal(Context *c, IntegerLiteral *stmt) {
     llvm::APSInt result;
     if (!stmt->EvaluateAsInt(result, *c->ctx)) {
-        fprintf(stderr, "TODO unable to convert integer literal to zig\n");
+        zig_panic("TODO handle libclang unable to evaluate C integer literal");
     }
-    node->data.int_literal.bigint = allocate<BigInt>(1);
-    aps_int_to_bigint(c, result, node->data.int_literal.bigint);
-    return node;
+    return trans_create_node_apint(c, result);
 }
 
-static AstNode * trans_conditional_operator(Context *c, AstNode *block, ConditionalOperator *stmt) {
+static AstNode *trans_conditional_operator(Context *c, AstNode *block, ConditionalOperator *stmt) {
     AstNode *node = trans_create_node(c, NodeTypeIfBoolExpr);
 
     Expr *cond_expr = stmt->getCond();
@@ -1034,9 +875,7 @@ static AstNode * trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCa
             return trans_expr(c, block, stmt->getSubExpr());
         case CK_IntegralCast:
             {
-                AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
-                node->data.fn_call_expr.fn_ref_expr = trans_create_symbol_node(c, "bitCast");
-                node->data.fn_call_expr.is_builtin = true;
+                AstNode *node = trans_create_node_builtin_fn_call_str(c, "bitCast");
                 node->data.fn_call_expr.params.append(trans_qual_type(c, stmt->getType(), stmt->getExprLoc()));
                 node->data.fn_call_expr.params.append(trans_expr(c, block, stmt->getSubExpr()));
                 return node;
@@ -1166,13 +1005,6 @@ static AstNode * trans_decl_ref_expr(Context *c, DeclRefExpr *stmt) {
     return node;
 }
 
-static AstNode * trans_create_num_lit_node_unsigned(Context *c, Stmt *stmt, uint64_t x) {
-    AstNode *node = trans_create_node(c, NodeTypeIntLiteral);
-    node->data.int_literal.bigint = allocate<BigInt>(1);
-    bigint_init_unsigned(node->data.int_literal.bigint, x);
-    return node;
-}
-
 static AstNode * trans_unary_operator(Context *c, AstNode *block, UnaryOperator *stmt) {
     switch (stmt->getOpcode()) {
         case UO_PostInc:
@@ -1200,7 +1032,7 @@ static AstNode * trans_unary_operator(Context *c, AstNode *block, UnaryOperator
                 } else if (c_is_unsigned_integer(c, op_expr->getType())) {
                     // we gotta emit 0 -% x
                     AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
-                    node->data.bin_op_expr.op1 = trans_create_num_lit_node_unsigned(c, stmt, 0);
+                    node->data.bin_op_expr.op1 = trans_create_node_unsigned(c, 0);
                     node->data.bin_op_expr.op2 = trans_expr(c, block, op_expr);
                     node->data.bin_op_expr.bin_op = BinOpTypeSubWrap;
                     return node;
@@ -1230,19 +1062,14 @@ static AstNode * trans_local_declaration(Context *c, AstNode *block, DeclStmt *s
         switch (decl->getKind()) {
             case Decl::Var: {
                 VarDecl *var_decl = (VarDecl *)decl;
-                AstNode *node = trans_create_node(c, NodeTypeVariableDeclaration);
-                node->data.variable_declaration.symbol = buf_create_from_str(decl_name(var_decl));
                 QualType qual_type = var_decl->getTypeSourceInfo()->getType();
-                node->data.variable_declaration.is_const = qual_type.isConstQualified();
-                node->data.variable_declaration.type = trans_qual_type(c, qual_type, stmt->getStartLoc());
-                if (var_decl->hasInit()) {
-                    node->data.variable_declaration.expr = trans_expr(c, block, var_decl->getInit());
-                }
-
+                AstNode *init_node = var_decl->hasInit() ? trans_expr(c, block, var_decl->getInit()) : nullptr;
+                AstNode *type_node = trans_qual_type(c, qual_type, stmt->getStartLoc());
+                AstNode *node = trans_create_node_var_decl(c, qual_type.isConstQualified(),
+                        buf_create_from_str(decl_name(var_decl)), type_node, init_node);
                 block->data.block.statements.append(node);
                 continue;
             }
-
             case Decl::AccessSpec:
                 zig_panic("TODO handle decl kind AccessSpec");
             case Decl::Block:
@@ -1802,53 +1629,57 @@ static AstNode *trans_stmt(Context *c, AstNode *block, Stmt *stmt) {
 static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
     Buf *fn_name = buf_create_from_str(decl_name(fn_decl));
 
-    if (fn_decl->hasBody()) {
-        fprintf(stderr, "fn %s\n", buf_ptr(fn_name));
-        Stmt *body = fn_decl->getBody();
-        AstNode *body_node = trans_stmt(c, nullptr, body);
-        ast_render(c->codegen, stderr, body_node, 4);
-        fprintf(stderr, "\n");
-    }
-
     if (get_global(c, fn_name)) {
         // we already saw this function
         return;
     }
 
-    TypeTableEntry *fn_type = resolve_qual_type(c, fn_decl->getType(), fn_decl);
-
-    if (fn_type->id == TypeTableEntryIdInvalid) {
-        emit_warning(c, fn_decl->getLocation(), "ignoring function '%s' - unable to resolve type", buf_ptr(fn_name));
+    AstNode *proto_node = trans_qual_type(c, fn_decl->getType(), fn_decl->getLocation());
+    if (proto_node == nullptr) {
+        emit_warning(c, fn_decl->getLocation(), "unable to resolve prototype of function '%s'", buf_ptr(fn_name));
         return;
     }
-    assert(fn_type->id == TypeTableEntryIdFn);
 
-    FnTableEntry *fn_entry = create_fn_raw(FnInlineAuto, GlobalLinkageIdStrong);
-    buf_init_from_buf(&fn_entry->symbol_name, fn_name);
-    fn_entry->type_entry = fn_type;
+    proto_node->data.fn_proto.name = fn_name;
+    proto_node->data.fn_proto.is_extern = !fn_decl->hasBody();
 
-    assert(fn_type->data.fn.fn_type_id.cc != CallingConventionNaked);
+    StorageClass sc = fn_decl->getStorageClass();
+    if (sc == SC_None) {
+        proto_node->data.fn_proto.visib_mod = fn_decl->hasBody() ? VisibModExport : c->visib_mod;
+    } else if (sc == SC_Extern || sc == SC_Static) {
+        proto_node->data.fn_proto.visib_mod = c->visib_mod;
+    } else if (sc == SC_PrivateExtern) {
+        emit_warning(c, fn_decl->getLocation(), "unsupported storage class: private extern");
+        return;
+    } else {
+        emit_warning(c, fn_decl->getLocation(), "unsupported storage class: unknown");
+        return;
+    }
 
-    size_t arg_count = fn_type->data.fn.fn_type_id.param_count;
-    fn_entry->param_names = allocate<Buf *>(arg_count);
-    Buf *name_buf;
+    const FunctionProtoType *fn_proto_ty = (const FunctionProtoType *) fn_decl->getType().getTypePtr();
+    size_t arg_count = fn_proto_ty->getNumParams();
     for (size_t i = 0; i < arg_count; i += 1) {
+        AstNode *param_node = proto_node->data.fn_proto.params.at(i);
         const ParmVarDecl *param = fn_decl->getParamDecl(i);
         const char *name = decl_name(param);
-        if (strlen(name) == 0) {
-            name_buf = buf_sprintf("arg%" ZIG_PRI_usize "", i);
-        } else {
-            name_buf = buf_create_from_str(name);
+        if (strlen(name) != 0) {
+            param_node->data.param_decl.name = buf_create_from_str(name);
         }
-        fn_entry->param_names[i] = name_buf;
     }
 
-    TldFn *tld_fn = allocate<TldFn>(1);
-    parseh_init_tld(c, &tld_fn->base, TldIdFn, fn_name);
-    tld_fn->fn_entry = fn_entry;
-    add_global(c, &tld_fn->base);
+    if (fn_decl->hasBody()) {
+        Stmt *body = fn_decl->getBody();
+
+        AstNode *fn_def_node = trans_create_node(c, NodeTypeFnDef);
+        fn_def_node->data.fn_def.fn_proto = proto_node;
+        fn_def_node->data.fn_def.body = trans_stmt(c, nullptr, body);
 
-    c->codegen->fn_protos.append(fn_entry);
+        proto_node->data.fn_proto.fn_def_node = fn_def_node;
+        c->root->data.root.top_level_decls.append(fn_def_node);
+        return;
+    }
+
+    c->root->data.root.top_level_decls.append(proto_node);
 }
 
 static void visit_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl) {
@@ -1874,53 +1705,36 @@ static void visit_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl)
     // use the name of this typedef
     // TODO
 
-    TypeTableEntry *child_type = resolve_qual_type(c, child_qt, typedef_decl);
-    if (child_type->id == TypeTableEntryIdInvalid) {
+    AstNode *type_node = trans_qual_type(c, child_qt, typedef_decl->getLocation());
+    if (type_node == nullptr) {
         emit_warning(c, typedef_decl->getLocation(), "typedef %s - unresolved child type", buf_ptr(type_name));
         return;
     }
-    add_const_type(c, type_name, child_type);
-}
-
-static void replace_with_fwd_decl(Context *c, TypeTableEntry *struct_type, Buf *full_type_name) {
-    unsigned line = c->source_node ? c->source_node->line : 0;
-    ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugForwardDeclType(c->codegen->dbuilder,
-        ZigLLVMTag_DW_structure_type(), buf_ptr(full_type_name),
-        ZigLLVMFileToScope(c->import->di_file), c->import->di_file, line);
-
-    ZigLLVMReplaceTemporary(c->codegen->dbuilder, struct_type->di_type, replacement_di_type);
-    struct_type->di_type = replacement_di_type;
-    struct_type->id = TypeTableEntryIdOpaque;
+    add_global_var(c, type_name, type_node);
+    c->global_type_table.put(type_name, type_node);
 }
 
-static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
+static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
     auto existing_entry = c->decl_table.maybe_get((void*)enum_decl);
     if (existing_entry) {
         return existing_entry->value;
     }
 
     const char *raw_name = decl_name(enum_decl);
-
-    Buf *bare_name;
-    if (raw_name[0] == 0) {
-        bare_name = buf_sprintf("anon_$%" PRIu32, get_next_anon_index(c));
-    } else {
-        bare_name = buf_create_from_str(raw_name);
-    }
-
-    Buf *full_type_name = buf_sprintf("enum_%s", buf_ptr(bare_name));
+    bool is_anonymous = (raw_name[0] == 0);
+    Buf *bare_name = is_anonymous ? nullptr : buf_create_from_str(raw_name);
+    Buf *full_type_name = is_anonymous ? nullptr : buf_sprintf("enum_%s", buf_ptr(bare_name));
 
     const EnumDecl *enum_def = enum_decl->getDefinition();
     if (!enum_def) {
-        TypeTableEntry *enum_type = get_partial_container_type(c->codegen, &c->import->decls_scope->base,
-                ContainerKindEnum, c->source_node, buf_ptr(full_type_name), ContainerLayoutExtern);
-        enum_type->data.enumeration.zero_bits_known = true;
-        enum_type->data.enumeration.abi_alignment = 1;
-        c->enum_type_table.put(bare_name, enum_type);
-        c->decl_table.put(enum_decl, enum_type);
-        replace_with_fwd_decl(c, enum_type, full_type_name);
-
-        return enum_type;
+        AstNode *opaque_node = trans_create_node_opaque(c);
+        if (!is_anonymous) {
+            c->enum_type_table.put(bare_name, opaque_node);
+            add_global_weak_alias(c, bare_name, opaque_node);
+            add_global_var(c, full_type_name, opaque_node);
+        }
+        c->decl_table.put(enum_decl, opaque_node);
+        return opaque_node;
     }
 
     bool pure_enum = true;
@@ -1935,25 +1749,16 @@ static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl)
         }
     }
 
-    TypeTableEntry *tag_int_type = resolve_qual_type(c, enum_decl->getIntegerType(), enum_decl);
+    AstNode *tag_int_type = trans_qual_type(c, enum_decl->getIntegerType(), enum_decl->getLocation());
+    assert(tag_int_type);
 
     if (pure_enum) {
-        TypeTableEntry *enum_type = get_partial_container_type(c->codegen, &c->import->decls_scope->base,
-                ContainerKindEnum, c->source_node, buf_ptr(full_type_name), ContainerLayoutExtern);
-        TypeTableEntry *tag_type_entry = create_enum_tag_type(c->codegen, enum_type, tag_int_type);
-        c->enum_type_table.put(bare_name, enum_type);
-        c->decl_table.put(enum_decl, enum_type);
-
-        enum_type->data.enumeration.gen_field_count = 0;
-        enum_type->data.enumeration.complete = true;
-        enum_type->data.enumeration.zero_bits_known = true;
-        enum_type->data.enumeration.abi_alignment = 1;
-        enum_type->data.enumeration.tag_type = tag_type_entry;
-
-        enum_type->data.enumeration.src_field_count = field_count;
-        enum_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
-        ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count);
+        AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl);
+        enum_node->data.container_decl.kind = ContainerKindEnum;
+        enum_node->data.container_decl.layout = ContainerLayoutExtern;
+        enum_node->data.container_decl.init_arg_expr = tag_int_type;
 
+        enum_node->data.container_decl.fields.resize(field_count);
         uint32_t i = 0;
         for (auto it = enum_def->enumerator_begin(),
                 it_end = enum_def->enumerator_end();
@@ -1963,92 +1768,63 @@ static TypeTableEntry *resolve_enum_decl(Context *c, const EnumDecl *enum_decl)
 
             Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
             Buf *field_name;
-            if (buf_starts_with_buf(enum_val_name, bare_name)) {
+            if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) {
                 field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name));
             } else {
                 field_name = enum_val_name;
             }
 
-            TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i];
-            type_enum_field->name = field_name;
-            type_enum_field->type_entry = c->codegen->builtin_types.entry_void;
-            type_enum_field->value = i;
-
-            di_enumerators[i] = ZigLLVMCreateDebugEnumerator(c->codegen->dbuilder, buf_ptr(type_enum_field->name), i);
-
+            AstNode *field_node = trans_create_node(c, NodeTypeStructField);
+            field_node->data.struct_field.name = field_name;
+            field_node->data.struct_field.type = nullptr;
+            enum_node->data.container_decl.fields.items[i] = field_node;
 
             // in C each enum value is in the global namespace. so we put them there too.
             // at this point we can rely on the enum emitting successfully
-            add_global(c, create_global_num_lit_unsigned_negative(c, enum_val_name, i, false));
+            AstNode *field_access_node = trans_create_node_field_access(c,
+                    trans_create_node_symbol(c, full_type_name), field_name);
+            add_global_var(c, enum_val_name, field_access_node);
         }
 
-        // create llvm type for root struct
-        enum_type->type_ref = tag_type_entry->type_ref;
-
-        enum_type->data.enumeration.abi_alignment = LLVMABIAlignmentOfType(c->codegen->target_data_ref,
-                enum_type->type_ref);
-
-        // create debug type for tag
-        unsigned line = c->source_node ? (c->source_node->line + 1) : 0;
-        uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(c->codegen->target_data_ref, enum_type->type_ref);
-        uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(c->codegen->target_data_ref, enum_type->type_ref);
-        ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(c->codegen->dbuilder,
-                ZigLLVMFileToScope(c->import->di_file), buf_ptr(bare_name),
-                c->import->di_file, line,
-                debug_size_in_bits,
-                debug_align_in_bits,
-                di_enumerators, field_count, tag_type_entry->di_type, "");
-
-        ZigLLVMReplaceTemporary(c->codegen->dbuilder, enum_type->di_type, tag_di_type);
-        enum_type->di_type = tag_di_type;
-
-        return enum_type;
-    } else {
-        // TODO after issue #305 is solved, make this be an enum with tag_int_type
-        // as the integer type and set the custom enum values
-        TypeTableEntry *enum_type = tag_int_type;
-        c->enum_type_table.put(bare_name, enum_type);
-        c->decl_table.put(enum_decl, enum_type);
-
-        // add variables for all the values with enum_type
-        for (auto it = enum_def->enumerator_begin(),
-                it_end = enum_def->enumerator_end();
-                it != it_end; ++it)
-        {
-            const EnumConstantDecl *enum_const = *it;
-
-            Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
-
-            Tld *tld = create_global_num_lit_ap(c, enum_decl, enum_val_name, enum_const->getInitVal());
-            if (!tld)
-                return c->codegen->builtin_types.entry_invalid;
-
-            add_global(c, tld);
+        if (!is_anonymous) {
+            c->enum_type_table.put(bare_name, enum_node);
+            add_global_weak_alias(c, bare_name, enum_node);
+            add_global_var(c, full_type_name, enum_node);
         }
+        c->decl_table.put(enum_decl, enum_node);
 
-        return enum_type;
+        return enum_node;
     }
-}
 
-static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {
-    TypeTableEntry *enum_type = resolve_enum_decl(c, enum_decl);
+    // TODO after issue #305 is solved, make this be an enum with tag_int_type
+    // as the integer type and set the custom enum values
+    AstNode *enum_node = tag_int_type;
 
-    if (enum_type->id == TypeTableEntryIdInvalid)
-        return;
 
-    // make an alias without the "enum_" prefix. this will get emitted at the
-    // end if it doesn't conflict with anything else
-    bool is_anonymous = (decl_name(enum_decl)[0] == 0);
-    if (is_anonymous)
-        return;
+    // add variables for all the values with enum_node
+    for (auto it = enum_def->enumerator_begin(),
+            it_end = enum_def->enumerator_end();
+            it != it_end; ++it)
+    {
+        const EnumConstantDecl *enum_const = *it;
+
+        Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
+        AstNode *int_node = trans_create_node_apint(c, enum_const->getInitVal());
+        AstNode *var_node = add_global_var(c, enum_val_name, int_node);
+        var_node->data.variable_declaration.type = tag_int_type;
+    }
 
-    Buf *bare_name = buf_create_from_str(decl_name(enum_decl));
+    if (!is_anonymous) {
+        c->enum_type_table.put(bare_name, enum_node);
+        add_global_weak_alias(c, bare_name, enum_node);
+        add_global_var(c, full_type_name, enum_node);
+    }
+    c->decl_table.put(enum_decl, enum_node);
 
-    Tld *tld = add_container_tld(c, enum_type);
-    add_global_weak_alias(c, bare_name, tld);
+    return enum_node;
 }
 
-static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_decl) {
+static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) {
     auto existing_entry = c->decl_table.maybe_get((void*)record_decl);
     if (existing_entry) {
         return existing_entry->value;
@@ -2058,35 +1834,26 @@ static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_
 
     if (!record_decl->isStruct()) {
         emit_warning(c, record_decl->getLocation(), "skipping record %s, not a struct", raw_name);
-        return c->codegen->builtin_types.entry_invalid;
-    }
-
-    Buf *bare_name;
-    if (record_decl->isAnonymousStructOrUnion() || raw_name[0] == 0) {
-        bare_name = buf_sprintf("anon_$%" PRIu32, get_next_anon_index(c));
-    } else {
-        bare_name = buf_create_from_str(raw_name);
+        c->decl_table.put(record_decl, nullptr);
+        return nullptr;
     }
 
-    Buf *full_type_name = buf_sprintf("struct_%s", buf_ptr(bare_name));
-
-
-    TypeTableEntry *struct_type = get_partial_container_type(c->codegen, &c->import->decls_scope->base,
-        ContainerKindStruct, c->source_node, buf_ptr(full_type_name), ContainerLayoutExtern);
-    struct_type->data.structure.zero_bits_known = true;
-    struct_type->data.structure.abi_alignment = 1;
-
-    c->struct_type_table.put(bare_name, struct_type);
-    c->decl_table.put(record_decl, struct_type);
+    bool is_anonymous = record_decl->isAnonymousStructOrUnion() || raw_name[0] == 0;
+    Buf *bare_name = is_anonymous ? nullptr : buf_create_from_str(raw_name);
+    Buf *full_type_name = (bare_name == nullptr) ? nullptr : buf_sprintf("struct_%s", buf_ptr(bare_name));
 
     RecordDecl *record_def = record_decl->getDefinition();
-    unsigned line = c->source_node ? c->source_node->line : 0;
-    if (!record_def) {
-        replace_with_fwd_decl(c, struct_type, full_type_name);
-        return struct_type;
+    if (record_def == nullptr) {
+        AstNode *opaque_node = trans_create_node_opaque(c);
+        if (!is_anonymous) {
+            c->struct_type_table.put(bare_name, opaque_node);
+            add_global_weak_alias(c, bare_name, opaque_node);
+            add_global_var(c, full_type_name, opaque_node);
+        }
+        c->decl_table.put(record_decl, opaque_node);
+        return opaque_node;
     }
 
-
     // count fields and validate
     uint32_t field_count = 0;
     for (auto it = record_def->field_begin(),
@@ -2096,105 +1863,69 @@ static TypeTableEntry *resolve_record_decl(Context *c, const RecordDecl *record_
         const FieldDecl *field_decl = *it;
 
         if (field_decl->isBitField()) {
-            emit_warning(c, field_decl->getLocation(), "struct %s demoted to opaque type - has bitfield\n", buf_ptr(bare_name));
-            replace_with_fwd_decl(c, struct_type, full_type_name);
-            return struct_type;
+            emit_warning(c, field_decl->getLocation(), "struct %s demoted to opaque type - has bitfield",
+                    is_anonymous ? "(anon)" : buf_ptr(bare_name));
+
+            AstNode *opaque_node = trans_create_node_opaque(c);
+
+            if (!is_anonymous) {
+                c->struct_type_table.put(bare_name, opaque_node);
+                add_global_weak_alias(c, bare_name, opaque_node);
+                add_global_var(c, full_type_name, opaque_node);
+            }
+            c->decl_table.put(record_decl, opaque_node);
+            return opaque_node;;
         }
     }
 
-    struct_type->data.structure.src_field_count = field_count;
-    struct_type->data.structure.fields = allocate<TypeStructField>(field_count);
-    LLVMTypeRef *element_types = allocate<LLVMTypeRef>(field_count);
-    ZigLLVMDIType **di_element_types = allocate<ZigLLVMDIType*>(field_count);
+    AstNode *struct_node = trans_create_node(c, NodeTypeContainerDecl);
+    struct_node->data.container_decl.kind = ContainerKindStruct;
+    struct_node->data.container_decl.layout = ContainerLayoutExtern;
 
-    // next, populate element_types as its needed for LLVMStructSetBody which is needed for LLVMOffsetOfElement
-    uint32_t i = 0;
-    for (auto it = record_def->field_begin(),
-              it_end = record_def->field_end();
-              it != it_end; ++it, i += 1)
-    {
-        const FieldDecl *field_decl = *it;
+    // TODO handle attribute packed
 
-        TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
-        type_struct_field->name = buf_create_from_str(decl_name(field_decl));
-        type_struct_field->src_index = i;
-        type_struct_field->gen_index = i;
-        TypeTableEntry *field_type = resolve_qual_type(c, field_decl->getType(), field_decl);
-        type_struct_field->type_entry = field_type;
-
-        if (type_is_invalid(field_type) || !type_is_complete(field_type)) {
-            emit_warning(c, field_decl->getLocation(), "struct %s demoted to opaque type - unresolved type\n", buf_ptr(bare_name));
-            replace_with_fwd_decl(c, struct_type, full_type_name);
-            return struct_type;
-        }
+    struct_node->data.container_decl.fields.resize(field_count);
 
-        element_types[i] = field_type->type_ref;
-        assert(element_types[i]);
+    // must be before fields in case a circular reference happens
+    if (!is_anonymous) {
+        c->struct_type_table.put(bare_name, struct_node);
+        add_global_weak_alias(c, bare_name, struct_node);
+        add_global_var(c, full_type_name, struct_node);
     }
+    c->decl_table.put(record_decl, struct_node);
 
-    LLVMStructSetBody(struct_type->type_ref, element_types, field_count, false);
-
-    // finally populate debug info
-    i = 0;
+    uint32_t i = 0;
     for (auto it = record_def->field_begin(),
               it_end = record_def->field_end();
               it != it_end; ++it, i += 1)
     {
-        TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
-        TypeTableEntry *field_type = type_struct_field->type_entry;
-
-        uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(c->codegen->target_data_ref, field_type->type_ref);
-        uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(c->codegen->target_data_ref, field_type->type_ref);
-        uint64_t debug_offset_in_bits = 8*LLVMOffsetOfElement(c->codegen->target_data_ref, struct_type->type_ref, i);
-        di_element_types[i] = ZigLLVMCreateDebugMemberType(c->codegen->dbuilder,
-                ZigLLVMTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name),
-                c->import->di_file, line + 1,
-                debug_size_in_bits,
-                debug_align_in_bits,
-                debug_offset_in_bits,
-                0, field_type->di_type);
-
-        assert(di_element_types[i]);
+        const FieldDecl *field_decl = *it;
 
-    }
-    struct_type->data.structure.embedded_in_current = false;
-
-    struct_type->data.structure.gen_field_count = field_count;
-    struct_type->data.structure.complete = true;
-    struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(c->codegen->target_data_ref,
-            struct_type->type_ref);
-
-    uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(c->codegen->target_data_ref, struct_type->type_ref);
-    uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(c->codegen->target_data_ref, struct_type->type_ref);
-    ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(c->codegen->dbuilder,
-            ZigLLVMFileToScope(c->import->di_file),
-            buf_ptr(full_type_name), c->import->di_file, line + 1,
-            debug_size_in_bits,
-            debug_align_in_bits,
-            0,
-            nullptr, di_element_types, field_count, 0, nullptr, "");
-
-    ZigLLVMReplaceTemporary(c->codegen->dbuilder, struct_type->di_type, replacement_di_type);
-    struct_type->di_type = replacement_di_type;
-
-    return struct_type;
-}
+        AstNode *field_node = trans_create_node(c, NodeTypeStructField);
+        field_node->data.struct_field.name = buf_create_from_str(decl_name(field_decl));
+        field_node->data.struct_field.type = trans_qual_type(c, field_decl->getType(), field_decl->getLocation());
 
-static void visit_record_decl(Context *c, const RecordDecl *record_decl) {
-    TypeTableEntry *struct_type = resolve_record_decl(c, record_decl);
+        if (field_node->data.struct_field.type == nullptr) {
+            emit_warning(c, field_decl->getLocation(),
+                    "struct %s demoted to opaque type - unresolved type",
+                    is_anonymous ? "(anon)" : buf_ptr(bare_name));
 
-    if (struct_type->id == TypeTableEntryIdInvalid) {
-        return;
-    }
+            AstNode *opaque_node = trans_create_node_opaque(c);
+            if (!is_anonymous) {
+                c->struct_type_table.put(bare_name, opaque_node);
+                add_global_weak_alias(c, bare_name, opaque_node);
+                add_global_var(c, full_type_name, opaque_node);
+            }
+            c->decl_table.put(record_decl, opaque_node);
 
-    bool is_anonymous = (record_decl->isAnonymousStructOrUnion() || decl_name(record_decl)[0] == 0);
-    if (is_anonymous)
-        return;
+            return opaque_node;
+        }
+
+        struct_node->data.container_decl.fields.items[i] = field_node;
+    }
 
-    Buf *bare_name = buf_create_from_str(decl_name(record_decl));
 
-    Tld *tld = add_container_tld(c, struct_type);
-    add_global_weak_alias(c, bare_name, tld);
+    return struct_node;
 }
 
 static void visit_var_decl(Context *c, const VarDecl *var_decl) {
@@ -2204,17 +1935,19 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) {
         case VarDecl::TLS_None:
             break;
         case VarDecl::TLS_Static:
-            emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - static thread local storage\n", buf_ptr(name));
+            emit_warning(c, var_decl->getLocation(),
+                    "ignoring variable '%s' - static thread local storage", buf_ptr(name));
             return;
         case VarDecl::TLS_Dynamic:
-            emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - dynamic thread local storage\n", buf_ptr(name));
+            emit_warning(c, var_decl->getLocation(),
+                    "ignoring variable '%s' - dynamic thread local storage", buf_ptr(name));
             return;
     }
 
     QualType qt = var_decl->getType();
-    TypeTableEntry *var_type = resolve_qual_type(c, qt, var_decl);
-    if (var_type->id == TypeTableEntryIdInvalid) {
-        emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - unresolved type\n", buf_ptr(name));
+    AstNode *var_type = trans_qual_type(c, qt, var_decl->getLocation());
+    if (var_type == nullptr) {
+        emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - unresolved type", buf_ptr(name));
         return;
     }
 
@@ -2223,59 +1956,53 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) {
     bool is_const = qt.isConstQualified();
 
     if (is_static && !is_extern) {
-        if (!var_decl->hasInit()) {
-            emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - no initializer\n", buf_ptr(name));
-            return;
-        }
-        APValue *ap_value = var_decl->evaluateValue();
-        if (!ap_value) {
-            emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - unable to evaluate initializer\n", buf_ptr(name));
-            return;
-        }
-        ConstExprValue *init_value = nullptr;
-        switch (ap_value->getKind()) {
-            case APValue::Int:
-                {
-                    if (var_type->id != TypeTableEntryIdInt) {
-                        emit_warning(c, var_decl->getLocation(),
-                            "ignoring variable '%s' - int initializer for non int type\n", buf_ptr(name));
-                        return;
-                    }
-                    init_value = create_const_int_ap(c, var_type, var_decl, ap_value->getInt());
-                    if (!init_value)
-                        return;
-
-                    break;
-                }
-            case APValue::Uninitialized:
-            case APValue::Float:
-            case APValue::ComplexInt:
-            case APValue::ComplexFloat:
-            case APValue::LValue:
-            case APValue::Vector:
-            case APValue::Array:
-            case APValue::Struct:
-            case APValue::Union:
-            case APValue::MemberPointer:
-            case APValue::AddrLabelDiff:
+        AstNode *init_node;
+        if (var_decl->hasInit()) {
+            APValue *ap_value = var_decl->evaluateValue();
+            if (ap_value == nullptr) {
                 emit_warning(c, var_decl->getLocation(),
-                        "ignoring variable '%s' - unrecognized initializer value kind\n", buf_ptr(name));
+                        "ignoring variable '%s' - unable to evaluate initializer", buf_ptr(name));
                 return;
+            }
+            switch (ap_value->getKind()) {
+                case APValue::Int:
+                    init_node = trans_create_node_apint(c, ap_value->getInt());
+                    break;
+                case APValue::Uninitialized:
+                    init_node = trans_create_node_symbol_str(c, "undefined");
+                    break;
+                case APValue::Float:
+                case APValue::ComplexInt:
+                case APValue::ComplexFloat:
+                case APValue::LValue:
+                case APValue::Vector:
+                case APValue::Array:
+                case APValue::Struct:
+                case APValue::Union:
+                case APValue::MemberPointer:
+                case APValue::AddrLabelDiff:
+                    emit_warning(c, var_decl->getLocation(),
+                            "ignoring variable '%s' - unrecognized initializer value kind", buf_ptr(name));
+                    return;
+            }
+        } else {
+            init_node = trans_create_node_symbol_str(c, "undefined");
         }
 
-        TldVar *tld_var = create_global_var(c, name, init_value, true);
-        add_global(c, &tld_var->base);
+        AstNode *var_node = trans_create_node_var_decl(c, is_const, name, var_type, init_node);
+        c->root->data.root.top_level_decls.append(var_node);
         return;
     }
 
     if (is_extern) {
-        TldVar *tld_var = create_global_var(c, name, create_const_runtime(var_type), is_const);
-        tld_var->var->linkage = VarLinkageExternal;
-        add_global(c, &tld_var->base);
+        AstNode *var_node = trans_create_node_var_decl(c, is_const, name, var_type, nullptr);
+        var_node->data.variable_declaration.is_extern = true;
+        c->root->data.root.top_level_decls.append(var_node);
         return;
     }
 
-    emit_warning(c, var_decl->getLocation(), "ignoring variable '%s' - non-extern, non-static variable\n", buf_ptr(name));
+    emit_warning(c, var_decl->getLocation(),
+        "ignoring variable '%s' - non-extern, non-static variable", buf_ptr(name));
     return;
 }
 
@@ -2290,25 +2017,22 @@ static bool decl_visitor(void *context, const Decl *decl) {
             visit_typedef_decl(c, static_cast<const TypedefNameDecl *>(decl));
             break;
         case Decl::Enum:
-            visit_enum_decl(c, static_cast<const EnumDecl *>(decl));
+            resolve_enum_decl(c, static_cast<const EnumDecl *>(decl));
             break;
         case Decl::Record:
-            visit_record_decl(c, static_cast<const RecordDecl *>(decl));
+            resolve_record_decl(c, static_cast<const RecordDecl *>(decl));
             break;
         case Decl::Var:
             visit_var_decl(c, static_cast<const VarDecl *>(decl));
             break;
         default:
-            emit_warning(c, decl->getLocation(), "ignoring %s decl\n", decl->getDeclKindName());
+            emit_warning(c, decl->getLocation(), "ignoring %s decl", decl->getDeclKindName());
     }
 
     return true;
 }
 
 static bool name_exists(Context *c, Buf *name) {
-    if (c->global_type_table.maybe_get(name)) {
-        return true;
-    }
     if (get_global(c, name)) {
         return true;
     }
@@ -2324,7 +2048,7 @@ static void render_aliases(Context *c) {
         if (name_exists(c, alias->name))
             continue;
 
-        add_global_alias(c, alias->name, alias->tld);
+        add_global_var(c, alias->name, alias->node);
     }
 }
 
@@ -2335,8 +2059,7 @@ static void render_macros(Context *c) {
         if (!entry)
             break;
 
-        Tld *var_tld = entry->value;
-        add_global(c, var_tld);
+        add_global_var(c, entry->key, entry->value);
     }
 }
 
@@ -2355,52 +2078,52 @@ static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *ch
         switch (tok->id) {
             case CTokIdCharLit:
                 if (is_last && is_first) {
-                    Tld *tld = create_global_num_lit_unsigned_negative(c, name, tok->data.char_lit, false);
-                    c->macro_table.put(name, tld);
+                    AstNode *node = trans_create_node_unsigned(c, tok->data.char_lit);
+                    c->macro_table.put(name, node);
                 }
                 return;
             case CTokIdStrLit:
                 if (is_last && is_first) {
-                    Tld *tld = create_global_str_lit_var(c, name, buf_create_from_buf(&tok->data.str_lit));
-                    c->macro_table.put(name, tld);
+                    AstNode *node = trans_create_node_str_lit_c(c, buf_create_from_buf(&tok->data.str_lit));
+                    c->macro_table.put(name, node);
                 }
                 return;
             case CTokIdNumLitInt:
                 if (is_last) {
-                    Tld *tld;
+                    AstNode *node;
                     switch (tok->data.num_lit_int.suffix) {
                         case CNumLitSuffixNone:
-                            tld = create_global_num_lit_unsigned_negative(c, name, tok->data.num_lit_int.x, negate);
+                            node = trans_create_node_unsigned_negative(c, tok->data.num_lit_int.x, negate);
                             break;
                         case CNumLitSuffixL:
-                            tld = create_global_num_lit_unsigned_negative_type(c, name, tok->data.num_lit_int.x, negate,
-                                    c->codegen->builtin_types.entry_c_int[CIntTypeLong]);
+                            node = trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate,
+                                    "c_long");
                             break;
                         case CNumLitSuffixU:
-                            tld = create_global_num_lit_unsigned_negative_type(c, name, tok->data.num_lit_int.x, negate,
-                                    c->codegen->builtin_types.entry_c_int[CIntTypeUInt]);
+                            node = trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate,
+                                    "c_uint");
                             break;
                         case CNumLitSuffixLU:
-                            tld = create_global_num_lit_unsigned_negative_type(c, name, tok->data.num_lit_int.x, negate,
-                                    c->codegen->builtin_types.entry_c_int[CIntTypeULong]);
+                            node = trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate,
+                                    "c_ulong");
                             break;
                         case CNumLitSuffixLL:
-                            tld = create_global_num_lit_unsigned_negative_type(c, name, tok->data.num_lit_int.x, negate,
-                                    c->codegen->builtin_types.entry_c_int[CIntTypeLongLong]);
+                            node = trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate,
+                                    "c_longlong");
                             break;
                         case CNumLitSuffixLLU:
-                            tld = create_global_num_lit_unsigned_negative_type(c, name, tok->data.num_lit_int.x, negate,
-                                    c->codegen->builtin_types.entry_c_int[CIntTypeULongLong]);
+                            node = trans_create_node_unsigned_negative_type(c, tok->data.num_lit_int.x, negate,
+                                    "c_ulonglong");
                             break;
                     }
-                    c->macro_table.put(name, tld);
+                    c->macro_table.put(name, node);
                 }
                 return;
             case CTokIdNumLitFloat:
                 if (is_last) {
                     double value = negate ? -tok->data.num_lit_float : tok->data.num_lit_float;
-                    Tld *tld = create_global_num_lit_float(c, name, value);
-                    c->macro_table.put(name, tld);
+                    AstNode *node = trans_create_node_float_lit(c, value);
+                    c->macro_table.put(name, node);
                 }
                 return;
             case CTokIdSymbol:
@@ -2429,29 +2152,29 @@ static void process_symbol_macros(Context *c) {
     for (size_t i = 0; i < c->macro_symbols.length; i += 1) {
         MacroSymbol ms = c->macro_symbols.at(i);
 
-        // If this macro aliases another top level declaration, we can make that happen by
-        // putting another entry in the decl table pointing to the same top level decl.
-        Tld *existing_tld = get_global(c, ms.value);
-        if (!existing_tld)
+        // Check if this macro aliases another top level declaration
+        AstNode *existing_node = get_global(c, ms.value);
+        if (!existing_node || name_exists(c, ms.name))
             continue;
 
         // If a macro aliases a global variable which is a function pointer, we conclude that
         // the macro is intended to represent a function that assumes the function pointer
         // variable is non-null and calls it.
-        if (existing_tld->id == TldIdVar) {
-            TldVar *tld_var = (TldVar *)existing_tld;
-            TypeTableEntry *var_type = tld_var->var->value->type;
-            if (var_type->id == TypeTableEntryIdMaybe && !tld_var->var->src_is_const) {
-                TypeTableEntry *child_type = var_type->data.maybe.child_type;
-                if (child_type->id == TypeTableEntryIdFn) {
-                    Tld *tld = create_inline_fn_tld(c, ms.name, tld_var);
-                    c->macro_table.put(ms.name, tld);
+        if (existing_node->type == NodeTypeVariableDeclaration) {
+            AstNode *var_expr = existing_node->data.variable_declaration.expr;
+            if (var_expr != nullptr && var_expr->type == NodeTypePrefixOpExpr &&
+                var_expr->data.prefix_op_expr.prefix_op == PrefixOpMaybe)
+            {
+                AstNode *fn_proto_node = var_expr->data.prefix_op_expr.primary_expr;
+                if (fn_proto_node->type == NodeTypeFnProto) {
+                    AstNode *inline_fn_node = trans_create_node_inline_fn(c, ms.name, ms.value, fn_proto_node);
+                    c->macro_table.put(ms.name, inline_fn_node);
                     continue;
                 }
             }
         }
 
-        add_global_alias(c, ms.name, existing_tld);
+        add_global_var(c, ms.name, existing_node);
     }
 }
 
@@ -2651,15 +2374,17 @@ int parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, const ch
 
     c->ctx = &ast_unit->getASTContext();
     c->source_manager = &ast_unit->getSourceManager();
+    c->root = trans_create_node(c, NodeTypeRoot);
 
     ast_unit->visitLocalTopLevelDecls(c, decl_visitor);
 
     process_preprocessor_entities(c, *ast_unit);
 
-    process_symbol_macros(c);
-
     render_macros(c);
+    process_symbol_macros(c);
     render_aliases(c);
 
+    import->root = c->root;
+
     return 0;
 }
test/parseh.zig
@@ -21,6 +21,16 @@ pub fn addCases(cases: &tests.ParseHContext) {
         \\pub extern fn foo() -> noreturn;
     );
 
+    cases.add("simple function",
+        \\int abs(int a) {
+        \\    return a < 0 ? -a : a;
+        \\}
+    ,
+        \\export fn abs(a: c_int) -> c_int {
+        \\    return if (a < 0) -a else a;
+        \\}
+    );
+
     cases.add("enums",
         \\enum Foo {
         \\    FooA,
@@ -34,13 +44,13 @@ pub fn addCases(cases: &tests.ParseHContext) {
         \\    @"1",
         \\};
     ,
-        \\pub const FooA = 0;
+        \\pub const FooA = Foo.A;
     ,
-        \\pub const FooB = 1;
+        \\pub const FooB = Foo.B;
     ,
-        \\pub const Foo1 = 2;
+        \\pub const Foo1 = Foo.1;
     ,
-        \\pub const Foo = enum_Foo
+        \\pub const Foo = enum_Foo;
     );
 
     cases.add("restrict -> noalias",