Commit 3c2093fec6

Andrew Kelley <superjoe30@gmail.com>
2016-01-31 09:20:47
parseh understands types better and handles some situations better
See #88 Also, includes partial implementation of typedef top level declaration. See #95 Also, fix function types. Previously the way we were deduping function type pointers was incorrect.
1 parent 436e355
doc/langref.md
@@ -5,15 +5,17 @@
 ```
 Root = many(TopLevelDecl) "EOF"
 
-TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl)
+TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl | TypeDecl)
 
 CImportDecl = "c_import" Block
 
+TypeDecl = "type" "Symbol" "=" TypeExpr ";"
+
 ErrorValueDecl = "error" "Symbol" ";"
 
 GlobalVarDecl = VariableDeclaration ";"
 
-VariableDeclaration = ("var" | "const") "Symbol" option(":" PrefixOpExpression) "=" Expression
+VariableDeclaration = ("var" | "const") "Symbol" option(":" TypeExpr) "=" Expression
 
 ContainerDecl = ("struct" | "enum") "Symbol" "{" many(StructMember) "}"
 
@@ -27,7 +29,7 @@ RootExportDecl = "export" "Symbol" "String" ";"
 
 ExternDecl = "extern" (FnProto | VariableDeclaration) ";"
 
-FnProto = "fn" option("Symbol") ParamDeclList option("->" PrefixOpExpression)
+FnProto = "fn" option("Symbol") ParamDeclList option("->" TypeExpr)
 
 Directive = "#" "Symbol" "(" "String" ")"
 
@@ -37,7 +39,7 @@ FnDef = FnProto Block
 
 ParamDeclList = "(" list(ParamDecl, ",") ")"
 
-ParamDecl = option("noalias") option("Symbol" ":") PrefixOpExpression | "..."
+ParamDecl = option("noalias") option("Symbol" ":") TypeExpr | "..."
 
 Block = "{" list(option(Statement), ";") "}"
 
@@ -47,6 +49,8 @@ Label = "Symbol" ":"
 
 Expression = BlockExpression | NonBlockExpression
 
+TypeExpr = PrefixOpExpression
+
 NonBlockExpression = ReturnExpression | AssignmentExpression
 
 AsmExpression = "asm" option("volatile") "(" "String" option(AsmOutput) ")"
@@ -55,7 +59,7 @@ AsmOutput = ":" list(AsmOutputItem, ",") option(AsmInput)
 
 AsmInput = ":" list(AsmInputItem, ",") option(AsmClobbers)
 
-AsmOutputItem = "[" "Symbol" "]" "String" "(" ("Symbol" | "->" PrefixOpExpression) ")"
+AsmOutputItem = "[" "Symbol" "]" "String" "(" ("Symbol" | "->" TypeExpr) ")"
 
 AsmInputItem = "[" "Symbol" "]" "String" "(" Expression ")"
 
@@ -91,7 +95,7 @@ IfExpression = IfVarExpression | IfBoolExpression
 
 IfBoolExpression = "if" "(" Expression ")" Expression option(Else)
 
-IfVarExpression = "if" "(" ("const" | "var") "Symbol" option(":" PrefixOpExpression) "?=" Expression ")" Expression Option(Else)
+IfVarExpression = "if" "(" ("const" | "var") "Symbol" option(":" TypeExpr) "?=" Expression ")" Expression Option(Else)
 
 Else = "else" Expression
 
@@ -117,7 +121,7 @@ AdditionOperator = "+" | "-" | "++"
 
 MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression
 
-CurlySuffixExpression = PrefixOpExpression option(ContainerInitExpression)
+CurlySuffixExpression = TypeExpr option(ContainerInitExpression)
 
 MultiplyOperator = "*" | "/" | "%"
 
@@ -143,13 +147,13 @@ PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const")) | "?" | "%" | "%%"
 
 PrimaryExpression = "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." "Symbol")
 
-ArrayType = "[" option(Expression) "]" option("const") PrefixOpExpression
+ArrayType = "[" option(Expression) "]" option("const") TypeExpr
 
 GotoExpression = "goto" "Symbol"
 
 GroupedExpression = "(" Expression ")"
 
-KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error"
+KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type"
 ```
 
 ## Operator Precedence
src/all_types.hpp
@@ -124,6 +124,7 @@ enum NodeType {
     NodeTypeDirective,
     NodeTypeReturnExpr,
     NodeTypeVariableDeclaration,
+    NodeTypeTypeDecl,
     NodeTypeErrorValueDecl,
     NodeTypeBinOpExpr,
     NodeTypeUnwrapErrorExpr,
@@ -159,6 +160,7 @@ enum NodeType {
     NodeTypeStructValueField,
     NodeTypeArrayType,
     NodeTypeErrorType,
+    NodeTypeTypeLiteral,
 };
 
 struct AstNodeRoot {
@@ -212,9 +214,6 @@ struct AstNodeParamDecl {
 
     // populated by semantic analyzer
     VariableTableEntry *variable;
-    bool is_byval;
-    int src_index;
-    int gen_index;
 };
 
 struct AstNodeBlock {
@@ -256,6 +255,19 @@ struct AstNodeVariableDeclaration {
     VariableTableEntry *variable;
 };
 
+struct AstNodeTypeDecl {
+    VisibMod visib_mod;
+    ZigList<AstNode *> *directives;
+    Buf symbol;
+    AstNode *child_type;
+
+    // populated by semantic analyzer
+    TopLevelDecl top_level_decl;
+    // if this is set, don't process the node; we've already done so
+    // and here is the type (with id TypeTableEntryIdTypeDecl)
+    TypeTableEntry *override_type;
+};
+
 struct AstNodeErrorValueDecl {
     Buf name;
     VisibMod visib_mod;
@@ -684,6 +696,11 @@ struct AstNodeErrorType {
     Expr resolved_expr;
 };
 
+struct AstNodeTypeLiteral {
+    // populated by semantic analyzer
+    Expr resolved_expr;
+};
+
 struct AstNode {
     enum NodeType type;
     int line;
@@ -704,6 +721,7 @@ struct AstNode {
         AstNodeBlock block;
         AstNodeReturnExpr return_expr;
         AstNodeVariableDeclaration variable_declaration;
+        AstNodeTypeDecl type_decl;
         AstNodeErrorValueDecl error_value_decl;
         AstNodeBinOpExpr bin_op_expr;
         AstNodeUnwrapErrorExpr unwrap_err_expr;
@@ -740,6 +758,7 @@ struct AstNode {
         AstNodeContinueExpr continue_expr;
         AstNodeArrayType array_type;
         AstNodeErrorType error_type;
+        AstNodeTypeLiteral type_literal;
     } data;
 };
 
@@ -755,6 +774,24 @@ struct AsmToken {
     int end;
 };
 
+struct FnTypeParamInfo {
+    bool is_noalias;
+    TypeTableEntry *type;
+};
+
+struct FnTypeId {
+    TypeTableEntry *return_type;
+    FnTypeParamInfo *param_info;
+    int param_count;
+    bool is_var_args;
+    bool is_naked;
+    bool is_extern;
+};
+
+uint32_t fn_type_id_hash(FnTypeId);
+bool fn_type_id_eql(FnTypeId a, FnTypeId b);
+
+
 struct TypeTableEntryPointer {
     TypeTableEntry *child_type;
     bool is_const;
@@ -820,17 +857,25 @@ struct TypeTableEntryEnum {
     bool complete;
 };
 
+struct FnGenParamInfo {
+    int src_index;
+    int gen_index;
+    bool is_byval;
+};
+
 struct TypeTableEntryFn {
-    TypeTableEntry *src_return_type;
+    FnTypeId fn_type_id;
     TypeTableEntry *gen_return_type;
-    TypeTableEntry **param_types;
-    int src_param_count;
-    LLVMTypeRef raw_type_ref;
-    bool is_var_args;
     int gen_param_count;
+    FnGenParamInfo *gen_param_info;
+
+    LLVMTypeRef raw_type_ref;
     LLVMCallConv calling_convention;
-    bool is_extern;
-    bool is_naked;
+};
+
+struct TypeTableEntryTypeDecl {
+    TypeTableEntry *child_type;
+    TypeTableEntry *canonical_type;
 };
 
 enum TypeTableEntryId {
@@ -852,6 +897,7 @@ enum TypeTableEntryId {
     TypeTableEntryIdPureError,
     TypeTableEntryIdEnum,
     TypeTableEntryIdFn,
+    TypeTableEntryIdTypeDecl,
 };
 
 struct TypeTableEntry {
@@ -873,6 +919,7 @@ struct TypeTableEntry {
         TypeTableEntryError error;
         TypeTableEntryEnum enumeration;
         TypeTableEntryFn fn;
+        TypeTableEntryTypeDecl type_decl;
     } data;
 
     // use these fields to make sure we don't duplicate type table entries for the same type
@@ -900,7 +947,6 @@ struct ImportTableEntry {
 
     // reminder: hash tables must be initialized before use
     HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
-    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> fn_type_table;
 };
 
 struct LabelTableEntry {
@@ -969,12 +1015,14 @@ struct CodeGen {
     HashMap<Buf *, BuiltinFnEntry *, buf_hash, buf_eql_buf> builtin_fn_table;
     HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> primitive_type_table;
     HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> unresolved_top_level_decls;
+    HashMap<FnTypeId, TypeTableEntry *, fn_type_id_hash, fn_type_id_eql> fn_type_table;
 
     uint32_t next_unresolved_index;
 
     struct {
         TypeTableEntry *entry_bool;
         TypeTableEntry *entry_int[2][4]; // [signed,unsigned][8,16,32,64]
+        TypeTableEntry *entry_c_int[8];
         TypeTableEntry *entry_u8;
         TypeTableEntry *entry_u16;
         TypeTableEntry *entry_u32;
@@ -1082,12 +1130,16 @@ struct BlockContext {
     Buf *c_import_buf;
 };
 
-struct ParseH {
-    ZigList<ErrorMsg*> errors;
-    ZigList<AstNode *> fn_list;
-    ZigList<AstNode *> struct_list;
-    ZigList<AstNode *> var_list;
-    ZigList<AstNode *> incomplete_struct_list;
+enum CIntType {
+    CIntTypeShort,
+    CIntTypeUShort,
+    CIntTypeInt,
+    CIntTypeUInt,
+    CIntTypeLong,
+    CIntTypeULong,
+    CIntTypeLongLong,
+    CIntTypeULongLong,
 };
 
+
 #endif
src/analyze.cpp
@@ -56,6 +56,7 @@ static AstNode *first_executing_node(AstNode *node) {
         case NodeTypeDirective:
         case NodeTypeReturnExpr:
         case NodeTypeVariableDeclaration:
+        case NodeTypeTypeDecl:
         case NodeTypeErrorValueDecl:
         case NodeTypeNumberLiteral:
         case NodeTypeStringLiteral:
@@ -83,6 +84,7 @@ static AstNode *first_executing_node(AstNode *node) {
         case NodeTypeSwitchProng:
         case NodeTypeArrayType:
         case NodeTypeErrorType:
+        case NodeTypeTypeLiteral:
         case NodeTypeContainerInitExpr:
             return node;
     }
@@ -123,6 +125,7 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
         case TypeTableEntryIdErrorUnion:
         case TypeTableEntryIdPureError:
         case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdTypeDecl:
             // nothing to init
             break;
         case TypeTableEntryIdStruct:
@@ -149,7 +152,7 @@ static int bits_needed_for_unsigned(uint64_t x) {
     }
 }
 
-static TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
+TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
     return get_int_type(g, false, bits_needed_for_unsigned(x));
 }
 
@@ -165,12 +168,15 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
         buf_resize(&entry->name, 0);
         buf_appendf(&entry->name, "&%s%s", const_str, buf_ptr(&child_type->name));
 
+        TypeTableEntry *canon_child_type = get_underlying_type(child_type);
+        assert(canon_child_type->id != TypeTableEntryIdInvalid);
+
         bool zero_bits;
-        if (child_type->size_in_bits == 0) {
-            if (child_type->id == TypeTableEntryIdStruct) {
-                zero_bits = child_type->data.structure.complete;
-            } else if (child_type->id == TypeTableEntryIdEnum) {
-                zero_bits = child_type->data.enumeration.complete;
+        if (canon_child_type->size_in_bits == 0) {
+            if (canon_child_type->id == TypeTableEntryIdStruct) {
+                zero_bits = canon_child_type->data.structure.complete;
+            } else if (canon_child_type->id == TypeTableEntryIdEnum) {
+                zero_bits = canon_child_type->data.enumeration.complete;
             } else {
                 zero_bits = true;
             }
@@ -196,7 +202,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
     }
 }
 
-static TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
+TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
     if (child_type->maybe_parent) {
         TypeTableEntry *entry = child_type->maybe_parent;
         return entry;
@@ -317,8 +323,7 @@ static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
     }
 }
 
-static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size)
-{
+TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) {
     auto existing_entry = child_type->arrays_by_size.maybe_get(array_size);
     if (existing_entry) {
         TypeTableEntry *entry = existing_entry->value;
@@ -417,147 +422,109 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, TypeTableEntry *c
     }
 }
 
-// If the node does not have a constant expression value with a metatype, generates an error
-// and returns invalid type. Otherwise, returns the type of the constant expression value.
-// Must be called after analyze_expression on the same node.
-static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
-    if (node->type == NodeTypeSymbol && node->data.symbol_expr.override_type_entry) {
-        return node->data.symbol_expr.override_type_entry;
-    }
-    Expr *expr = get_resolved_expr(node);
-    assert(expr->type_entry);
-    if (expr->type_entry->id == TypeTableEntryIdInvalid) {
-        return g->builtin_types.entry_invalid;
-    } else if (expr->type_entry->id == TypeTableEntryIdMetaType) {
-        // OK
-    } else {
-        add_node_error(g, node, buf_sprintf("expected type, found expression"));
-        return g->builtin_types.entry_invalid;
-    }
+TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *child_type) {
+    TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdTypeDecl);
 
-    ConstExprValue *const_val = &expr->const_val;
-    if (!const_val->ok) {
-        add_node_error(g, node, buf_sprintf("unable to resolve constant expression"));
-        return g->builtin_types.entry_invalid;
-    }
+    buf_init_from_str(&entry->name, name);
 
-    return const_val->data.x_type;
-}
+    entry->type_ref = child_type->type_ref;
+    entry->type_ref = child_type->type_ref;
+    entry->di_type = child_type->di_type;
+    entry->size_in_bits = child_type->size_in_bits;
+    entry->align_in_bits = child_type->align_in_bits;
 
-// Calls analyze_expression on node, and then resolve_type.
-static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        AstNode *node)
-{
-    AstNode **node_ptr = node->parent_field;
-    analyze_expression(g, import, context, nullptr, *node_ptr);
-    return resolve_type(g, *node_ptr);
-}
+    entry->data.type_decl.child_type = child_type;
 
-static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        TypeTableEntry *expected_type, AstNode *node, bool is_naked)
-{
-    assert(node->type == NodeTypeFnProto);
-    AstNodeFnProto *fn_proto = &node->data.fn_proto;
+    if (child_type->id == TypeTableEntryIdTypeDecl) {
+        entry->data.type_decl.canonical_type = child_type->data.type_decl.canonical_type;
+    } else {
+        entry->data.type_decl.canonical_type = child_type;
+    }
 
-    if (fn_proto->skip) {
-        return g->builtin_types.entry_invalid;
+    return entry;
+}
+
+// accepts ownership of fn_type_id memory
+TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId fn_type_id) {
+    auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
+    if (table_entry) {
+        return table_entry->value;
     }
 
     TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
-    fn_type->data.fn.is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport);
-    fn_type->data.fn.is_naked = is_naked;
-    fn_type->data.fn.calling_convention = fn_proto->is_extern ? LLVMCCallConv : LLVMFastCallConv;
+    fn_type->data.fn.fn_type_id = fn_type_id;
+    fn_type->data.fn.calling_convention = fn_type_id.is_extern ? LLVMCCallConv : LLVMFastCallConv;
 
-    int src_param_count = node->data.fn_proto.params.length;
     fn_type->size_in_bits = g->pointer_size_bytes * 8;
     fn_type->align_in_bits = g->pointer_size_bytes * 8;
-    fn_type->data.fn.src_param_count = src_param_count;
-    fn_type->data.fn.param_types = allocate<TypeTableEntry*>(src_param_count);
 
-    // first, analyze the parameters and return type in order they appear in
-    // source code in order for error messages to be in the best order.
+    // populate the name of the type
     buf_resize(&fn_type->name, 0);
-    const char *extern_str = fn_type->data.fn.is_extern ? "extern " : "";
-    const char *naked_str = fn_type->data.fn.is_naked ? "naked " : "";
+    const char *extern_str = fn_type_id.is_extern ? "extern " : "";
+    const char *naked_str = fn_type_id.is_naked ? "naked " : "";
     buf_appendf(&fn_type->name, "%s%sfn(", extern_str, naked_str);
-    for (int i = 0; i < src_param_count; i += 1) {
-        AstNode *child = node->data.fn_proto.params.at(i);
-        assert(child->type == NodeTypeParamDecl);
-        TypeTableEntry *type_entry = analyze_type_expr(g, import, import->block_context,
-                child->data.param_decl.type);
-        fn_type->data.fn.param_types[i] = type_entry;
+    for (int i = 0; i < fn_type_id.param_count; i += 1) {
+        FnTypeParamInfo *param_info = &fn_type_id.param_info[i];
 
+        TypeTableEntry *param_type = param_info->type;
         const char *comma = (i == 0) ? "" : ", ";
-        buf_appendf(&fn_type->name, "%s%s", comma, buf_ptr(&type_entry->name));
+        const char *noalias_str = param_info->is_noalias ? "noalias " : "";
+        buf_appendf(&fn_type->name, "%s%s%s", comma, noalias_str, buf_ptr(&param_type->name));
     }
 
-    TypeTableEntry *return_type = analyze_type_expr(g, import, import->block_context,
-            node->data.fn_proto.return_type);
-    fn_type->data.fn.src_return_type = return_type;
-    if (return_type->id == TypeTableEntryIdInvalid) {
-        fn_proto->skip = true;
-    }
-    fn_type->data.fn.is_var_args = fn_proto->is_var_args;
-    if (fn_proto->is_var_args) {
-        const char *comma = (src_param_count == 0) ? "" : ", ";
+    if (fn_type_id.is_var_args) {
+        const char *comma = (fn_type_id.param_count == 0) ? "" : ", ";
         buf_appendf(&fn_type->name, "%s...", comma);
     }
     buf_appendf(&fn_type->name, ")");
-    if (return_type->id != TypeTableEntryIdVoid) {
-        buf_appendf(&fn_type->name, " -> %s", buf_ptr(&return_type->name));
+    if (fn_type_id.return_type->id != TypeTableEntryIdVoid) {
+        buf_appendf(&fn_type->name, " -> %s", buf_ptr(&fn_type_id.return_type->name));
     }
 
 
     // next, loop over the parameters again and compute debug information
     // and codegen information
-    bool first_arg_return = !fn_proto->skip && handle_is_ptr(return_type);
+    bool first_arg_return = handle_is_ptr(fn_type_id.return_type);
     // +1 for maybe making the first argument the return value
-    LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + src_param_count);
+    LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(1 + fn_type_id.param_count);
     // +1 because 0 is the return type and +1 for maybe making first arg ret val
-    LLVMZigDIType **param_di_types = allocate<LLVMZigDIType*>(2 + src_param_count);
-    param_di_types[0] = return_type->di_type;
+    LLVMZigDIType **param_di_types = allocate<LLVMZigDIType*>(2 + fn_type_id.param_count);
+    param_di_types[0] = fn_type_id.return_type->di_type;
     int gen_param_index = 0;
     TypeTableEntry *gen_return_type;
     if (first_arg_return) {
-        TypeTableEntry *gen_type = get_pointer_to_type(g, return_type, false);
+        TypeTableEntry *gen_type = get_pointer_to_type(g, fn_type_id.return_type, false);
         gen_param_types[gen_param_index] = gen_type->type_ref;
         gen_param_index += 1;
         // after the gen_param_index += 1 because 0 is the return type
         param_di_types[gen_param_index] = gen_type->di_type;
         gen_return_type = g->builtin_types.entry_void;
-    } else if (return_type->size_in_bits == 0) {
+    } else if (fn_type_id.return_type->size_in_bits == 0) {
         gen_return_type = g->builtin_types.entry_void;
     } else {
-        gen_return_type = return_type;
+        gen_return_type = fn_type_id.return_type;
     }
     fn_type->data.fn.gen_return_type = gen_return_type;
-    for (int i = 0; i < src_param_count; i += 1) {
-        AstNode *child = node->data.fn_proto.params.at(i);
-        assert(child->type == NodeTypeParamDecl);
-        TypeTableEntry *type_entry = fn_type->data.fn.param_types[i];
-
-        if (type_entry->id == TypeTableEntryIdUnreachable) {
-            add_node_error(g, child->data.param_decl.type,
-                buf_sprintf("parameter of type 'unreachable' not allowed"));
-            fn_proto->skip = true;
-        } else if (type_entry->id == TypeTableEntryIdInvalid) {
-            fn_proto->skip = true;
-        }
 
-        child->data.param_decl.src_index = i;
-        child->data.param_decl.gen_index = -1;
+    fn_type->data.fn.gen_param_info = allocate<FnGenParamInfo>(fn_type_id.param_count);
+    for (int i = 0; i < fn_type_id.param_count; i += 1) {
+        FnTypeParamInfo *src_param_info = &fn_type->data.fn.fn_type_id.param_info[i];
+        TypeTableEntry *type_entry = src_param_info->type;
+        FnGenParamInfo *gen_param_info = &fn_type->data.fn.gen_param_info[i];
 
-        if (!fn_proto->skip && type_entry->size_in_bits > 0) {
+        gen_param_info->src_index = i;
+        gen_param_info->gen_index = -1;
 
+        if (type_entry->size_in_bits > 0) {
             TypeTableEntry *gen_type;
             if (handle_is_ptr(type_entry)) {
                 gen_type = get_pointer_to_type(g, type_entry, true);
-                child->data.param_decl.is_byval = true;
+                gen_param_info->is_byval = true;
             } else {
                 gen_type = type_entry;
             }
             gen_param_types[gen_param_index] = gen_type->type_ref;
-            child->data.param_decl.gen_index = gen_param_index;
+            gen_param_info->gen_index = gen_param_index;
 
             gen_param_index += 1;
 
@@ -568,24 +535,168 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
 
     fn_type->data.fn.gen_param_count = gen_param_index;
 
+    fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
+            gen_param_types, gen_param_index, fn_type_id.is_var_args);
+    fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0);
+    LLVMZigDIFile *di_file = nullptr; // TODO if we get a crash maybe this is the culprit
+    fn_type->di_type = LLVMZigCreateSubroutineType(g->dbuilder, di_file,
+            param_di_types, gen_param_index + 1, 0);
+
+    g->fn_type_table.put(fn_type_id, fn_type);
+
+    return fn_type;
+}
+
+static TypeTableEntryId container_to_type(ContainerKind kind) {
+    switch (kind) {
+        case ContainerKindStruct:
+            return TypeTableEntryIdStruct;
+        case ContainerKindEnum:
+            return TypeTableEntryIdEnum;
+    }
+    zig_unreachable();
+}
+
+TypeTableEntry *get_partial_container_type(CodeGen *g, ImportTableEntry *import,
+        ContainerKind kind, AstNode *decl_node, const char *name)
+{
+    TypeTableEntryId type_id = container_to_type(kind);
+    TypeTableEntry *entry = new_type_table_entry(type_id);
+
+    switch (kind) {
+        case ContainerKindStruct:
+            entry->data.structure.decl_node = decl_node;
+            break;
+        case ContainerKindEnum:
+            entry->data.enumeration.decl_node = decl_node;
+            break;
+    }
+
+    unsigned line = decl_node ? decl_node->line : 0;
+
+    entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name);
+    entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder,
+        LLVMZigTag_DW_structure_type(), name,
+        LLVMZigFileToScope(import->di_file), import->di_file, line + 1);
+
+    buf_init_from_str(&entry->name, name);
+
+    return entry;
+}
+
+
+TypeTableEntry *get_underlying_type(TypeTableEntry *type_entry) {
+    if (type_entry->id == TypeTableEntryIdTypeDecl) {
+        return type_entry->data.type_decl.canonical_type;
+    } else {
+        return type_entry;
+    }
+}
+
+// If the node does not have a constant expression value with a metatype, generates an error
+// and returns invalid type. Otherwise, returns the type of the constant expression value.
+// Must be called after analyze_expression on the same node.
+static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
+    if (node->type == NodeTypeSymbol && node->data.symbol_expr.override_type_entry) {
+        return node->data.symbol_expr.override_type_entry;
+    }
+    Expr *expr = get_resolved_expr(node);
+    assert(expr->type_entry);
+    if (expr->type_entry->id == TypeTableEntryIdInvalid) {
+        return g->builtin_types.entry_invalid;
+    } else if (expr->type_entry->id == TypeTableEntryIdMetaType) {
+        // OK
+    } else {
+        add_node_error(g, node, buf_sprintf("expected type, found expression"));
+        return g->builtin_types.entry_invalid;
+    }
+
+    ConstExprValue *const_val = &expr->const_val;
+    if (!const_val->ok) {
+        add_node_error(g, node, buf_sprintf("unable to resolve constant expression"));
+        return g->builtin_types.entry_invalid;
+    }
+
+    return const_val->data.x_type;
+}
+
+// Calls analyze_expression on node, and then resolve_type.
+static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        AstNode *node)
+{
+    AstNode **node_ptr = node->parent_field;
+    analyze_expression(g, import, context, nullptr, *node_ptr);
+    return resolve_type(g, *node_ptr);
+}
+
+static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node, bool is_naked)
+{
+    assert(node->type == NodeTypeFnProto);
+    AstNodeFnProto *fn_proto = &node->data.fn_proto;
+
     if (fn_proto->skip) {
         return g->builtin_types.entry_invalid;
     }
 
-    auto table_entry = import->fn_type_table.maybe_get(&fn_type->name);
-    if (table_entry) {
-        return table_entry->value;
-    } else {
-        fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
-                gen_param_types, gen_param_index, fn_type->data.fn.is_var_args);
-        fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0);
-        fn_type->di_type = LLVMZigCreateSubroutineType(g->dbuilder, import->di_file,
-                param_di_types, gen_param_index + 1, 0);
+    FnTypeId fn_type_id;
+    fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport);
+    fn_type_id.is_naked = is_naked;
+    fn_type_id.param_count = node->data.fn_proto.params.length;
+    fn_type_id.param_info = allocate<FnTypeParamInfo>(fn_type_id.param_count);
+    fn_type_id.is_var_args = fn_proto->is_var_args;
+    fn_type_id.return_type = analyze_type_expr(g, import, import->block_context, node->data.fn_proto.return_type);
+
+    if (fn_type_id.return_type->id == TypeTableEntryIdInvalid) {
+        fn_proto->skip = true;
+    }
 
-        import->fn_type_table.put(&fn_type->name, fn_type);
+    for (int i = 0; i < fn_type_id.param_count; i += 1) {
+        AstNode *child = node->data.fn_proto.params.at(i);
+        assert(child->type == NodeTypeParamDecl);
+        TypeTableEntry *type_entry = analyze_type_expr(g, import, import->block_context,
+                child->data.param_decl.type);
+        switch (type_entry->id) {
+            case TypeTableEntryIdInvalid:
+                fn_proto->skip = true;
+                break;
+            case TypeTableEntryIdNumLitFloat:
+            case TypeTableEntryIdNumLitInt:
+            case TypeTableEntryIdUndefLit:
+            case TypeTableEntryIdMetaType:
+            case TypeTableEntryIdUnreachable:
+                fn_proto->skip = true;
+                add_node_error(g, child->data.param_decl.type,
+                    buf_sprintf("parameter of type '%s' not allowed'", buf_ptr(&type_entry->name)));
+                break;
+            case TypeTableEntryIdVoid:
+            case TypeTableEntryIdBool:
+            case TypeTableEntryIdInt:
+            case TypeTableEntryIdFloat:
+            case TypeTableEntryIdPointer:
+            case TypeTableEntryIdArray:
+            case TypeTableEntryIdStruct:
+            case TypeTableEntryIdMaybe:
+            case TypeTableEntryIdErrorUnion:
+            case TypeTableEntryIdPureError:
+            case TypeTableEntryIdEnum:
+            case TypeTableEntryIdFn:
+            case TypeTableEntryIdTypeDecl:
+                break;
+        }
+        if (type_entry->id == TypeTableEntryIdInvalid) {
+            fn_proto->skip = true;
+        }
+        FnTypeParamInfo *param_info = &fn_type_id.param_info[i];
+        param_info->type = type_entry;
+        param_info->is_noalias = child->data.param_decl.is_noalias;
+    }
 
-        return fn_type;
+    if (fn_proto->skip) {
+        return g->builtin_types.entry_invalid;
     }
+
+    return get_fn_type(g, fn_type_id);
 }
 
 
@@ -640,14 +751,14 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
     if (fn_table_entry->is_inline) {
         LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMAlwaysInlineAttribute);
     }
-    if (fn_type->data.fn.is_naked) {
+    if (fn_type->data.fn.fn_type_id.is_naked) {
         LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNakedAttribute);
     }
 
     LLVMSetLinkage(fn_table_entry->fn_value, fn_table_entry->internal_linkage ?
             LLVMInternalLinkage : LLVMExternalLinkage);
 
-    if (fn_type->data.fn.src_return_type->id == TypeTableEntryIdUnreachable) {
+    if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) {
         LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoReturnAttribute);
     }
     LLVMSetFunctionCallConv(fn_table_entry->fn_value, fn_type->data.fn.calling_convention);
@@ -691,6 +802,7 @@ static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_
 }
 
 static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *enum_type) {
+    // if you change this logic you likely must also change similar logic in parseh.cpp
     assert(enum_type->id == TypeTableEntryIdEnum);
 
     AstNode *decl_node = enum_type->data.enumeration.decl_node;
@@ -853,6 +965,8 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt
 }
 
 static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type) {
+    // if you change the logic of this function likely you must make a similar change in
+    // parseh.cpp
     assert(struct_type->id == TypeTableEntryIdStruct);
 
     AstNode *decl_node = struct_type->data.structure.decl_node;
@@ -1104,15 +1218,12 @@ static void resolve_c_import_decl(CodeGen *g, ImportTableEntry *parent_import, A
 
     ImportTableEntry *child_import = allocate<ImportTableEntry>(1);
     child_import->fn_table.init(32);
-    child_import->fn_type_table.init(32);
     child_import->c_import_node = node;
 
     ZigList<ErrorMsg *> errors = {0};
 
     int err;
-    if ((err = parse_h_buf(child_import, &errors, child_context->c_import_buf, g->clang_argv, g->clang_argv_len,
-                    buf_ptr(g->libc_include_path), false, &g->next_node_index)))
-    {
+    if ((err = parse_h_buf(child_import, &errors, child_context->c_import_buf, g, node))) {
         zig_panic("unable to parse h file: %s\n", err_str(err));
     }
 
@@ -1175,6 +1286,27 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
                 VariableTableEntry *var = analyze_variable_declaration(g, import, import->block_context,
                         nullptr, node);
                 g->global_vars.append(var);
+                break;
+            }
+        case NodeTypeTypeDecl:
+            {
+                AstNode *type_node = node->data.type_decl.child_type;
+                Buf *decl_name = &node->data.type_decl.symbol;
+
+                TypeTableEntry *typedecl_type;
+                if (node->data.type_decl.override_type) {
+                    typedecl_type = node->data.type_decl.override_type;
+                } else {
+                    TypeTableEntry *child_type = analyze_type_expr(g, import, import->block_context, type_node);
+                    if (child_type->id == TypeTableEntryIdInvalid) {
+                        typedecl_type = child_type;
+                    } else {
+                        typedecl_type = get_typedecl_type(g, buf_ptr(decl_name), child_type);
+                    }
+                }
+
+                import->block_context->type_table.put(decl_name, typedecl_type);
+
                 break;
             }
         case NodeTypeErrorValueDecl:
@@ -1224,6 +1356,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeContainerInitExpr:
         case NodeTypeArrayType:
         case NodeTypeErrorType:
+        case NodeTypeTypeLiteral:
             zig_unreachable();
     }
 
@@ -1278,8 +1411,10 @@ static bool type_has_codegen_value(TypeTableEntryId id) {
         case TypeTableEntryIdEnum:
         case TypeTableEntryIdFn:
             return true;
+
+        case TypeTableEntryIdTypeDecl:
+            zig_unreachable();
     }
-    zig_unreachable();
 }
 
 static void add_global_const_expr(CodeGen *g, Expr *expr) {
@@ -1376,25 +1511,30 @@ static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTable
     if (expected_type->id == TypeTableEntryIdFn &&
         actual_type->id == TypeTableEntryIdFn)
     {
-        if (expected_type->data.fn.is_extern != actual_type->data.fn.is_extern) {
+        if (expected_type->data.fn.fn_type_id.is_extern != actual_type->data.fn.fn_type_id.is_extern) {
             return false;
         }
-        if (expected_type->data.fn.is_naked != actual_type->data.fn.is_naked) {
+        if (expected_type->data.fn.fn_type_id.is_naked != actual_type->data.fn.fn_type_id.is_naked) {
             return false;
         }
-        if (!types_match_const_cast_only(expected_type->data.fn.src_return_type,
-            actual_type->data.fn.src_return_type))
+        if (!types_match_const_cast_only(expected_type->data.fn.fn_type_id.return_type,
+            actual_type->data.fn.fn_type_id.return_type))
         {
             return false;
         }
-        if (expected_type->data.fn.src_param_count != actual_type->data.fn.src_param_count) {
+        if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
             return false;
         }
-        for (int i = 0; i < expected_type->data.fn.src_param_count; i += 1) {
+        for (int i = 0; i < expected_type->data.fn.fn_type_id.param_count; i += 1) {
             // note it's reversed for parameters
-            if (types_match_const_cast_only(actual_type->data.fn.param_types[i],
-                        expected_type->data.fn.param_types[i]))
-            {
+            FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i];
+            FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i];
+
+            if (!types_match_const_cast_only(actual_param_info->type, expected_param_info->type)) {
+                return false;
+            }
+
+            if (expected_param_info->is_noalias != actual_param_info->is_noalias) {
                 return false;
             }
         }
@@ -3610,6 +3750,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
                     case TypeTableEntryIdPureError:
                     case TypeTableEntryIdEnum:
                     case TypeTableEntryIdFn:
+                    case TypeTableEntryIdTypeDecl:
                         return resolve_expr_const_val_as_type(g, node, type_entry);
                 }
             }
@@ -3664,14 +3805,14 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
     assert(node->type == NodeTypeFnCallExpr);
 
     // count parameters
-    int src_param_count = fn_type->data.fn.src_param_count;
+    int src_param_count = fn_type->data.fn.fn_type_id.param_count;
     int actual_param_count = node->data.fn_call_expr.params.length;
 
     if (struct_type) {
         actual_param_count += 1;
     }
 
-    if (fn_type->data.fn.is_var_args) {
+    if (fn_type->data.fn.fn_type_id.is_var_args) {
         if (actual_param_count < src_param_count) {
             add_node_error(g, node,
                 buf_sprintf("expected at least %d arguments, got %d", src_param_count, actual_param_count));
@@ -3689,12 +3830,12 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import,
         TypeTableEntry *expected_param_type = nullptr;
         int fn_proto_i = i + (struct_type ? 1 : 0);
         if (fn_proto_i < src_param_count) {
-            expected_param_type = fn_type->data.fn.param_types[fn_proto_i];
+            expected_param_type = fn_type->data.fn.fn_type_id.param_info[fn_proto_i].type;
         }
         analyze_expression(g, import, context, expected_param_type, child);
     }
 
-    TypeTableEntry *return_type = fn_type->data.fn.src_return_type;
+    TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type;
 
     if (return_type->id == TypeTableEntryIdInvalid) {
         return return_type;
@@ -4304,6 +4445,9 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import,
         case NodeTypeErrorType:
             return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_pure_error);
             break;
+        case NodeTypeTypeLiteral:
+            return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_type);
+            break;
         case NodeTypeSwitchExpr:
             return_type = analyze_switch_expr(g, import, context, expected_type, node);
             break;
@@ -4322,6 +4466,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import,
         case NodeTypeStructField:
         case NodeTypeStructValueField:
         case NodeTypeErrorValueDecl:
+        case NodeTypeTypeDecl:
             zig_unreachable();
     }
     assert(return_type);
@@ -4354,8 +4499,9 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
 
     BlockContext *context = node->data.fn_def.block_context;
 
+    FnTableEntry *fn_table_entry = fn_proto_node->data.fn_proto.fn_table_entry;
+    TypeTableEntry *fn_type = fn_table_entry->type_entry;
     AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
-    bool is_exported = (fn_proto->visib_mod == VisibModExport);
     for (int i = 0; i < fn_proto->params.length; i += 1) {
         AstNode *param_decl_node = fn_proto->params.at(i);
         assert(param_decl_node->type == NodeTypeParamDecl);
@@ -4369,9 +4515,9 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
                 buf_sprintf("noalias on non-pointer parameter"));
         }
 
-        if (is_exported && type->id == TypeTableEntryIdStruct) {
+        if (fn_type->data.fn.fn_type_id.is_extern && type->id == TypeTableEntryIdStruct) {
             add_node_error(g, param_decl_node,
-                buf_sprintf("byvalue struct parameters not yet supported on exported functions"));
+                buf_sprintf("byvalue struct parameters not yet supported on extern functions"));
         }
 
         if (buf_len(&param_decl->name) == 0) {
@@ -4382,16 +4528,15 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
         var->src_arg_index = i;
         param_decl_node->data.param_decl.variable = var;
 
-        var->gen_arg_index = param_decl_node->data.param_decl.gen_index;
+        var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
     }
 
-    TypeTableEntry *expected_type = unwrapped_node_type(fn_proto->return_type);
+    TypeTableEntry *expected_type = fn_type->data.fn.fn_type_id.return_type;
     TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);
 
     node->data.fn_def.implicit_return_type = block_return_type;
 
     {
-        FnTableEntry *fn_table_entry = fn_proto_node->data.fn_proto.fn_table_entry;
         auto it = fn_table_entry->label_table.entry_iterator();
         for (;;) {
             auto *entry = it.next();
@@ -4427,6 +4572,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeVariableDeclaration:
         case NodeTypeErrorValueDecl:
         case NodeTypeFnProto:
+        case NodeTypeTypeDecl:
             // already took care of these
             break;
         case NodeTypeDirective:
@@ -4466,6 +4612,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeContainerInitExpr:
         case NodeTypeArrayType:
         case NodeTypeErrorType:
+        case NodeTypeTypeLiteral:
             zig_unreachable();
     }
 }
@@ -4485,10 +4632,14 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeContinue:
         case NodeTypeErrorValueDecl:
         case NodeTypeErrorType:
+        case NodeTypeTypeLiteral:
             // no dependencies on other top level declarations
             break;
         case NodeTypeSymbol:
             {
+                if (node->data.symbol_expr.override_type_entry) {
+                    break;
+                }
                 Buf *name = &node->data.symbol_expr.symbol;
                 auto table_entry = g->primitive_type_table.maybe_get(name);
                 if (!table_entry) {
@@ -4627,6 +4778,9 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeParamDecl:
             collect_expr_decl_deps(g, import, node->data.param_decl.type, decl_node);
             break;
+        case NodeTypeTypeDecl:
+            collect_expr_decl_deps(g, import, node->data.type_decl.child_type, decl_node);
+            break;
         case NodeTypeVariableDeclaration:
         case NodeTypeRootExportDecl:
         case NodeTypeFnDef:
@@ -4642,16 +4796,6 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
     }
 }
 
-static TypeTableEntryId container_to_type(ContainerKind kind) {
-    switch (kind) {
-        case ContainerKindStruct:
-            return TypeTableEntryIdStruct;
-        case ContainerKindEnum:
-            return TypeTableEntryIdEnum;
-    }
-    zig_unreachable();
-}
-
 static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node) {
     switch (node->type) {
         case NodeTypeRoot:
@@ -4669,28 +4813,16 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
                 }
                 if (table_entry) {
                     node->data.struct_decl.type_entry = table_entry->value;
-                    add_node_error(g, node,
-                            buf_sprintf("redefinition of '%s'", buf_ptr(name)));
+                    add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(name)));
                 } else {
-                    TypeTableEntryId type_id = container_to_type(node->data.struct_decl.kind);
-                    TypeTableEntry *entry = new_type_table_entry(type_id);
-                    switch (node->data.struct_decl.kind) {
-                        case ContainerKindStruct:
-                            entry->data.structure.decl_node = node;
-                            break;
-                        case ContainerKindEnum:
-                            entry->data.enumeration.decl_node = node;
-                            break;
+                    TypeTableEntry *entry;
+                    if (node->data.struct_decl.type_entry) {
+                        entry = node->data.struct_decl.type_entry;
+                    } else {
+                        entry = get_partial_container_type(g, import,
+                                node->data.struct_decl.kind, node, buf_ptr(name));
                     }
 
-                    entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(name));
-                    entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder,
-                        LLVMZigTag_DW_structure_type(), buf_ptr(name),
-                        LLVMZigFileToScope(import->di_file), import->di_file, node->line + 1);
-
-                    buf_init_from_buf(&entry->name, name);
-                    // put off adding the debug type until we do the full struct body
-                    // this type is incomplete until we do another pass
                     import->block_context->type_table.put(&entry->name, entry);
                     node->data.struct_decl.type_entry = entry;
 
@@ -4761,6 +4893,23 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
                 }
                 break;
             }
+        case NodeTypeTypeDecl:
+            {
+                // determine which other top level declarations this variable declaration depends on.
+                TopLevelDecl *decl_node = &node->data.type_decl.top_level_decl;
+                decl_node->deps.init(1);
+                collect_expr_decl_deps(g, import, node, decl_node);
+
+                Buf *name = &node->data.type_decl.symbol;
+                decl_node->name = name;
+                decl_node->import = import;
+                if (decl_node->deps.size() > 0) {
+                    g->unresolved_top_level_decls.put(name, node);
+                } else {
+                    resolve_top_level_decl(g, import, node);
+                }
+                break;
+            }
         case NodeTypeFnProto:
             {
                 // if the name is missing, we immediately announce an error
@@ -4848,6 +4997,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
         case NodeTypeStructValueField:
         case NodeTypeArrayType:
         case NodeTypeErrorType:
+        case NodeTypeTypeLiteral:
             zig_unreachable();
     }
 }
@@ -5063,6 +5213,8 @@ Expr *get_resolved_expr(AstNode *node) {
             return &node->data.array_type.resolved_expr;
         case NodeTypeErrorType:
             return &node->data.error_type.resolved_expr;
+        case NodeTypeTypeLiteral:
+            return &node->data.type_literal.resolved_expr;
         case NodeTypeSwitchExpr:
             return &node->data.switch_expr.resolved_expr;
         case NodeTypeFnProto:
@@ -5081,6 +5233,7 @@ Expr *get_resolved_expr(AstNode *node) {
         case NodeTypeStructField:
         case NodeTypeStructValueField:
         case NodeTypeErrorValueDecl:
+        case NodeTypeTypeDecl:
             zig_unreachable();
     }
     zig_unreachable();
@@ -5098,6 +5251,8 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
             return &node->data.error_value_decl.top_level_decl;
         case NodeTypeCImport:
             return &node->data.c_import.top_level_decl;
+        case NodeTypeTypeDecl:
+            return &node->data.type_decl.top_level_decl;
         case NodeTypeNumberLiteral:
         case NodeTypeReturnExpr:
         case NodeTypeBinOpExpr:
@@ -5138,6 +5293,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
         case NodeTypeStructValueField:
         case NodeTypeArrayType:
         case NodeTypeErrorType:
+        case NodeTypeTypeLiteral:
             zig_unreachable();
     }
     zig_unreachable();
@@ -5178,6 +5334,14 @@ TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits) {
     return *get_int_type_ptr(g, is_signed, size_in_bits);
 }
 
+TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) {
+    return &g->builtin_types.entry_c_int[c_int_type];
+}
+
+TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type) {
+    return *get_c_int_type_ptr(g, c_int_type);
+}
+
 bool handle_is_ptr(TypeTableEntry *type_entry) {
     switch (type_entry->id) {
         case TypeTableEntryIdInvalid:
@@ -5204,6 +5368,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
              return type_entry->data.enumeration.gen_field_count != 0;
         case TypeTableEntryIdMaybe:
              return type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer;
+        case TypeTableEntryIdTypeDecl:
+             return handle_is_ptr(type_entry->data.type_decl.canonical_type);
     }
     zig_unreachable();
 }
@@ -5226,3 +5392,48 @@ void find_libc_path(CodeGen *g) {
     }
 }
 
+static uint32_t hash_ptr(void *ptr) {
+    uint64_t x = (uint64_t)(uintptr_t)(ptr);
+    uint32_t a = x >> 32;
+    uint32_t b = x & 0xffffffff;
+    return a ^ b;
+}
+
+uint32_t fn_type_id_hash(FnTypeId id) {
+    uint32_t result = 0;
+    result += id.is_extern ? 3349388391 : 0;
+    result += id.is_naked ? 608688877 : 0;
+    result += id.is_var_args ? 1931444534 : 0;
+    result += hash_ptr(id.return_type);
+    result += id.param_count;
+    for (int i = 0; i < id.param_count; i += 1) {
+        FnTypeParamInfo *info = &id.param_info[i];
+        result += info->is_noalias ? 892356923 : 0;
+        result += hash_ptr(info->type);
+    }
+    return result;
+}
+
+bool fn_type_id_eql(FnTypeId a, FnTypeId b) {
+    if (a.is_extern != b.is_extern ||
+        a.is_naked != b.is_naked ||
+        a.return_type != b.return_type ||
+        a.is_var_args != b.is_var_args ||
+        a.param_count != b.param_count)
+    {
+        return false;
+    }
+    for (int i = 0; i < a.param_count; i += 1) {
+        FnTypeParamInfo *a_param_info = &a.param_info[i];
+        FnTypeParamInfo *b_param_info = &b.param_info[i];
+
+        if (a_param_info->type != b_param_info->type) {
+            return false;
+        }
+
+        if (a_param_info->is_noalias != b_param_info->is_noalias) {
+            return false;
+        }
+    }
+    return true;
+}
src/analyze.hpp
@@ -22,7 +22,18 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node);
 bool is_node_void_expr(AstNode *node);
 TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits);
 TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits);
+TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type);
+TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type);
+TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *child_type);
+TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId fn_type_id);
+TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type);
+TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size);
+TypeTableEntry *get_partial_container_type(CodeGen *g, ImportTableEntry *import,
+        ContainerKind kind, AstNode *decl_node, const char *name);
+TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x);
 bool handle_is_ptr(TypeTableEntry *type_entry);
 void find_libc_path(CodeGen *g);
 
+TypeTableEntry *get_underlying_type(TypeTableEntry *type_entry);
+
 #endif
src/ast_render.cpp
@@ -119,6 +119,8 @@ static const char *node_type_str(NodeType node_type) {
             return "ReturnExpr";
         case NodeTypeVariableDeclaration:
             return "VariableDeclaration";
+        case NodeTypeTypeDecl:
+            return "TypeDecl";
         case NodeTypeErrorValueDecl:
             return "ErrorValueDecl";
         case NodeTypeNumberLiteral:
@@ -179,6 +181,8 @@ static const char *node_type_str(NodeType node_type) {
             return "ArrayType";
         case NodeTypeErrorType:
             return "ErrorType";
+        case NodeTypeTypeLiteral:
+            return "TypeLiteral";
     }
 }
 
@@ -260,6 +264,13 @@ void ast_print(FILE *f, AstNode *node, int indent) {
                     ast_print(f, node->data.variable_declaration.expr, indent + 2);
                 break;
             }
+        case NodeTypeTypeDecl:
+            {
+                Buf *name_buf = &node->data.type_decl.symbol;
+                fprintf(f, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf));
+                ast_print(f, node->data.type_decl.child_type, indent + 2);
+                break;
+            }
         case NodeTypeErrorValueDecl:
             {
                 Buf *name_buf = &node->data.error_value_decl.name;
@@ -478,6 +489,9 @@ void ast_print(FILE *f, AstNode *node, int indent) {
         case NodeTypeErrorType:
             fprintf(f, "%s\n", node_type_str(node->type));
             break;
+        case NodeTypeTypeLiteral:
+            fprintf(f, "%s\n", node_type_str(node->type));
+            break;
     }
 }
 
@@ -494,7 +508,14 @@ static void print_indent(AstRender *ar) {
 }
 
 static bool is_node_void(AstNode *node) {
-    return node->type == NodeTypeSymbol && buf_eql_str(&node->data.symbol_expr.symbol, "void");
+    if (node->type == NodeTypeSymbol) {
+        if (node->data.symbol_expr.override_type_entry) {
+            return node->data.symbol_expr.override_type_entry->id == TypeTableEntryIdVoid;
+        } else if (buf_eql_str(&node->data.symbol_expr.symbol, "void")) {
+            return true;
+        }
+    }
+    return false;
 }
 
 static bool is_printable(uint8_t c) {
@@ -515,6 +536,7 @@ static void render_node(AstRender *ar, AstNode *node) {
 
                 if (child->type == NodeTypeImport ||
                     child->type == NodeTypeVariableDeclaration ||
+                    child->type == NodeTypeTypeDecl ||
                     child->type == NodeTypeErrorValueDecl ||
                     child->type == NodeTypeFnProto)
                 {
@@ -588,6 +610,14 @@ static void render_node(AstRender *ar, AstNode *node) {
                 }
                 break;
             }
+        case NodeTypeTypeDecl:
+            {
+                const char *pub_str = visib_mod_string(node->data.type_decl.visib_mod);
+                const char *var_name = buf_ptr(&node->data.type_decl.symbol);
+                fprintf(ar->f, "%stype %s = ", pub_str, var_name);
+                render_node(ar, node->data.type_decl.child_type);
+                break;
+            }
         case NodeTypeErrorValueDecl:
             zig_panic("TODO");
         case NodeTypeBinOpExpr:
@@ -617,7 +647,14 @@ static void render_node(AstRender *ar, AstNode *node) {
                 break;
             }
         case NodeTypeSymbol:
-            fprintf(ar->f, "%s", buf_ptr(&node->data.symbol_expr.symbol));
+            {
+                TypeTableEntry *override_type = node->data.symbol_expr.override_type_entry;
+                if (override_type) {
+                    fprintf(ar->f, "%s", buf_ptr(&override_type->name));
+                } else {
+                    fprintf(ar->f, "%s", buf_ptr(&node->data.symbol_expr.symbol));
+                }
+            }
             break;
         case NodeTypePrefixOpExpr:
             {
@@ -719,7 +756,11 @@ static void render_node(AstRender *ar, AstNode *node) {
                 break;
             }
         case NodeTypeErrorType:
-            zig_panic("TODO");
+            fprintf(ar->f, "error");
+            break;
+        case NodeTypeTypeLiteral:
+            fprintf(ar->f, "type");
+            break;
     }
 }
 
src/codegen.cpp
@@ -13,11 +13,13 @@
 #include "error.hpp"
 #include "analyze.hpp"
 #include "errmsg.hpp"
+#include "parseh.hpp"
 #include "ast_render.hpp"
 
 #include <stdio.h>
 #include <errno.h>
 
+
 CodeGen *codegen_create(Buf *root_source_dir) {
     CodeGen *g = allocate<CodeGen>(1);
     g->link_table.init(32);
@@ -25,6 +27,7 @@ CodeGen *codegen_create(Buf *root_source_dir) {
     g->builtin_fn_table.init(32);
     g->primitive_type_table.init(32);
     g->unresolved_top_level_decls.init(32);
+    g->fn_type_table.init(32);
     g->build_type = CodeGenBuildTypeDebug;
     g->root_source_dir = root_source_dir;
     g->next_error_index = 1;
@@ -530,12 +533,12 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
         fn_type = get_expr_type(fn_ref_expr);
     }
 
-    TypeTableEntry *src_return_type = fn_type->data.fn.src_return_type;
+    TypeTableEntry *src_return_type = fn_type->data.fn.fn_type_id.return_type;
 
     int fn_call_param_count = node->data.fn_call_expr.params.length;
     bool first_arg_ret = handle_is_ptr(src_return_type);
     int actual_param_count = fn_call_param_count + (struct_type ? 1 : 0) + (first_arg_ret ? 1 : 0);
-    bool is_var_args = fn_type->data.fn.is_var_args;
+    bool is_var_args = fn_type->data.fn.fn_type_id.is_var_args;
 
     // don't really include void values
     LLVMValueRef *gen_param_values = allocate<LLVMValueRef>(actual_param_count);
@@ -1460,7 +1463,7 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
 }
 
 static LLVMValueRef gen_return(CodeGen *g, AstNode *source_node, LLVMValueRef value) {
-    TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.src_return_type;
+    TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
     if (handle_is_ptr(return_type)) {
         assert(g->cur_ret_ptr);
         gen_assign_raw(g, source_node, BinOpTypeAssign, g->cur_ret_ptr, value, return_type, return_type);
@@ -1503,7 +1506,7 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
                 LLVMBuildCondBr(g->builder, cond_val, continue_block, return_block);
 
                 LLVMPositionBuilderAtEnd(g->builder, return_block);
-                TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.src_return_type;
+                TypeTableEntry *return_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type;
                 if (return_type->id == TypeTableEntryIdPureError) {
                     gen_return(g, node, err_val);
                 } else if (return_type->id == TypeTableEntryIdErrorUnion) {
@@ -2296,6 +2299,9 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
         case NodeTypeCharLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeUndefinedLiteral:
+        case NodeTypeErrorType:
+        case NodeTypeTypeLiteral:
+        case NodeTypeArrayType:
             // caught by constant expression eval codegen
             zig_unreachable();
         case NodeTypeRoot:
@@ -2310,11 +2316,10 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
         case NodeTypeStructDecl:
         case NodeTypeStructField:
         case NodeTypeStructValueField:
-        case NodeTypeArrayType:
-        case NodeTypeErrorType:
         case NodeTypeSwitchProng:
         case NodeTypeSwitchRange:
         case NodeTypeErrorValueDecl:
+        case NodeTypeTypeDecl:
             zig_unreachable();
     }
     zig_unreachable();
@@ -2341,6 +2346,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE
     }
 
     switch (type_entry->id) {
+        case TypeTableEntryIdTypeDecl:
+            return gen_const_val(g, type_entry->data.type_decl.canonical_type, const_val);
         case TypeTableEntryIdInt:
             return LLVMConstInt(type_entry->type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false);
         case TypeTableEntryIdPureError:
@@ -2557,7 +2564,9 @@ static void do_code_gen(CodeGen *g) {
         assert(proto_node->type == NodeTypeFnProto);
         AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
 
-        if (handle_is_ptr(fn_table_entry->type_entry->data.fn.src_return_type)) {
+        TypeTableEntry *fn_type = fn_table_entry->type_entry;
+
+        if (handle_is_ptr(fn_type->data.fn.fn_type_id.return_type)) {
             LLVMValueRef first_arg = LLVMGetParam(fn_table_entry->fn_value, 0);
             LLVMAddAttribute(first_arg, LLVMStructRetAttribute);
         }
@@ -2567,7 +2576,9 @@ static void do_code_gen(CodeGen *g) {
             AstNode *param_node = fn_proto->params.at(param_decl_i);
             assert(param_node->type == NodeTypeParamDecl);
 
-            int gen_index = param_node->data.param_decl.gen_index;
+            FnGenParamInfo *info = &fn_type->data.fn.gen_param_info[param_decl_i];
+            int gen_index = info->gen_index;
+            bool is_byval = info->is_byval;
 
             if (gen_index < 0) {
                 continue;
@@ -2587,7 +2598,7 @@ static void do_code_gen(CodeGen *g) {
                 // when https://github.com/andrewrk/zig/issues/82 is fixed, add
                 // non null attribute here
             }
-            if (param_node->data.param_decl.is_byval) {
+            if (is_byval) {
                 LLVMAddAttribute(argument_val, LLVMByValAttribute);
             }
         }
@@ -2601,7 +2612,7 @@ static void do_code_gen(CodeGen *g) {
         AstNode *fn_def_node = fn_table_entry->fn_def_node;
         LLVMValueRef fn = fn_table_entry->fn_value;
         g->cur_fn = fn_table_entry;
-        if (handle_is_ptr(fn_table_entry->type_entry->data.fn.src_return_type)) {
+        if (handle_is_ptr(fn_table_entry->type_entry->data.fn.fn_type_id.return_type)) {
             g->cur_ret_ptr = LLVMGetParam(fn, 0);
         } else {
             g->cur_ret_ptr = nullptr;
@@ -2685,7 +2696,9 @@ static void do_code_gen(CodeGen *g) {
             AstNode *param_decl = fn_proto->params.at(param_i);
             assert(param_decl->type == NodeTypeParamDecl);
 
-            if (param_decl->data.param_decl.gen_index < 0) {
+            FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i];
+
+            if (info->gen_index < 0) {
                 continue;
             }
 
@@ -2724,17 +2737,6 @@ static const int int_sizes_in_bits[] = {
     64,
 };
 
-enum CIntType {
-    CIntTypeShort,
-    CIntTypeUShort,
-    CIntTypeInt,
-    CIntTypeUInt,
-    CIntTypeLong,
-    CIntTypeULong,
-    CIntTypeLongLong,
-    CIntTypeULongLong,
-};
-
 struct CIntTypeInfo {
     CIntType id;
     const char *name;
@@ -2840,6 +2842,8 @@ static void define_builtin_types(CodeGen *g) {
                 is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned());
         entry->data.integral.is_signed = is_signed;
         g->primitive_type_table.put(&entry->name, entry);
+
+        get_c_int_type_ptr(g, info->id)[0] = entry;
     }
 
     {
@@ -3093,6 +3097,42 @@ static void init(CodeGen *g, Buf *source_path) {
 
 }
 
+void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source_code) {
+    find_libc_path(g);
+    Buf *full_path = buf_alloc();
+    os_path_join(src_dirname, src_basename, full_path);
+
+    ImportTableEntry *import = allocate<ImportTableEntry>(1);
+    import->source_code = source_code;
+    import->path = full_path;
+    import->fn_table.init(32);
+    g->root_import = import;
+
+    init(g, full_path);
+
+    import->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname));
+
+    ZigList<ErrorMsg *> errors = {0};
+    int err = parse_h_buf(import, &errors, source_code, g, nullptr);
+    if (err) {
+        fprintf(stderr, "unable to parse .h file: %s\n", err_str(err));
+        exit(1);
+    }
+
+    if (errors.length > 0) {
+        for (int i = 0; i < errors.length; i += 1) {
+            ErrorMsg *err_msg = errors.at(i);
+            print_err_msg(err_msg, g->err_color);
+        }
+        exit(1);
+    }
+}
+
+void codegen_render_ast(CodeGen *g, FILE *f, int indent_size) {
+    ast_render(stdout, g->root_import->root, 4);
+}
+
+
 static int parse_version_string(Buf *buf, int *major, int *minor, int *patch) {
     char *dot1 = strstr(buf_ptr(buf), ".");
     if (!dot1)
@@ -3156,7 +3196,6 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path,
     import_entry->line_offsets = tokenization.line_offsets;
     import_entry->path = full_path;
     import_entry->fn_table.init(32);
-    import_entry->fn_type_table.init(32);
 
     import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color,
             &g->next_node_index);
src/codegen.hpp
@@ -11,6 +11,8 @@
 #include "parser.hpp"
 #include "errmsg.hpp"
 
+#include <stdio.h>
+
 CodeGen *codegen_create(Buf *root_source_dir);
 
 void codegen_set_clang_argv(CodeGen *codegen, const char **args, int len);
@@ -27,4 +29,7 @@ void codegen_add_root_code(CodeGen *g, Buf *source_dir, Buf *source_basename, Bu
 
 void codegen_link(CodeGen *g, const char *out_file);
 
+void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source_code);
+void codegen_render_ast(CodeGen *g, FILE *f, int indent_size);
+
 #endif
src/main.cpp
@@ -10,8 +10,6 @@
 #include "codegen.hpp"
 #include "os.hpp"
 #include "error.hpp"
-#include "parseh.hpp"
-#include "ast_render.hpp"
 
 #include <stdio.h>
 
@@ -38,40 +36,41 @@ static int usage(const char *arg0) {
     return EXIT_FAILURE;
 }
 
-static int version(const char *arg0, int argc, char **argv) {
-    printf("%s\n", ZIG_VERSION_STRING);
-    return EXIT_SUCCESS;
-}
-
-struct Build {
-    const char *in_file;
-    const char *out_file;
-    bool release;
-    bool strip;
-    bool is_static;
-    OutType out_type;
-    const char *out_name;
-    bool verbose;
-    ErrColor color;
-    const char *libc_path;
-    ZigList<const char *> clang_argv;
+enum Cmd {
+    CmdInvalid,
+    CmdBuild,
+    CmdVersion,
+    CmdParseH,
 };
 
-static int build(const char *arg0, int argc, char **argv) {
+int main(int argc, char **argv) {
+    char *arg0 = argv[0];
+    Cmd cmd = CmdInvalid;
+    const char *in_file = nullptr;
+    const char *out_file = nullptr;
+    bool release = false;
+    bool strip = false;
+    bool is_static = false;
+    OutType out_type = OutTypeUnknown;
+    const char *out_name = nullptr;
+    bool verbose = false;
+    ErrColor color = ErrColorAuto;
+    const char *libc_path = nullptr;
+    ZigList<const char *> clang_argv = {0};
     int err;
-    Build b = {0};
 
-    for (int i = 0; i < argc; i += 1) {
+    for (int i = 1; i < argc; i += 1) {
         char *arg = argv[i];
+
         if (arg[0] == '-') {
             if (strcmp(arg, "--release") == 0) {
-                b.release = true;
+                release = true;
             } else if (strcmp(arg, "--strip") == 0) {
-                b.strip = true;
+                strip = true;
             } else if (strcmp(arg, "--static") == 0) {
-                b.is_static = true;
+                is_static = true;
             } else if (strcmp(arg, "--verbose") == 0) {
-                b.verbose = true;
+                verbose = true;
             } else if (i + 1 >= argc) {
                 return usage(arg0);
             } else {
@@ -79,190 +78,128 @@ static int build(const char *arg0, int argc, char **argv) {
                 if (i >= argc) {
                     return usage(arg0);
                 } else if (strcmp(arg, "--output") == 0) {
-                    b.out_file = argv[i];
+                    out_file = argv[i];
                 } else if (strcmp(arg, "--export") == 0) {
                     if (strcmp(argv[i], "exe") == 0) {
-                        b.out_type = OutTypeExe;
+                        out_type = OutTypeExe;
                     } else if (strcmp(argv[i], "lib") == 0) {
-                        b.out_type = OutTypeLib;
+                        out_type = OutTypeLib;
                     } else if (strcmp(argv[i], "obj") == 0) {
-                        b.out_type = OutTypeObj;
+                        out_type = OutTypeObj;
                     } else {
                         return usage(arg0);
                     }
                 } else if (strcmp(arg, "--color") == 0) {
                     if (strcmp(argv[i], "auto") == 0) {
-                        b.color = ErrColorAuto;
+                        color = ErrColorAuto;
                     } else if (strcmp(argv[i], "on") == 0) {
-                        b.color = ErrColorOn;
+                        color = ErrColorOn;
                     } else if (strcmp(argv[i], "off") == 0) {
-                        b.color = ErrColorOff;
+                        color = ErrColorOff;
                     } else {
                         return usage(arg0);
                     }
                 } else if (strcmp(arg, "--name") == 0) {
-                    b.out_name = argv[i];
+                    out_name = argv[i];
                 } else if (strcmp(arg, "--libc-path") == 0) {
-                    b.libc_path = argv[i];
+                    libc_path = argv[i];
                 } else if (strcmp(arg, "-isystem") == 0) {
-                    b.clang_argv.append("-isystem");
-                    b.clang_argv.append(argv[i]);
+                    clang_argv.append("-isystem");
+                    clang_argv.append(argv[i]);
                 } else if (strcmp(arg, "-dirafter") == 0) {
-                    b.clang_argv.append("-dirafter");
-                    b.clang_argv.append(argv[i]);
+                    clang_argv.append("-dirafter");
+                    clang_argv.append(argv[i]);
                 } else {
                     return usage(arg0);
                 }
             }
-        } else if (!b.in_file) {
-            b.in_file = arg;
-        } else {
-            return usage(arg0);
-        }
-    }
-
-    if (!b.in_file)
-        return usage(arg0);
-
-    Buf in_file_buf = BUF_INIT;
-    buf_init_from_str(&in_file_buf, b.in_file);
-
-    Buf root_source_dir = BUF_INIT;
-    Buf root_source_code = BUF_INIT;
-    Buf root_source_name = BUF_INIT;
-    if (buf_eql_str(&in_file_buf, "-")) {
-        os_get_cwd(&root_source_dir);
-        if ((err = os_fetch_file(stdin, &root_source_code))) {
-            fprintf(stderr, "unable to read stdin: %s\n", err_str(err));
-            return 1;
-        }
-        buf_init_from_str(&root_source_name, "");
-    } else {
-        os_path_split(&in_file_buf, &root_source_dir, &root_source_name);
-        if ((err = os_fetch_file_path(buf_create_from_str(b.in_file), &root_source_code))) {
-            fprintf(stderr, "unable to open '%s': %s\n", b.in_file, err_str(err));
-            return 1;
-        }
-    }
-
-    CodeGen *g = codegen_create(&root_source_dir);
-    codegen_set_build_type(g, b.release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug);
-    codegen_set_clang_argv(g, b.clang_argv.items, b.clang_argv.length);
-    codegen_set_strip(g, b.strip);
-    codegen_set_is_static(g, b.is_static);
-    if (b.out_type != OutTypeUnknown)
-        codegen_set_out_type(g, b.out_type);
-    if (b.out_name)
-        codegen_set_out_name(g, buf_create_from_str(b.out_name));
-    if (b.libc_path)
-        codegen_set_libc_path(g, buf_create_from_str(b.libc_path));
-    codegen_set_verbose(g, b.verbose);
-    codegen_set_errmsg_color(g, b.color);
-    codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code);
-    codegen_link(g, b.out_file);
-
-    return 0;
-}
-
-static int parseh(const char *arg0, int argc, char **argv) {
-    char *in_file = nullptr;
-    ZigList<const char *> clang_argv = {0};
-    ErrColor color = ErrColorAuto;
-    bool warnings_on = false;
-    for (int i = 0; i < argc; i += 1) {
-        char *arg = argv[i];
-        if (arg[0] == '-') {
-            if (arg[1] == 'I') {
-                clang_argv.append(arg);
-            } else if (strcmp(arg, "-isystem") == 0) {
-                if (i + 1 >= argc) {
-                    return usage(arg0);
-                }
-                i += 1;
-                clang_argv.append("-isystem");
-                clang_argv.append(argv[i]);
-            } else if (strcmp(arg, "--color") == 0) {
-                if (i + 1 >= argc) {
-                    return usage(arg0);
-                }
-                i += 1;
-                if (strcmp(argv[i], "auto") == 0) {
-                    color = ErrColorAuto;
-                } else if (strcmp(argv[i], "on") == 0) {
-                    color = ErrColorOn;
-                } else if (strcmp(argv[i], "off") == 0) {
-                    color = ErrColorOff;
-                } else {
-                    return usage(arg0);
-                }
-            } else if (strcmp(arg, "--c-import-warnings") == 0) {
-                warnings_on = true;
+        } else if (cmd == CmdInvalid) {
+            if (strcmp(arg, "build") == 0) {
+                cmd = CmdBuild;
+            } else if (strcmp(arg, "version") == 0) {
+                cmd = CmdVersion;
+            } else if (strcmp(arg, "parseh") == 0) {
+                cmd = CmdParseH;
             } else {
-                fprintf(stderr, "unrecognized argument: %s", arg);
+                fprintf(stderr, "Unrecognized command: %s\n", arg);
                 return usage(arg0);
             }
-        } else if (!in_file) {
-            in_file = arg;
         } else {
-            return usage(arg0);
-        }
-    }
-    if (!in_file) {
-        fprintf(stderr, "missing target argument");
-        return usage(arg0);
-    }
-
-    clang_argv.append(in_file);
-
-    Buf *libc_include_path = buf_alloc();
-    os_path_join(buf_create_from_str(ZIG_LIBC_DIR), buf_create_from_str("include"), libc_include_path);
-    clang_argv.append("-isystem");
-    clang_argv.append(buf_ptr(libc_include_path));
-
-    ImportTableEntry import = {0};
-    ZigList<ErrorMsg *> errors = {0};
-    uint32_t next_node_index = 0;
-    int err = parse_h_file(&import, &errors, &clang_argv, warnings_on, &next_node_index);
-
-    if (err) {
-        fprintf(stderr, "unable to parse .h file: %s\n", err_str(err));
-        return EXIT_FAILURE;
-    }
-
-    if (errors.length > 0) {
-        for (int i = 0; i < errors.length; i += 1) {
-            ErrorMsg *err_msg = errors.at(i);
-            print_err_msg(err_msg, color);
+            switch (cmd) {
+                case CmdBuild:
+                case CmdParseH:
+                    if (!in_file) {
+                        in_file = arg;
+                    } else {
+                        return usage(arg0);
+                    }
+                    break;
+                case CmdVersion:
+                    return usage(arg0);
+                case CmdInvalid:
+                    zig_unreachable();
+            }
         }
-        return EXIT_FAILURE;
     }
 
-    ast_render(stdout, import.root, 4);
+    switch (cmd) {
+    case CmdBuild:
+    case CmdParseH:
+        {
+            if (!in_file)
+                return usage(arg0);
 
-    return 0;
-}
+            Buf in_file_buf = BUF_INIT;
+            buf_init_from_str(&in_file_buf, in_file);
+
+            Buf root_source_dir = BUF_INIT;
+            Buf root_source_code = BUF_INIT;
+            Buf root_source_name = BUF_INIT;
+            if (buf_eql_str(&in_file_buf, "-")) {
+                os_get_cwd(&root_source_dir);
+                if ((err = os_fetch_file(stdin, &root_source_code))) {
+                    fprintf(stderr, "unable to read stdin: %s\n", err_str(err));
+                    return 1;
+                }
+                buf_init_from_str(&root_source_name, "");
+            } else {
+                os_path_split(&in_file_buf, &root_source_dir, &root_source_name);
+                if ((err = os_fetch_file_path(buf_create_from_str(in_file), &root_source_code))) {
+                    fprintf(stderr, "unable to open '%s': %s\n", in_file, err_str(err));
+                    return 1;
+                }
+            }
 
-int main(int argc, char **argv) {
-    char *arg0 = argv[0];
-    int (*cmd)(const char *, int, char **) = nullptr;
-    for (int i = 1; i < argc; i += 1) {
-        char *arg = argv[i];
-        if (arg[0] == '-' && arg[1] == '-') {
-            return usage(arg0);
-        } else {
-            if (strcmp(arg, "build") == 0) {
-                cmd = build;
-            } else if (strcmp(arg, "version") == 0) {
-                cmd = version;
-            } else if (strcmp(arg, "parseh") == 0) {
-                cmd = parseh;
+            CodeGen *g = codegen_create(&root_source_dir);
+            codegen_set_build_type(g, release ? CodeGenBuildTypeRelease : CodeGenBuildTypeDebug);
+            codegen_set_clang_argv(g, clang_argv.items, clang_argv.length);
+            codegen_set_strip(g, strip);
+            codegen_set_is_static(g, is_static);
+            if (out_type != OutTypeUnknown)
+                codegen_set_out_type(g, out_type);
+            if (out_name)
+                codegen_set_out_name(g, buf_create_from_str(out_name));
+            if (libc_path)
+                codegen_set_libc_path(g, buf_create_from_str(libc_path));
+            codegen_set_verbose(g, verbose);
+            codegen_set_errmsg_color(g, color);
+
+            if (cmd == CmdBuild) {
+                codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code);
+                codegen_link(g, out_file);
+                return EXIT_SUCCESS;
+            } else if (cmd == CmdParseH) {
+                codegen_parseh(g, &root_source_dir, &root_source_name, &root_source_code);
+                codegen_render_ast(g, stdout, 4);
+                return EXIT_SUCCESS;
             } else {
-                fprintf(stderr, "Unrecognized command: %s\n", arg);
-                return usage(arg0);
+                zig_unreachable();
             }
-            return cmd(arg0, argc - i - 1, &argv[i + 1]);
         }
+    case CmdVersion:
+        printf("%s\n", ZIG_VERSION_STRING);
+        return EXIT_SUCCESS;
+    case CmdInvalid:
+        return usage(arg0);
     }
-
-    return usage(arg0);
 }
src/parseh.cpp
@@ -12,6 +12,7 @@
 #include "parser.hpp"
 #include "all_types.hpp"
 #include "tokenizer.hpp"
+#include "analyze.hpp"
 
 #include <clang/Frontend/ASTUnit.h>
 #include <clang/Frontend/CompilerInstance.h>
@@ -30,22 +31,27 @@ struct Context {
     ZigList<ErrorMsg *> *errors;
     bool warnings_on;
     VisibMod visib_mod;
-    bool have_c_void_decl_node;
+    TypeTableEntry *c_void_type;
     AstNode *root;
-    HashMap<Buf *, bool, buf_hash, buf_eql_buf> root_name_table;
-    HashMap<Buf *, bool, buf_hash, buf_eql_buf> struct_type_table;
-    HashMap<Buf *, bool, buf_hash, buf_eql_buf> enum_type_table;
+    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> global_type_table;
+    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> global_value_table;
+    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> struct_type_table;
+    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> enum_type_table;
     HashMap<Buf *, bool, buf_hash, buf_eql_buf> fn_table;
     HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> macro_table;
     SourceManager *source_manager;
     ZigList<AstNode *> aliases;
     ZigList<MacroSymbol> macro_symbols;
-    uint32_t *next_node_index;
+    AstNode *source_node;
+
+    CodeGen *codegen;
 };
 
-static AstNode *make_qual_type_node(Context *c, QualType qt, const Decl *decl);
-static AstNode *make_qual_type_node_with_table(Context *c, QualType qt, const Decl *decl,
-    HashMap<Buf *, bool, buf_hash, buf_eql_buf> *type_table);
+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);
+
 
 __attribute__ ((format (printf, 3, 4)))
 static void emit_warning(Context *c, const Decl *decl, const char *format, ...) {
@@ -77,8 +83,8 @@ static AstNode *create_node(Context *c, NodeType type) {
     AstNode *node = allocate<AstNode>(1);
     node->type = type;
     node->owner = c->import;
-    node->create_index = *c->next_node_index;
-    *c->next_node_index += 1;
+    node->create_index = c->codegen->next_node_index;
+    c->codegen->next_node_index += 1;
     return node;
 }
 
@@ -178,61 +184,51 @@ static AstNode *create_num_lit_signed(Context *c, int64_t x) {
     return create_prefix_node(c, PrefixOpNegation, num_lit_node);
 }
 
-static AstNode *create_array_type_node(Context *c, AstNode *child_type_node, uint64_t size, bool is_const) {
-    AstNode *node = create_node(c, NodeTypeArrayType);
-    node->data.array_type.size = create_num_lit_unsigned(c, size);
-    node->data.array_type.child_type = child_type_node;
-    node->data.array_type.is_const = is_const;
+static AstNode *create_type_decl_node(Context *c, const char *name, AstNode *child_type_node) {
+    AstNode *node = create_node(c, NodeTypeTypeDecl);
+    buf_init_from_str(&node->data.type_decl.symbol, name);
+    node->data.type_decl.visib_mod = c->visib_mod;
+    node->data.type_decl.directives = create_empty_directives(c);
+    node->data.type_decl.child_type = child_type_node;
 
     normalize_parent_ptrs(node);
     return node;
 }
 
+static AstNode *make_type_node(Context *c, TypeTableEntry *type_entry) {
+    AstNode *node = create_node(c, NodeTypeSymbol);
+    node->data.symbol_expr.override_type_entry = type_entry;
+    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 *add_typedef_node(Context *c, TypeTableEntry *type_decl) {
+    assert(type_decl);
 
-static AstNode *add_typedef_node(Context *c, Buf *new_name, AstNode *target_node) {
-    if (!target_node) {
-        return nullptr;
-    }
-    AstNode *node = create_var_decl_node(c, buf_ptr(new_name), target_node);
+    AstNode *node = create_type_decl_node(c, buf_ptr(&type_decl->name),
+            make_type_node(c, type_decl->data.type_decl.child_type));
+    node->data.type_decl.override_type = type_decl;
 
-    c->root_name_table.put(new_name, true);
+    c->global_type_table.put(&type_decl->name, type_decl);
     c->root->data.root.top_level_decls.append(node);
     return node;
 }
 
-static AstNode *convert_to_c_void(Context *c, AstNode *type_node) {
-    if (type_node->type == NodeTypeSymbol &&
-        buf_eql_str(&type_node->data.symbol_expr.symbol, "void"))
-    {
-        if (!c->have_c_void_decl_node) {
-            add_typedef_node(c, buf_create_from_str("c_void"), create_symbol_node(c, "u8"));
-            c->have_c_void_decl_node = true;
-        }
-        return create_symbol_node(c, "c_void");
-    } else {
-        return type_node;
+static TypeTableEntry *get_c_void_type(Context *c) {
+    if (!c->c_void_type) {
+        c->c_void_type = get_typedecl_type(c->codegen, "c_void", c->codegen->builtin_types.entry_u8);
+        add_typedef_node(c, c->c_void_type);
     }
-}
-
-static AstNode *pointer_to_type(Context *c, AstNode *type_node, bool is_const) {
-    assert(type_node);
-    PrefixOp op = is_const ? PrefixOpConstAddressOf : PrefixOpAddressOf;
-    AstNode *child_node = create_prefix_node(c, op, convert_to_c_void(c, type_node));
-    return create_prefix_node(c, PrefixOpMaybe, child_node);
-}
 
-static bool type_is_int(AstNode *type_node) {
-    // TODO recurse through the type table
-    return true;
+    return c->c_void_type;
 }
 
-static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
-    HashMap<Buf *, bool, buf_hash, buf_eql_buf> *type_table)
+static TypeTableEntry *resolve_type_with_table(Context *c, const Type *ty, const Decl *decl,
+    HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> *type_table)
 {
     switch (ty->getTypeClass()) {
         case Type::Builtin:
@@ -240,35 +236,35 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
                 const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
                 switch (builtin_ty->getKind()) {
                     case BuiltinType::Void:
-                        return create_symbol_node(c, "void");
+                        return c->codegen->builtin_types.entry_void;
                     case BuiltinType::Bool:
-                        return create_symbol_node(c, "bool");
+                        return c->codegen->builtin_types.entry_bool;
                     case BuiltinType::Char_U:
                     case BuiltinType::UChar:
                     case BuiltinType::Char_S:
-                        return create_symbol_node(c, "u8");
+                        return c->codegen->builtin_types.entry_u8;
                     case BuiltinType::SChar:
-                        return create_symbol_node(c, "i8");
+                        return c->codegen->builtin_types.entry_i8;
                     case BuiltinType::UShort:
-                        return create_symbol_node(c, "c_ushort");
+                        return get_c_int_type(c->codegen, CIntTypeUShort);
                     case BuiltinType::UInt:
-                        return create_symbol_node(c, "c_uint");
+                        return get_c_int_type(c->codegen, CIntTypeUInt);
                     case BuiltinType::ULong:
-                        return create_symbol_node(c, "c_ulong");
+                        return get_c_int_type(c->codegen, CIntTypeULong);
                     case BuiltinType::ULongLong:
-                        return create_symbol_node(c, "c_ulonglong");
+                        return get_c_int_type(c->codegen, CIntTypeULongLong);
                     case BuiltinType::Short:
-                        return create_symbol_node(c, "c_short");
+                        return get_c_int_type(c->codegen, CIntTypeShort);
                     case BuiltinType::Int:
-                        return create_symbol_node(c, "c_int");
+                        return get_c_int_type(c->codegen, CIntTypeInt);
                     case BuiltinType::Long:
-                        return create_symbol_node(c, "c_long");
+                        return get_c_int_type(c->codegen, CIntTypeLong);
                     case BuiltinType::LongLong:
-                        return create_symbol_node(c, "c_longlong");
+                        return get_c_int_type(c->codegen, CIntTypeLongLong);
                     case BuiltinType::Float:
-                        return create_symbol_node(c, "f32");
+                        return c->codegen->builtin_types.entry_f32;
                     case BuiltinType::Double:
-                        return create_symbol_node(c, "f64");
+                        return c->codegen->builtin_types.entry_f64;
                     case BuiltinType::LongDouble:
                     case BuiltinType::WChar_U:
                     case BuiltinType::Char16:
@@ -297,7 +293,7 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
                     case BuiltinType::BuiltinFn:
                     case BuiltinType::ARCUnbridgedCast:
                         emit_warning(c, decl, "missed a builtin type");
-                        return nullptr;
+                        return c->codegen->builtin_types.entry_invalid;
                 }
                 break;
             }
@@ -305,17 +301,26 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
             {
                 const PointerType *pointer_ty = static_cast<const PointerType*>(ty);
                 QualType child_qt = pointer_ty->getPointeeType();
-                AstNode *type_node = make_qual_type_node(c, child_qt, decl);
-                if (!type_node) {
-                    return nullptr;
+                TypeTableEntry *child_type = resolve_qual_type(c, child_qt, decl);
+                if (get_underlying_type(child_type)->id == TypeTableEntryIdInvalid) {
+                    emit_warning(c, decl, "pointer to unresolved type");
+                    return c->codegen->builtin_types.entry_invalid;
                 }
+
                 if (child_qt.getTypePtr()->getTypeClass() == Type::Paren) {
                     const ParenType *paren_type = static_cast<const ParenType *>(child_qt.getTypePtr());
                     if (paren_type->getInnerType()->getTypeClass() == Type::FunctionProto) {
-                        return create_prefix_node(c, PrefixOpMaybe, type_node);
+                        return get_maybe_type(c->codegen, child_type);
                     }
                 }
-                return pointer_to_type(c, type_node, child_qt.isConstQualified());
+                bool is_const = child_qt.isConstQualified();
+
+                if (child_type->id == TypeTableEntryIdVoid) {
+                    child_type = get_c_void_type(c);
+                }
+
+                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:
             {
@@ -323,32 +328,28 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
                 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 create_symbol_node(c, "u8");
+                    return c->codegen->builtin_types.entry_u8;
                 } else if (buf_eql_str(type_name, "int8_t")) {
-                    return create_symbol_node(c, "i8");
+                    return c->codegen->builtin_types.entry_i8;
                 } else if (buf_eql_str(type_name, "uint16_t")) {
-                    return create_symbol_node(c, "u16");
+                    return c->codegen->builtin_types.entry_u16;
                 } else if (buf_eql_str(type_name, "int16_t")) {
-                    return create_symbol_node(c, "i16");
+                    return c->codegen->builtin_types.entry_i16;
                 } else if (buf_eql_str(type_name, "uint32_t")) {
-                    return create_symbol_node(c, "u32");
+                    return c->codegen->builtin_types.entry_u32;
                 } else if (buf_eql_str(type_name, "int32_t")) {
-                    return create_symbol_node(c, "i32");
+                    return c->codegen->builtin_types.entry_i32;
                 } else if (buf_eql_str(type_name, "uint64_t")) {
-                    return create_symbol_node(c, "u64");
+                    return c->codegen->builtin_types.entry_u64;
                 } else if (buf_eql_str(type_name, "int64_t")) {
-                    return create_symbol_node(c, "i64");
+                    return c->codegen->builtin_types.entry_i64;
                 } else if (buf_eql_str(type_name, "intptr_t")) {
-                    return create_symbol_node(c, "isize");
+                    return c->codegen->builtin_types.entry_isize;
                 } else if (buf_eql_str(type_name, "uintptr_t")) {
-                    return create_symbol_node(c, "usize");
+                    return c->codegen->builtin_types.entry_usize;
                 } else {
                     auto entry = type_table->maybe_get(type_name);
-                    if (entry) {
-                        return create_symbol_node(c, buf_ptr(type_name));
-                    } else {
-                        return nullptr;
-                    }
+                    return entry ? entry->value : c->codegen->builtin_types.entry_invalid;
                 }
             }
         case Type::Elaborated:
@@ -356,10 +357,10 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
                 const ElaboratedType *elaborated_ty = static_cast<const ElaboratedType*>(ty);
                 switch (elaborated_ty->getKeyword()) {
                     case ETK_Struct:
-                        return make_qual_type_node_with_table(c, elaborated_ty->getNamedType(),
+                        return resolve_qual_type_with_table(c, elaborated_ty->getNamedType(),
                                 decl, &c->struct_type_table);
                     case ETK_Enum:
-                        return make_qual_type_node_with_table(c, elaborated_ty->getNamedType(),
+                        return resolve_qual_type_with_table(c, elaborated_ty->getNamedType(),
                                 decl, &c->enum_type_table);
                     case ETK_Interface:
                     case ETK_Union:
@@ -367,35 +368,63 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
                     case ETK_Typename:
                     case ETK_None:
                         emit_warning(c, decl, "unsupported elaborated type");
-                        return nullptr;
+                        return c->codegen->builtin_types.entry_invalid;
                 }
             }
         case Type::FunctionProto:
             {
                 const FunctionProtoType *fn_proto_ty = static_cast<const FunctionProtoType*>(ty);
-                AstNode *node = create_node(c, NodeTypeFnProto);
-                buf_resize(&node->data.fn_proto.name, 0);
-                node->data.fn_proto.is_extern = true;
-                node->data.fn_proto.is_var_args = fn_proto_ty->isVariadic();
-                node->data.fn_proto.return_type = make_qual_type_node(c, fn_proto_ty->getReturnType(), decl);
-
-                if (!node->data.fn_proto.return_type) {
-                    return nullptr;
+
+                switch (fn_proto_ty->getCallConv()) {
+                    case CC_C:           // __attribute__((cdecl))
+                        break;
+                    case CC_X86StdCall:  // __attribute__((stdcall))
+                    case CC_X86FastCall: // __attribute__((fastcall))
+                    case CC_X86ThisCall: // __attribute__((thiscall))
+                    case CC_X86VectorCall: // __attribute__((vectorcall))
+                    case CC_X86Pascal:   // __attribute__((pascal))
+                    case CC_X86_64Win64: // __attribute__((ms_abi))
+                    case CC_X86_64SysV:  // __attribute__((sysv_abi))
+                    case CC_AAPCS:       // __attribute__((pcs("aapcs")))
+                    case CC_AAPCS_VFP:   // __attribute__((pcs("aapcs-vfp")))
+                    case CC_IntelOclBicc: // __attribute__((intel_ocl_bicc))
+                    case CC_SpirFunction: // default for OpenCL functions on SPIR target
+                    case CC_SpirKernel:    // inferred for OpenCL kernels on SPIR target
+                        emit_warning(c, decl, "function type has non C calling convention");
+                        return c->codegen->builtin_types.entry_invalid;
+                }
+
+                FnTypeId fn_type_id;
+                fn_type_id.is_naked = false;
+                fn_type_id.is_extern = true;
+                fn_type_id.is_var_args = fn_proto_ty->isVariadic();
+                fn_type_id.param_count = fn_proto_ty->getNumParams();
+
+
+                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 (fn_type_id.return_type->id == TypeTableEntryIdInvalid) {
+                        return c->codegen->builtin_types.entry_invalid;
+                    }
                 }
 
-                int arg_count = fn_proto_ty->getNumParams();
-                for (int i = 0; i < arg_count; i += 1) {
+                fn_type_id.param_info = allocate<FnTypeParamInfo>(fn_type_id.param_count);
+                for (int i = 0; i < fn_type_id.param_count; i += 1) {
                     QualType qt = fn_proto_ty->getParamType(i);
-                    bool is_noalias = qt.isRestrictQualified();
-                    AstNode *type_node = make_qual_type_node(c, qt, decl);
-                    if (!type_node) {
-                        return nullptr;
+                    TypeTableEntry *param_type = resolve_qual_type(c, qt, decl);
+
+                    if (param_type->id == TypeTableEntryIdInvalid) {
+                        return c->codegen->builtin_types.entry_invalid;
                     }
-                    node->data.fn_proto.params.append(create_param_decl_node(c, "", type_node, is_noalias));
+
+                    FnTypeParamInfo *param_info = &fn_type_id.param_info[i];
+                    param_info->type = param_type;
+                    param_info->is_noalias = qt.isRestrictQualified();
                 }
 
-                normalize_parent_ptrs(node);
-                return node;
+                return get_fn_type(c->codegen, fn_type_id);
             }
         case Type::Record:
             {
@@ -403,20 +432,15 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
                 Buf *record_name = buf_create_from_str(decl_name(record_ty->getDecl()));
                 if (buf_len(record_name) == 0) {
                     emit_warning(c, decl, "unhandled anonymous struct");
-                    return nullptr;
-                } else if (type_table->maybe_get(record_name)) {
-                    const char *prefix_str;
-                    if (type_table == &c->enum_type_table) {
-                        prefix_str = "enum_";
-                    } else if (type_table == &c->struct_type_table) {
-                        prefix_str = "struct_";
-                    } else {
-                        prefix_str = "";
-                    }
-                    return create_symbol_node(c, buf_ptr(buf_sprintf("%s%s", prefix_str, buf_ptr(record_name))));
-                } else {
-                    return nullptr;
+                    return c->codegen->builtin_types.entry_invalid;
                 }
+
+                auto entry = type_table->maybe_get(record_name);
+                if (!entry) {
+                    return c->codegen->builtin_types.entry_invalid;
+                }
+
+                return entry->value;
             }
         case Type::Enum:
             {
@@ -424,32 +448,32 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
                 Buf *record_name = buf_create_from_str(decl_name(enum_ty->getDecl()));
                 if (buf_len(record_name) == 0) {
                     emit_warning(c, decl, "unhandled anonymous enum");
-                    return nullptr;
-                } else if (type_table->maybe_get(record_name)) {
-                    const char *prefix_str;
-                    if (type_table == &c->enum_type_table) {
-                        prefix_str = "enum_";
-                    } else if (type_table == &c->struct_type_table) {
-                        prefix_str = "struct_";
-                    } else {
-                        prefix_str = "";
-                    }
-                    return create_symbol_node(c, buf_ptr(buf_sprintf("%s%s", prefix_str, buf_ptr(record_name))));
-                } else {
-                    return nullptr;
+                    return c->codegen->builtin_types.entry_invalid;
+                }
+
+                auto entry = type_table->maybe_get(record_name);
+                if (!entry) {
+                    return c->codegen->builtin_types.entry_invalid;
                 }
+
+                return entry->value;
             }
         case Type::ConstantArray:
             {
                 const ConstantArrayType *const_arr_ty = static_cast<const ConstantArrayType *>(ty);
-                AstNode *child_type_node = make_qual_type_node(c, const_arr_ty->getElementType(), decl);
+                TypeTableEntry *child_type = resolve_qual_type(c, const_arr_ty->getElementType(), decl);
                 uint64_t size = const_arr_ty->getSize().getLimitedValue();
-                return create_array_type_node(c, child_type_node, size, false);
+                return get_array_type(c->codegen, child_type, size);
             }
         case Type::Paren:
             {
                 const ParenType *paren_ty = static_cast<const ParenType *>(ty);
-                return make_qual_type_node(c, paren_ty->getInnerType(), decl);
+                return resolve_qual_type(c, paren_ty->getInnerType(), decl);
+            }
+        case Type::Decayed:
+            {
+                const DecayedType *decayed_ty = static_cast<const DecayedType *>(ty);
+                return resolve_qual_type(c, decayed_ty->getOriginalType(), decl);
             }
         case Type::BlockPointer:
         case Type::LValueReference:
@@ -464,7 +488,6 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
         case Type::FunctionNoProto:
         case Type::UnresolvedUsing:
         case Type::Adjusted:
-        case Type::Decayed:
         case Type::TypeOfExpr:
         case Type::TypeOf:
         case Type::Decltype:
@@ -485,68 +508,68 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
         case Type::ObjCObjectPointer:
         case Type::Atomic:
             emit_warning(c, decl, "missed a '%s' type", ty->getTypeClassName());
-            return nullptr;
+            return c->codegen->builtin_types.entry_invalid;
     }
 }
 
-static AstNode *make_qual_type_node_with_table(Context *c, QualType qt, const Decl *decl,
-    HashMap<Buf *, bool, buf_hash, buf_eql_buf> *type_table)
+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 make_type_node(c, qt.getTypePtr(), decl, type_table);
+    return resolve_type_with_table(c, qt.getTypePtr(), decl, type_table);
 }
 
-static AstNode *make_qual_type_node(Context *c, QualType qt, const Decl *decl) {
-    return make_qual_type_node_with_table(c, qt, decl, &c->root_name_table);
+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 void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
-    AstNode *node = create_node(c, NodeTypeFnProto);
-    buf_init_from_str(&node->data.fn_proto.name, decl_name(fn_decl));
+    Buf fn_name = BUF_INIT;
+    buf_init_from_str(&fn_name, decl_name(fn_decl));
 
-    auto fn_entry = c->fn_table.maybe_get(&node->data.fn_proto.name);
-    if (fn_entry) {
+    if (c->fn_table.maybe_get(&fn_name)) {
         // we already saw this function
         return;
     }
 
-    node->data.fn_proto.is_extern = true;
+    TypeTableEntry *fn_type = resolve_qual_type(c, fn_decl->getType(), fn_decl);
+
+    if (fn_type->id == TypeTableEntryIdInvalid) {
+        emit_warning(c, fn_decl, "ignoring function '%s' - unable to resolve type", buf_ptr(&fn_name));
+        return;
+    }
+    assert(fn_type->id == TypeTableEntryIdFn);
+
+
+    AstNode *node = create_node(c, NodeTypeFnProto);
+    buf_init_from_buf(&node->data.fn_proto.name, &fn_name);
+
+    node->data.fn_proto.is_extern = fn_type->data.fn.fn_type_id.is_extern;
     node->data.fn_proto.visib_mod = c->visib_mod;
     node->data.fn_proto.directives = create_empty_directives(c);
-    node->data.fn_proto.is_var_args = fn_decl->isVariadic();
+    node->data.fn_proto.is_var_args = fn_type->data.fn.fn_type_id.is_var_args;
+    node->data.fn_proto.return_type = make_type_node(c, fn_type->data.fn.fn_type_id.return_type);
+
+    assert(!fn_type->data.fn.fn_type_id.is_naked);
 
-    int arg_count = fn_decl->getNumParams();
+    int arg_count = fn_type->data.fn.fn_type_id.param_count;
+    Buf name_buf = BUF_INIT;
     for (int i = 0; i < arg_count; i += 1) {
+        FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[i];
+        AstNode *type_node = make_type_node(c, param_info->type);
         const ParmVarDecl *param = fn_decl->getParamDecl(i);
         const char *name = decl_name(param);
         if (strlen(name) == 0) {
-            name = buf_ptr(buf_sprintf("arg%d", i));
+            buf_resize(&name_buf, 0);
+            buf_appendf(&name_buf, "arg%d", i);
+            name = buf_ptr(&name_buf);
         }
-        QualType qt = param->getOriginalType();
-        bool is_noalias = qt.isRestrictQualified();
-        AstNode *type_node = make_qual_type_node(c, qt, fn_decl);
-        if (!type_node) {
-            emit_warning(c, param, "skipping function %s, unresolved param type\n", name);
-            return;
-        }
-
-        node->data.fn_proto.params.append(create_param_decl_node(c, name, type_node, is_noalias));
-    }
 
-    if (fn_decl->isNoReturn()) {
-        node->data.fn_proto.return_type = create_symbol_node(c, "unreachable");
-    } else {
-        node->data.fn_proto.return_type = make_qual_type_node(c, fn_decl->getReturnType(), fn_decl);
-    }
-
-    if (!node->data.fn_proto.return_type) {
-        emit_warning(c, fn_decl, "skipping function %s, unresolved return type\n",
-                buf_ptr(&node->data.fn_proto.name));
-        return;
+        node->data.fn_proto.params.append(create_param_decl_node(c, name, type_node, param_info->is_noalias));
     }
 
     normalize_parent_ptrs(node);
 
-    c->fn_table.put(&node->data.fn_proto.name, true);
+    c->fn_table.put(buf_create_from_buf(&fn_name), true);
     c->root->data.root.top_level_decls.append(node);
 }
 
@@ -573,7 +596,9 @@ static void visit_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl)
     // use the name of this typedef
     // TODO
 
-    add_typedef_node(c, type_name, make_qual_type_node(c, child_qt, typedef_decl));
+    TypeTableEntry *child_type = resolve_qual_type(c, child_qt, typedef_decl);
+    TypeTableEntry *decl_type = get_typedecl_type(c->codegen, buf_ptr(type_name), child_type);
+    add_typedef_node(c, decl_type);
 }
 
 static void add_alias(Context *c, const char *new_name, const char *target_name) {
@@ -582,7 +607,14 @@ static void add_alias(Context *c, const char *new_name, const char *target_name)
 }
 
 static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {
-    Buf *bare_name = buf_create_from_str(decl_name(enum_decl));
+    const char *raw_name = decl_name(enum_decl);
+    // we have no interest in top level anonymous enums since they're
+    // not exposing anything.
+    if (raw_name[0] == 0) {
+        return;
+    }
+
+    Buf *bare_name = buf_create_from_str(raw_name);
     Buf *full_type_name = buf_sprintf("enum_%s", buf_ptr(bare_name));
 
     if (c->enum_type_table.maybe_get(bare_name)) {
@@ -590,97 +622,145 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {
         return;
     }
 
-    // eagerly put the name in the table, but we need to remember to remove it if it fails 
-    // boy it would be nice to have defer here wouldn't it
-    c->enum_type_table.put(bare_name, true);
-
     const EnumDecl *enum_def = enum_decl->getDefinition();
 
     if (!enum_def) {
+        TypeTableEntry *typedecl_type = get_typedecl_type(c->codegen, buf_ptr(full_type_name),
+                c->codegen->builtin_types.entry_u8);
+        c->enum_type_table.put(bare_name, typedecl_type);
+
         // this is a type that we can point to but that's it, same as `struct Foo;`.
-        add_typedef_node(c, full_type_name, create_symbol_node(c, "u8"));
+        add_typedef_node(c, typedecl_type);
         add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name));
         return;
     }
 
-    AstNode *node = create_node(c, NodeTypeStructDecl);
-    buf_init_from_buf(&node->data.struct_decl.name, full_type_name);
-
-    node->data.struct_decl.kind = ContainerKindEnum;
-    node->data.struct_decl.visib_mod = VisibModExport;
-    node->data.struct_decl.directives = create_empty_directives(c);
-
-
-    ZigList<AstNode *> var_decls = {0};
-    int i = 0;
+    // count and validate
+    uint32_t field_count = 0;
     for (auto it = enum_def->enumerator_begin(),
               it_end = enum_def->enumerator_end();
-              it != it_end; ++it, i += 1)
+              it != it_end; ++it, field_count += 1)
     {
         const EnumConstantDecl *enum_const = *it;
         if (enum_const->getInitExpr()) {
-            c->enum_type_table.remove(bare_name);
             emit_warning(c, enum_const, "skipping enum %s - has init expression\n", buf_ptr(bare_name));
             return;
         }
-        Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
+    }
+
+    TypeTableEntry *enum_type = get_partial_container_type(c->codegen, c->import,
+            ContainerKindEnum, c->source_node, buf_ptr(full_type_name));
+
+    enum_type->data.enumeration.gen_field_count = 0;
+    enum_type->data.enumeration.complete = true;
 
-        Buf field_name = BUF_INIT;
+    TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(c->codegen, field_count);
+    enum_type->align_in_bits = tag_type_entry->size_in_bits;
+    enum_type->size_in_bits = tag_type_entry->size_in_bits;
+    enum_type->data.enumeration.tag_type = tag_type_entry;
 
+    c->enum_type_table.put(bare_name, enum_type);
+    // make an alias without the "enum_" prefix. this will get emitted at the
+    // end if it doesn't conflict with anything else
+    add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name));
+
+    enum_type->data.enumeration.field_count = field_count;
+    enum_type->data.enumeration.fields = allocate<TypeEnumField>(field_count);
+    LLVMZigDIEnumerator **di_enumerators = allocate<LLVMZigDIEnumerator*>(field_count);
+
+    ZigList<AstNode *> var_decls = {0};
+    uint32_t i = 0;
+    for (auto it = enum_def->enumerator_begin(),
+              it_end = enum_def->enumerator_end();
+              it != it_end; ++it, i += 1)
+    {
+        const EnumConstantDecl *enum_const = *it;
+
+        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)) {
             Buf *slice = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name));
             if (valid_symbol_starter(buf_ptr(slice)[0])) {
-                buf_init_from_buf(&field_name, slice);
+                field_name = slice;
             } else {
-                buf_resize(&field_name, 0);
-                buf_appendf(&field_name, "_%s", buf_ptr(slice));
+                field_name = buf_sprintf("_%s", buf_ptr(slice));
             }
         } else {
-            buf_init_from_buf(&field_name, enum_val_name);
+            field_name = enum_val_name;
         }
 
-        AstNode *field_node = create_struct_field_node(c, buf_ptr(&field_name), create_symbol_node(c, "void"));
-        node->data.struct_decl.fields.append(field_node);
+        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] = LLVMZigCreateDebugEnumerator(c->codegen->dbuilder, buf_ptr(type_enum_field->name), i);
+
 
         // in C each enum value is in the global namespace. so we put them there too.
-        AstNode *field_access_node = create_field_access_node(c, buf_ptr(full_type_name), buf_ptr(&field_name));
+        // at this point we can rely on the enum emitting successfully
+        AstNode *field_access_node = create_field_access_node(c, buf_ptr(full_type_name), buf_ptr(field_name));
         AstNode *var_node = create_var_decl_node(c, buf_ptr(enum_val_name), field_access_node);
         var_decls.append(var_node);
-        c->root_name_table.put(enum_val_name, true);
+        c->global_value_table.put(enum_val_name, enum_type);
     }
 
-    normalize_parent_ptrs(node);
-    c->root->data.root.top_level_decls.append(node);
+    // create llvm type for root struct
+    enum_type->type_ref = tag_type_entry->type_ref;
+
+    // create debug type for tag
+    unsigned line = c->source_node ? (c->source_node->line + 1) : 0;
+    LLVMZigDIType *tag_di_type = LLVMZigCreateDebugEnumerationType(c->codegen->dbuilder,
+            LLVMZigFileToScope(c->import->di_file), buf_ptr(bare_name),
+            c->import->di_file, line,
+            tag_type_entry->size_in_bits, tag_type_entry->align_in_bits, di_enumerators, field_count,
+            tag_type_entry->di_type, "");
+
+    LLVMZigReplaceTemporary(c->codegen->dbuilder, enum_type->di_type, tag_di_type);
+    enum_type->di_type = tag_di_type;
+
+    //////////
+
+    // now create top level decl for the type
+    AstNode *enum_node = create_node(c, NodeTypeStructDecl);
+    buf_init_from_buf(&enum_node->data.struct_decl.name, full_type_name);
+    enum_node->data.struct_decl.kind = ContainerKindEnum;
+    enum_node->data.struct_decl.visib_mod = VisibModExport;
+    enum_node->data.struct_decl.directives = create_empty_directives(c);
+    enum_node->data.struct_decl.type_entry = enum_type;
+
+    for (uint32_t i = 0; i < field_count; i += 1) {
+        TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i];
+        AstNode *type_node = make_type_node(c, type_enum_field->type_entry);
+        AstNode *field_node = create_struct_field_node(c, buf_ptr(type_enum_field->name), type_node);
+        enum_node->data.struct_decl.fields.append(field_node);
+    }
+
+    normalize_parent_ptrs(enum_node);
+    c->root->data.root.top_level_decls.append(enum_node);
 
     for (int i = 0; i < var_decls.length; i += 1) {
         AstNode *var_node = var_decls.at(i);
         c->root->data.root.top_level_decls.append(var_node);
     }
 
-    // make an alias without the "enum_" prefix. this will get emitted at the
-    // end if it doesn't conflict with anything else
-    add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name));
 }
 
 static void visit_record_decl(Context *c, const RecordDecl *record_decl) {
     const char *raw_name = decl_name(record_decl);
 
+    // we have no interest in top level anonymous structs since they're
+    // not exposing anything.
     if (record_decl->isAnonymousStructOrUnion() || raw_name[0] == 0) {
         return;
     }
 
-    Buf *bare_name = buf_create_from_str(raw_name);
-
     if (!record_decl->isStruct()) {
-        emit_warning(c, record_decl, "skipping record %s, not a struct", buf_ptr(bare_name));
-        return;
-    }
-
-    if (buf_len(bare_name) == 0) {
-        emit_warning(c, record_decl, "skipping anonymous struct");
+        emit_warning(c, record_decl, "skipping record %s, not a struct", raw_name);
         return;
     }
 
+    Buf *bare_name = buf_create_from_str(raw_name);
     Buf *full_type_name = buf_sprintf("struct_%s", buf_ptr(bare_name));
 
     if (c->struct_type_table.maybe_get(bare_name)) {
@@ -688,55 +768,127 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) {
         return;
     }
 
-    // eagerly put the name in the table, but we need to remember to remove it if it fails 
-    // boy it would be nice to have defer here wouldn't it
-    c->struct_type_table.put(bare_name, true);
-
-
     RecordDecl *record_def = record_decl->getDefinition();
     if (!record_def) {
+        TypeTableEntry *typedecl_type = get_typedecl_type(c->codegen, buf_ptr(full_type_name),
+                c->codegen->builtin_types.entry_u8);
+        c->struct_type_table.put(bare_name, typedecl_type);
+
         // this is a type that we can point to but that's it, such as `struct Foo;`.
-        add_typedef_node(c, full_type_name, create_symbol_node(c, "u8"));
+        add_typedef_node(c, typedecl_type);
         add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name));
         return;
     }
 
-    AstNode *node = create_node(c, NodeTypeStructDecl);
-    buf_init_from_buf(&node->data.struct_decl.name, full_type_name);
+    TypeTableEntry *struct_type = get_partial_container_type(c->codegen, c->import,
+            ContainerKindStruct, c->source_node, buf_ptr(full_type_name));
 
-    node->data.struct_decl.kind = ContainerKindStruct;
-    node->data.struct_decl.visib_mod = VisibModExport;
-    node->data.struct_decl.directives = create_empty_directives(c);
+    c->struct_type_table.put(bare_name, struct_type);
+    // make an alias without the "struct_" prefix. this will get emitted at the
+    // end if it doesn't conflict with anything else
+    add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name));
 
+    // count fields and validate
+    uint32_t field_count = 0;
     for (auto it = record_def->field_begin(),
               it_end = record_def->field_end();
-              it != it_end; ++it)
+              it != it_end; ++it, field_count += 1)
     {
         const FieldDecl *field_decl = *it;
 
         if (field_decl->isBitField()) {
-            c->struct_type_table.remove(bare_name);
             emit_warning(c, field_decl, "skipping struct %s - has bitfield\n", buf_ptr(bare_name));
             return;
         }
+    }
+
+    struct_type->data.structure.src_field_count = field_count;
+    struct_type->data.structure.fields = allocate<TypeStructField>(field_count);
+
+    // we possibly allocate too much here since gen_field_count can be lower than field_count.
+    // the only problem is potential wasted space though.
+    LLVMTypeRef *element_types = allocate<LLVMTypeRef>(field_count);
+    LLVMZigDIType **di_element_types = allocate<LLVMZigDIType*>(field_count);
+
+    uint64_t total_size_in_bits = 0;
+    uint64_t first_field_align_in_bits = 0;
+    uint64_t offset_in_bits = 0;
+
+    uint32_t i = 0;
+    unsigned line = c->source_node ? c->source_node->line : 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;
+
+        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;
+        type_struct_field->type_entry = resolve_qual_type(c, field_decl->getType(), field_decl);
 
-        AstNode *type_node = make_qual_type_node(c, field_decl->getType(), field_decl);
-        if (!type_node) {
-            c->struct_type_table.remove(bare_name);
-            emit_warning(c, field_decl, "skipping struct %s - unhandled type\n", buf_ptr(bare_name));
+        if (type_struct_field->type_entry->id == TypeTableEntryIdInvalid) {
+            emit_warning(c, field_decl, "skipping struct %s - unresolved type\n", buf_ptr(bare_name));
             return;
         }
 
-        AstNode *field_node = create_struct_field_node(c, decl_name(field_decl), type_node);
-        node->data.struct_decl.fields.append(field_node);
+        di_element_types[i] = LLVMZigCreateDebugMemberType(c->codegen->dbuilder,
+                LLVMZigTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name),
+                c->import->di_file, line + 1,
+                type_struct_field->type_entry->size_in_bits,
+                type_struct_field->type_entry->align_in_bits,
+                offset_in_bits, 0, type_struct_field->type_entry->di_type);
+
+        element_types[i] = type_struct_field->type_entry->type_ref;
+        assert(di_element_types[i]);
+        assert(element_types[i]);
+
+        total_size_in_bits += type_struct_field->type_entry->size_in_bits;
+        if (first_field_align_in_bits == 0) {
+            first_field_align_in_bits = type_struct_field->type_entry->align_in_bits;
+        }
+        offset_in_bits += type_struct_field->type_entry->size_in_bits;
+
     }
+    struct_type->data.structure.embedded_in_current = false;
 
-    normalize_parent_ptrs(node);
-    c->root->data.root.top_level_decls.append(node);
+    struct_type->data.structure.gen_field_count = field_count;
+    struct_type->data.structure.complete = true;
 
-    // make an alias without the "struct_" prefix. this will get emitted at the
-    // end if it doesn't conflict with anything else
-    add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name));
+    LLVMStructSetBody(struct_type->type_ref, element_types, field_count, false);
+
+    struct_type->align_in_bits = first_field_align_in_bits;
+    struct_type->size_in_bits = total_size_in_bits;
+
+    LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(c->codegen->dbuilder,
+            LLVMZigFileToScope(c->import->di_file),
+            buf_ptr(full_type_name),
+            c->import->di_file, line + 1, struct_type->size_in_bits, struct_type->align_in_bits, 0,
+            nullptr, di_element_types, field_count, 0, nullptr, "");
+
+    LLVMZigReplaceTemporary(c->codegen->dbuilder, struct_type->di_type, replacement_di_type);
+    struct_type->di_type = replacement_di_type;
+
+    //////
+
+    // now create a top level decl node for the type
+    AstNode *struct_node = create_node(c, NodeTypeStructDecl);
+    buf_init_from_buf(&struct_node->data.struct_decl.name, full_type_name);
+    struct_node->data.struct_decl.kind = ContainerKindStruct;
+    struct_node->data.struct_decl.visib_mod = VisibModExport;
+    struct_node->data.struct_decl.directives = create_empty_directives(c);
+    struct_node->data.struct_decl.type_entry = struct_type;
+
+    for (uint32_t i = 0; i < field_count; i += 1) {
+        TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
+        AstNode *type_node = make_type_node(c, type_struct_field->type_entry);
+        AstNode *field_node = create_struct_field_node(c, buf_ptr(type_struct_field->name), type_node);
+        struct_node->data.struct_decl.fields.append(field_node);
+    }
+
+    normalize_parent_ptrs(struct_node);
+    c->root->data.root.top_level_decls.append(struct_node);
 }
 
 static void visit_var_decl(Context *c, const VarDecl *var_decl) {
@@ -754,8 +906,8 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) {
     }
 
     QualType qt = var_decl->getType();
-    AstNode *type_node = make_qual_type_node(c, qt, var_decl);
-    if (!type_node) {
+    TypeTableEntry *var_type = resolve_qual_type(c, qt, var_decl);
+    if (var_type->id == TypeTableEntryIdInvalid) {
         emit_warning(c, var_decl, "ignoring variable '%s' - unresolved type\n", buf_ptr(name));
         return;
     }
@@ -778,7 +930,8 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) {
         switch (ap_value->getKind()) {
             case APValue::Int:
                 {
-                    if (!type_is_int(type_node)) {
+                    TypeTableEntry *canon_type = get_underlying_type(var_type);
+                    if (canon_type->id != TypeTableEntryIdInt) {
                         emit_warning(c, var_decl,
                             "ignoring variable '%s' - int initializer for non int type\n", buf_ptr(name));
                         return;
@@ -819,17 +972,19 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) {
                 return;
         }
 
+        AstNode *type_node = make_type_node(c, var_type);
         AstNode *var_node = create_typed_var_decl_node(c, true, buf_ptr(name), type_node, init_node);
         c->root->data.root.top_level_decls.append(var_node);
-        c->root_name_table.put(name, true);
+        c->global_value_table.put(name, var_type);
         return;
     }
 
     if (is_extern) {
+        AstNode *type_node = make_type_node(c, var_type);
         AstNode *var_node = create_typed_var_decl_node(c, is_const, buf_ptr(name), type_node, nullptr);
         var_node->data.variable_declaration.is_extern = true;
         c->root->data.root.top_level_decls.append(var_node);
-        c->root_name_table.put(name, true);
+        c->global_value_table.put(name, var_type);
         return;
     }
 
@@ -864,7 +1019,10 @@ static bool decl_visitor(void *context, const Decl *decl) {
 }
 
 static bool name_exists(Context *c, Buf *name) {
-    if (c->root_name_table.maybe_get(name)) {
+    if (c->global_type_table.maybe_get(name)) {
+        return true;
+    }
+    if (c->global_value_table.maybe_get(name)) {
         return true;
     }
     if (c->fn_table.maybe_get(name)) {
@@ -1001,6 +1159,11 @@ static void process_macro(Context *c, Buf *name, Buf *value) {
 
     // maybe it's a symbol
     if (is_simple_symbol(value)) {
+        // if it equals itself, ignore. for example, from stdio.h:
+        // #define stdin stdin
+        if (buf_eql_buf(name, value)) {
+            return;
+        }
         c->macro_symbols.append({name, value});
     }
 }
@@ -1054,46 +1217,43 @@ static void process_preprocessor_entities(Context *c, ASTUnit &unit) {
 }
 
 int parse_h_buf(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, Buf *source,
-        const char **args, int args_len, const char *libc_include_path, bool warnings_on,
-        uint32_t *next_node_index)
+        CodeGen *codegen, AstNode *source_node)
 {
     int err;
     Buf tmp_file_path = BUF_INIT;
     if ((err = os_buf_to_tmp_file(source, buf_create_from_str(".h"), &tmp_file_path))) {
         return err;
     }
-    ZigList<const char *> clang_argv = {0};
-    clang_argv.append(buf_ptr(&tmp_file_path));
-
-    clang_argv.append("-isystem");
-    clang_argv.append(libc_include_path);
-
-    for (int i = 0; i < args_len; i += 1) {
-        clang_argv.append(args[i]);
-    }
 
-    err = parse_h_file(import, errors, &clang_argv, warnings_on, next_node_index);
+    err = parse_h_file(import, errors, buf_ptr(&tmp_file_path), codegen, source_node);
 
     os_delete_file(&tmp_file_path);
 
     return err;
 }
 
-int parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors,
-        ZigList<const char *> *clang_argv, bool warnings_on, uint32_t *next_node_index)
+int parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, const char *target_file,
+        CodeGen *codegen, AstNode *source_node)
 {
     Context context = {0};
     Context *c = &context;
-    c->warnings_on = warnings_on;
+    c->warnings_on = codegen->verbose;
     c->import = import;
     c->errors = errors;
     c->visib_mod = VisibModPub;
-    c->root_name_table.init(8);
+    c->global_type_table.init(8);
+    c->global_value_table.init(8);
     c->enum_type_table.init(8);
     c->struct_type_table.init(8);
     c->fn_table.init(8);
     c->macro_table.init(8);
-    c->next_node_index = next_node_index;
+    c->codegen = codegen;
+    c->source_node = source_node;
+
+    ZigList<const char *> clang_argv = {0};
+
+    clang_argv.append("-x");
+    clang_argv.append("c");
 
     char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS");
     if (ZIG_PARSEH_CFLAGS) {
@@ -1103,28 +1263,37 @@ int parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors,
         while (space) {
             if (space - start > 0) {
                 buf_init_from_mem(&tmp_buf, start, space - start);
-                clang_argv->append(buf_ptr(buf_create_from_buf(&tmp_buf)));
+                clang_argv.append(buf_ptr(buf_create_from_buf(&tmp_buf)));
             }
             start = space + 1;
             space = strstr(start, " ");
         }
         buf_init_from_str(&tmp_buf, start);
-        clang_argv->append(buf_ptr(buf_create_from_buf(&tmp_buf)));
+        clang_argv.append(buf_ptr(buf_create_from_buf(&tmp_buf)));
     }
 
-    clang_argv->append("-isystem");
-    clang_argv->append(ZIG_HEADERS_DIR);
+    clang_argv.append("-isystem");
+    clang_argv.append(ZIG_HEADERS_DIR);
+
+    clang_argv.append("-isystem");
+    clang_argv.append(buf_ptr(codegen->libc_include_path));
+
+    for (int i = 0; i < codegen->clang_argv_len; i += 1) {
+        clang_argv.append(codegen->clang_argv[i]);
+    }
 
     // we don't need spell checking and it slows things down
-    clang_argv->append("-fno-spell-checking");
+    clang_argv.append("-fno-spell-checking");
 
     // this gives us access to preprocessing entities, presumably at
     // the cost of performance
-    clang_argv->append("-Xclang");
-    clang_argv->append("-detailed-preprocessing-record");
+    clang_argv.append("-Xclang");
+    clang_argv.append("-detailed-preprocessing-record");
+
+    clang_argv.append(target_file);
 
-    // to make the end argument work
-    clang_argv->append(nullptr);
+    // to make the [start...end] argument work
+    clang_argv.append(nullptr);
 
     IntrusiveRefCntPtr<DiagnosticsEngine> diags(CompilerInstance::createDiagnostics(new DiagnosticOptions));
 
@@ -1138,7 +1307,7 @@ int parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors,
     const char *resources_path = ZIG_HEADERS_DIR;
     std::unique_ptr<ASTUnit> err_unit;
     std::unique_ptr<ASTUnit> ast_unit(ASTUnit::LoadFromCommandLine(
-            &clang_argv->at(0), &clang_argv->last(),
+            &clang_argv.at(0), &clang_argv.last(),
             pch_container_ops, diags, resources_path,
             only_local_decls, capture_diagnostics, None, true, false, TU_Complete,
             false, false, allow_pch_with_compiler_errors, skip_function_bodies,
src/parseh.hpp
@@ -11,10 +11,10 @@
 
 #include "all_types.hpp"
 
-int parse_h_file(ImportTableEntry *out_import, ZigList<ErrorMsg *> *out_errs,
-        ZigList<const char *> *clang_argv, bool warnings_on, uint32_t *next_node_index);
-int parse_h_buf(ImportTableEntry *out_import, ZigList<ErrorMsg *> *out_errs,
-        Buf *source, const char **args, int args_len, const char *libc_include_path,
-        bool warnings_on, uint32_t *next_node_index);
+int parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, const char *target_file,
+        CodeGen *codegen, AstNode *source_node);
+
+int parse_h_buf(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, Buf *source,
+        CodeGen *codegen, AstNode *source_node);
 
 #endif
src/parser.cpp
@@ -908,7 +908,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand
 
 /*
 PrimaryExpression = "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." "Symbol")
-KeywordLiteral : "true" | "false" | "null" | "break" | "continue" | "undefined" | "error"
+KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type"
 */
 static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
@@ -954,6 +954,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token);
         *token_index += 1;
         return node;
+    } else if (token->id == TokenIdKeywordType) {
+        AstNode *node = ast_create_node(pc, NodeTypeTypeLiteral, token);
+        *token_index += 1;
+        return node;
     } else if (token->id == TokenIdKeywordError) {
         AstNode *node = ast_create_node(pc, NodeTypeErrorType, token);
         *token_index += 1;
@@ -2470,7 +2474,34 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index,
 }
 
 /*
-TopLevelDecl : many(Directive) option(FnVisibleMod) (FnDef | ExternFnProto | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl | CImportDecl)
+TypeDecl = "type" "Symbol" "=" TypeExpr ";"
+*/
+static AstNode *ast_parse_type_decl(ParseContext *pc, int *token_index,
+        ZigList<AstNode*> *directives, VisibMod visib_mod)
+{
+    Token *first_token = &pc->tokens->at(*token_index);
+
+    if (first_token->id != TokenIdKeywordType) {
+        return nullptr;
+    }
+    *token_index += 1;
+
+    Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
+    ast_eat_token(pc, token_index, TokenIdEq);
+
+    AstNode *node = ast_create_node(pc, NodeTypeTypeDecl, first_token);
+    ast_buf_from_token(pc, name_tok, &node->data.type_decl.symbol);
+    node->data.type_decl.child_type = ast_parse_prefix_op_expr(pc, token_index, true);
+
+    node->data.type_decl.visib_mod = visib_mod;
+    node->data.type_decl.directives = directives;
+
+    normalize_parent_ptrs(node);
+    return node;
+}
+
+/*
+TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl | TypeDecl)
 */
 static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) {
     for (;;) {
@@ -2545,6 +2576,12 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis
             continue;
         }
 
+        AstNode *type_decl_node = ast_parse_type_decl(pc, token_index, directives, visib_mod);
+        if (type_decl_node) {
+            top_level_decls->append(type_decl_node);
+            continue;
+        }
+
         if (directives->length > 0) {
             ast_error(pc, directive_token, "invalid directive");
         }
@@ -2631,9 +2668,16 @@ void normalize_parent_ptrs(AstNode *node) {
             set_field(&node->data.return_expr.expr);
             break;
         case NodeTypeVariableDeclaration:
+            if (node->data.variable_declaration.directives) {
+                set_list_fields(node->data.variable_declaration.directives);
+            }
             set_field(&node->data.variable_declaration.type);
             set_field(&node->data.variable_declaration.expr);
             break;
+        case NodeTypeTypeDecl:
+            set_list_fields(node->data.type_decl.directives);
+            set_field(&node->data.type_decl.child_type);
+            break;
         case NodeTypeErrorValueDecl:
             // none
             break;
@@ -2772,5 +2816,8 @@ void normalize_parent_ptrs(AstNode *node) {
         case NodeTypeErrorType:
             // none
             break;
+        case NodeTypeTypeLiteral:
+            // none
+            break;
     }
 }
src/tokenizer.cpp
@@ -101,7 +101,7 @@ const char * zig_keywords[] = {
     "true", "false", "null", "fn", "return", "var", "const", "extern",
     "pub", "export", "import", "c_import", "if", "else", "goto", "asm",
     "volatile", "struct", "enum", "while", "for", "continue", "break",
-    "null", "noalias", "switch", "undefined", "error"
+    "null", "noalias", "switch", "undefined", "error", "type"
 };
 
 bool is_zig_keyword(Buf *buf) {
@@ -271,6 +271,8 @@ static void end_token(Tokenize *t) {
         t->cur_tok->id = TokenIdKeywordUndefined;
     } else if (mem_eql_str(token_mem, token_len, "error")) {
         t->cur_tok->id = TokenIdKeywordError;
+    } else if (mem_eql_str(token_mem, token_len, "type")) {
+        t->cur_tok->id = TokenIdKeywordType;
     }
 
     t->cur_tok = nullptr;
@@ -1084,6 +1086,7 @@ const char * token_name(TokenId id) {
         case TokenIdKeywordSwitch: return "switch";
         case TokenIdKeywordUndefined: return "undefined";
         case TokenIdKeywordError: return "error";
+        case TokenIdKeywordType: return "type";
         case TokenIdLParen: return "(";
         case TokenIdRParen: return ")";
         case TokenIdComma: return ",";
src/tokenizer.hpp
@@ -40,6 +40,7 @@ enum TokenId {
     TokenIdKeywordSwitch,
     TokenIdKeywordUndefined,
     TokenIdKeywordError,
+    TokenIdKeywordType,
     TokenIdLParen,
     TokenIdRParen,
     TokenIdComma,
test/run_tests.cpp
@@ -115,7 +115,7 @@ static TestCase *add_parseh_case(const char *case_name, const char *source, int
 
     test_case->compiler_args.append("parseh");
     test_case->compiler_args.append(tmp_h_path);
-    test_case->compiler_args.append("--c-import-warnings");
+    test_case->compiler_args.append("--verbose");
 
     test_cases.append(test_case);
 
@@ -1689,7 +1689,7 @@ var a : i32 = 2;
     add_compile_fail_case("byvalue struct on exported functions", R"SOURCE(
 struct A { x : i32, }
 export fn f(a : A) {}
-    )SOURCE", 1, ".tmp_source.zig:3:13: error: byvalue struct parameters not yet supported on exported functions");
+    )SOURCE", 1, ".tmp_source.zig:3:13: error: byvalue struct parameters not yet supported on extern functions");
 
     add_compile_fail_case("duplicate field in struct value expression", R"SOURCE(
 struct A {
@@ -1929,7 +1929,7 @@ pub const Foo1 = enum_Foo._1;)OUTPUT",
 
     add_parseh_case("restrict -> noalias", R"SOURCE(
 void foo(void *restrict bar, void *restrict);
-    )SOURCE", 1, R"OUTPUT(pub const c_void = u8;
+    )SOURCE", 1, R"OUTPUT(pub type c_void = u8;
 pub extern fn foo(noalias bar: ?&c_void, noalias arg1: ?&c_void);)OUTPUT");
 
     add_parseh_case("simple struct", R"SOURCE(
@@ -1977,14 +1977,14 @@ struct Foo {
     void (*derp)(struct Foo *foo);
 };
     )SOURCE", 2, R"OUTPUT(export struct struct_Foo {
-    derp: ?extern fn (?&struct_Foo),
+    derp: ?extern fn(?&struct_Foo),
 })OUTPUT", R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT");
 
 
     add_parseh_case("struct prototype used in func", R"SOURCE(
 struct Foo;
 struct Foo *some_func(struct Foo *foo, int x);
-    )SOURCE", 2, R"OUTPUT(pub const struct_Foo = u8;
+    )SOURCE", 2, R"OUTPUT(pub type struct_Foo = u8;
 pub extern fn some_func(foo: ?&struct_Foo, x: c_int) -> ?&struct_Foo;)OUTPUT",
         R"OUTPUT(pub const Foo = struct_Foo;)OUTPUT");