Commit f1d338194e

Andrew Kelley <superjoe30@gmail.com>
2016-02-28 06:06:46
rewrite how importing works
* Introduce the concept of packages. Closes #3 * Add support for error notes. * Introduce `@import` and `@c_import` builtin functions and remove the `import` and `c_import` top level declarations. * Introduce the `use` top level declaration. * Add `--check-unused` parameter to perform semantic analysis and codegen on all top level declarations, not just exported ones and ones referenced by exported ones. * Delete the root export node and add `--library` argument.
1 parent 28fe994
doc/vim/syntax/zig.vim
@@ -14,7 +14,7 @@ syn keyword zigConditional if else switch
 syn keyword zigRepeat while for
 
 syn keyword zigConstant null undefined
-syn keyword zigKeyword fn import c_import
+syn keyword zigKeyword fn use
 syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 void unreachable type error
 syn keyword zigType c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong
 
doc/langref.md
@@ -5,9 +5,7 @@
 ```
 Root = many(TopLevelDecl) "EOF"
 
-TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl | TypeDecl)
-
-CImportDecl = "c_import" Block
+TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | ContainerDecl | GlobalVarDecl | ErrorValueDecl | TypeDecl | UseDecl)
 
 TypeDecl = "type" "Symbol" "=" TypeExpr ";"
 
@@ -23,9 +21,7 @@ StructMember = many(Directive) option(VisibleMod) (StructField | FnDef)
 
 StructField = "Symbol" option(":" Expression) ",")
 
-Import = "import" "String" ";"
-
-RootExportDecl = "export" "Symbol" "String" ";"
+UseDecl = "use" Expression ";"
 
 ExternDecl = "extern" (FnProto | VariableDeclaration) ";"
 
doc/semantic_analysis.md
@@ -0,0 +1,75 @@
+# How Semantic Analysis Works
+
+We start with a set of files. Typically the user only has one entry point file,
+which imports the other files they want to use. However, the compiler may
+choose to add more files to the compilation, for example bootstrap.zig which
+contains the code that calls main.
+
+Our goal now is to treat everything that is marked with the `export` keyword
+as a root node, and then then parse and semantically analyze as little as
+possible in order to fulfill these exports.
+
+So, some parts of the code very well may have uncaught semantic errors, but as
+long as the code is not referenced in any way, the compiler will not complain
+because the code may as well not exist. This is similar to the fact that code
+excluded from compilation with an `#ifdef` in C is not analyzed. Avoiding
+analyzing unused code will save compilation time - one of Zig's goals.
+
+So, for each file, we iterate over the top level declarations. The set of top
+level declarations are:
+
+ * Function Definition
+ * Global Variable Declaration
+ * Container Declaration (struct or enum)
+ * Type Declaration
+ * Error Value Declaration
+ * Use Declaration
+
+Each of these can have `export` attached to them except for error value
+declarations and use declarations.
+
+When we see a top level declaration during this iteration, we determine its
+unique name identifier within the file. For example, for a function definition,
+the unique name identifier is simply its name. Using this name we add the top
+level declaration to a map.
+
+If the top level declaration is exported, we add it to a set of exported top
+level identifiers.
+
+If the top level declaration is a use declaration, we add it to a set of use
+declarations.
+
+If the top level declaration is an error value declaration, we assign it a value
+and increment the count of error values.
+
+After this preliminary iteration over the top level declarations, we iterate
+over the use declarations and resolve them. To resolve a use declaration, we
+analyze the associated expression, verify that its type is the namespace type,
+and then add all the items from the namespace into the top level declaration
+map for the current file.
+
+To analyze an expression, we recurse the abstract syntax tree of the
+expression. Whenever we must look up a symbol, if the symbol exists already,
+we can use it. Otherwise, we look it up in the top level declaration map.
+If it exists, we can use it. Otherwise, we interrupt resolving this use
+declaration to resolve the next one. If a dependency loop is detected, emit
+an error. If all use declarations are resolved yet the symbol we need still
+does not exist, emit an error.
+
+To analyze an `@import` expression, find the referenced file, parse it, and
+add it to the set of files to perform semantic analysis on.
+
+Proceed through the rest of the use declarations the same way.
+
+If we make it through the use declarations without an error, then we have a
+complete map of all globals that exist in the current file.
+
+Next we iterate over the set of exported top level declarations.
+
+If it's a function definition, add it to the set of exported function
+definitions and resolve the function prototype only. Otherwise, resolve the
+top level declaration completely. This may involve recursively resolving other
+top level declarations that expressions depend on.
+
+Finally, iterate over the set of exported function definitions and analyze the
+bodies.
doc/targets.md
@@ -8,7 +8,7 @@ How to pass a byvalue struct parameter in the C calling convention is
 target-specific. Add logic for how to do function prototypes and function calls
 for the target when an exported or external function has a byvalue struct.
 
-Write the target-specific code in std.zig.
+Write the target-specific code in the standard library.
 
 Update the C integer types to be the correct size for the target.
 
example/guess_number/main.zig
@@ -1,39 +1,38 @@
-export executable "guess_number";
-
-import "std.zig";
-import "rand.zig";
-import "os.zig";
+const std = @import("std");
+const io = std.io;
+const Rand = std.Rand;
+const os = std.os;
 
 pub fn main(args: [][]u8) -> %void {
-    %%stdout.printf("Welcome to the Guess Number Game in Zig.\n");
+    %%io.stdout.printf("Welcome to the Guess Number Game in Zig.\n");
 
     var seed : u32 = undefined;
     const seed_bytes = (&u8)(&seed)[0...4];
-    %%os_get_random_bytes(seed_bytes);
+    %%os.get_random_bytes(seed_bytes);
 
-    var rand = rand_new(seed);
+    var rand = Rand.init(seed);
 
     const answer = rand.range_u64(0, 100) + 1;
 
     while (true) {
-        %%stdout.printf("\nGuess a number between 1 and 100: ");
+        %%io.stdout.printf("\nGuess a number between 1 and 100: ");
         var line_buf : [20]u8 = undefined;
 
-        const line_len = stdin.read(line_buf) %% |err| {
-            %%stdout.printf("Unable to read from stdin.\n");
+        const line_len = io.stdin.read(line_buf) %% |err| {
+            %%io.stdout.printf("Unable to read from stdin.\n");
             return err;
         };
 
-        const guess = parse_u64(line_buf[0...line_len - 1], 10) %% {
-            %%stdout.printf("Invalid number.\n");
+        const guess = io.parse_u64(line_buf[0...line_len - 1], 10) %% {
+            %%io.stdout.printf("Invalid number.\n");
             continue;
         };
         if (guess > answer) {
-            %%stdout.printf("Guess lower.\n");
+            %%io.stdout.printf("Guess lower.\n");
         } else if (guess < answer) {
-            %%stdout.printf("Guess higher.\n");
+            %%io.stdout.printf("Guess higher.\n");
         } else {
-            %%stdout.printf("You win!\n");
+            %%io.stdout.printf("You win!\n");
             return;
         }
     }
example/hello_world/hello.zig
@@ -1,7 +1,5 @@
-export executable "hello";
-
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
-    %%stdout.printf("Hello, world!\n");
+    %%io.stdout.printf("Hello, world!\n");
 }
example/hello_world/hello_libc.zig
@@ -1,11 +1,6 @@
-#link("c")
-export executable "hello";
-
-c_import {
-    @c_include("stdio.h");
-}
+const c = @c_import(@c_include("stdio.h"));
 
 export fn main(argc: c_int, argv: &&u8) -> c_int {
-    printf(c"Hello, world!\n");
+    c.printf(c"Hello, world!\n");
     return 0;
 }
src/all_types.hpp
@@ -76,6 +76,7 @@ struct ConstExprValue {
         ConstStructValue x_struct;
         ConstArrayValue x_array;
         ConstPtrValue x_ptr;
+        ImportTableEntry *x_import;
     } data;
 };
 
@@ -91,6 +92,7 @@ enum ReturnKnowledge {
 struct Expr {
     TypeTableEntry *type_entry;
     ReturnKnowledge return_knowledge;
+    VariableTableEntry *variable;
 
     LLVMValueRef const_llvm_val;
     ConstExprValue const_val;
@@ -103,13 +105,30 @@ struct StructValExprCodeGen {
     AstNode *source_node;
 };
 
+enum VisibMod {
+    VisibModPrivate,
+    VisibModPub,
+    VisibModExport,
+};
+
+enum TldResolution {
+    TldResolutionUnresolved,
+    TldResolutionInvalid,
+    TldResolutionOk,
+};
+
 struct TopLevelDecl {
-    // reminder: hash tables must be initialized before use
-    HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> deps;
+    // populated by parser
     Buf *name;
+    ZigList<AstNode *> *directives;
+    VisibMod visib_mod;
+
+    // populated by semantic analyzer
     ImportTableEntry *import;
     // set this flag temporarily to detect infinite loops
-    bool in_current_deps;
+    bool dep_loop_flag;
+    TldResolution resolution;
+    AstNode *parent_decl;
 };
 
 struct TypeEnumField {
@@ -120,7 +139,6 @@ struct TypeEnumField {
 
 enum NodeType {
     NodeTypeRoot,
-    NodeTypeRootExportDecl,
     NodeTypeFnProto,
     NodeTypeFnDef,
     NodeTypeFnDecl,
@@ -143,8 +161,7 @@ enum NodeType {
     NodeTypeArrayAccessExpr,
     NodeTypeSliceExpr,
     NodeTypeFieldAccessExpr,
-    NodeTypeImport,
-    NodeTypeCImport,
+    NodeTypeUse,
     NodeTypeBoolLiteral,
     NodeTypeNullLiteral,
     NodeTypeUndefinedLiteral,
@@ -173,15 +190,8 @@ struct AstNodeRoot {
     ZigList<AstNode *> top_level_decls;
 };
 
-enum VisibMod {
-    VisibModPrivate,
-    VisibModPub,
-    VisibModExport,
-};
-
 struct AstNodeFnProto {
-    ZigList<AstNode *> *directives; // can be null if no directives
-    VisibMod visib_mod;
+    TopLevelDecl top_level_decl;
     Buf name;
     ZigList<AstNode *> params;
     AstNode *return_type;
@@ -191,13 +201,10 @@ struct AstNodeFnProto {
 
     // populated by semantic analyzer:
 
-    // the struct decl node this fn proto is inside. can be null.
-    AstNode *struct_node;
     // the function definition this fn proto is inside. can be null.
     AstNode *fn_def_node;
     FnTableEntry *fn_table_entry;
     bool skip;
-    TopLevelDecl top_level_decl;
     Expr resolved_expr;
 };
 
@@ -263,41 +270,36 @@ struct AstNodeDefer {
 };
 
 struct AstNodeVariableDeclaration {
+    TopLevelDecl top_level_decl;
     Buf symbol;
     bool is_const;
     bool is_extern;
-    VisibMod visib_mod;
     // one or both of type and expr will be non null
     AstNode *type;
     AstNode *expr;
-    ZigList<AstNode *> *directives;
 
     // populated by semantic analyzer
-    TopLevelDecl top_level_decl;
     Expr resolved_expr;
     VariableTableEntry *variable;
 };
 
 struct AstNodeTypeDecl {
-    VisibMod visib_mod;
-    ZigList<AstNode *> *directives;
+    TopLevelDecl top_level_decl;
     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;
+    TypeTableEntry *child_type_entry;
 };
 
 struct AstNodeErrorValueDecl {
+    TopLevelDecl top_level_decl;
     Buf name;
-    VisibMod visib_mod;
-    ZigList<AstNode *> *directives;
 
     // populated by semantic analyzer
-    TopLevelDecl top_level_decl;
     ErrorTableEntry *err;
 };
 
@@ -430,12 +432,6 @@ struct AstNodeDirective {
     AstNode *expr;
 };
 
-struct AstNodeRootExportDecl {
-    Buf type;
-    Buf name;
-    ZigList<AstNode *> *directives;
-};
-
 enum PrefixOp {
     PrefixOpInvalid,
     PrefixOpBoolNot,
@@ -458,19 +454,8 @@ struct AstNodePrefixOpExpr {
     Expr resolved_expr;
 };
 
-struct AstNodeImport {
-    Buf path;
-    ZigList<AstNode *> *directives;
-    VisibMod visib_mod;
-
-    // populated by semantic analyzer
-    ImportTableEntry *import;
-};
-
-struct AstNodeCImport {
-    ZigList<AstNode *> *directives;
-    VisibMod visib_mod;
-    AstNode *block;
+struct AstNodeUse {
+    AstNode *expr;
 
     // populated by semantic analyzer
     TopLevelDecl top_level_decl;
@@ -600,23 +585,21 @@ enum ContainerKind {
 };
 
 struct AstNodeStructDecl {
+    TopLevelDecl top_level_decl;
     Buf name;
     ContainerKind kind;
     ZigList<AstNode *> fields;
     ZigList<AstNode *> fns;
-    ZigList<AstNode *> *directives;
-    VisibMod visib_mod;
 
     // populated by semantic analyzer
+    BlockContext *block_context;
     TypeTableEntry *type_entry;
-    TopLevelDecl top_level_decl;
 };
 
 struct AstNodeStructField {
+    TopLevelDecl top_level_decl;
     Buf name;
     AstNode *type;
-    ZigList<AstNode *> *directives;
-    VisibMod visib_mod;
 };
 
 struct AstNodeStringLiteral {
@@ -695,8 +678,6 @@ struct AstNodeSymbolExpr {
 
     // populated by semantic analyzer
     Expr resolved_expr;
-    VariableTableEntry *variable;
-    FnTableEntry *fn_entry;
     // set this to instead of analyzing the node, pretend it's a type entry and it's this one.
     TypeTableEntry *override_type_entry;
     TypeEnumField *enum_field;
@@ -750,7 +731,6 @@ struct AstNode {
     BlockContext *block_context;
     union {
         AstNodeRoot root;
-        AstNodeRootExportDecl root_export_decl;
         AstNodeFnDef fn_def;
         AstNodeFnDecl fn_decl;
         AstNodeFnProto fn_proto;
@@ -768,8 +748,7 @@ struct AstNode {
         AstNodeFnCallExpr fn_call_expr;
         AstNodeArrayAccessExpr array_access_expr;
         AstNodeSliceExpr slice_expr;
-        AstNodeImport import;
-        AstNodeCImport c_import;
+        AstNodeUse use;
         AstNodeIfBoolExpr if_bool_expr;
         AstNodeIfVarExpr if_var_expr;
         AstNodeWhileExpr while_expr;
@@ -868,8 +847,7 @@ struct TypeTableEntryStruct {
     uint64_t size_bytes;
     bool is_invalid; // true if any fields are invalid
     bool is_unknown_size_array;
-    // reminder: hash tables must be initialized before use
-    HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
+    BlockContext *block_context;
 
     // set this flag temporarily to detect infinite loops
     bool embedded_in_current;
@@ -895,8 +873,7 @@ struct TypeTableEntryEnum {
     TypeTableEntry *tag_type;
     TypeTableEntry *union_type;
 
-    // reminder: hash tables must be initialized before use
-    HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> fn_table;
+    BlockContext *block_context;
 
     // set this flag temporarily to detect infinite loops
     bool embedded_in_current;
@@ -947,6 +924,7 @@ enum TypeTableEntryId {
     TypeTableEntryIdEnum,
     TypeTableEntryIdFn,
     TypeTableEntryIdTypeDecl,
+    TypeTableEntryIdNamespace,
 };
 
 struct TypeTableEntry {
@@ -979,26 +957,26 @@ struct TypeTableEntry {
     TypeTableEntry *error_parent;
 };
 
-struct ImporterInfo {
-    ImportTableEntry *import;
-    AstNode *source_node;
+struct PackageTableEntry {
+    Buf root_src_dir;
+    Buf root_src_path; // relative to root_src_dir
+
+    // reminder: hash tables must be initialized before use
+    HashMap<Buf *, PackageTableEntry *, buf_hash, buf_eql_buf> package_table;
 };
 
 struct ImportTableEntry {
     AstNode *root;
-    Buf *path; // relative to root_source_dir
+    Buf *path; // relative to root_package->root_src_dir
+    PackageTableEntry *package;
     LLVMZigDIFile *di_file;
     Buf *source_code;
     ZigList<int> *line_offsets;
     BlockContext *block_context;
-    ZigList<ImporterInfo> importers;
     AstNode *c_import_node;
     bool any_imports_failed;
 
-    // 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> type_table;
-    HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table;
+    ZigList<AstNode *> use_decls;
 };
 
 struct FnTableEntry {
@@ -1008,14 +986,12 @@ struct FnTableEntry {
     ImportTableEntry *import_entry;
     // Required to be a pre-order traversal of the AST. (parents must come before children)
     ZigList<BlockContext *> all_block_contexts;
-    TypeTableEntry *member_of_struct;
     Buf symbol_name;
     TypeTableEntry *type_entry; // function type
     bool is_inline;
     bool internal_linkage;
     bool is_extern;
     bool is_test;
-    uint32_t ref_count; // if this is 0 we don't have to codegen it
 
     ZigList<AstNode *> cast_alloca_list;
     ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
@@ -1042,6 +1018,8 @@ enum BuiltinFnId {
     BuiltinFnIdConstEval,
     BuiltinFnIdCtz,
     BuiltinFnIdClz,
+    BuiltinFnIdImport,
+    BuiltinFnIdCImport,
 };
 
 struct BuiltinFnEntry {
@@ -1061,17 +1039,22 @@ struct CodeGen {
     LLVMZigDIBuilder *dbuilder;
     LLVMZigDICompileUnit *compile_unit;
 
-    ZigList<Buf *> lib_search_paths;
-    ZigList<Buf *> link_libs;
+    ZigList<Buf *> link_libs; // non-libc link libs
 
     // reminder: hash tables must be initialized before use
     HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
     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;
     HashMap<Buf *, ErrorTableEntry *, buf_hash, buf_eql_buf> error_table;
 
+    ZigList<ImportTableEntry *> import_queue;
+    int import_queue_index;
+    ZigList<AstNode *> export_queue;
+    int export_queue_index;
+    ZigList<AstNode *> use_queue;
+    int use_queue_index;
+
     uint32_t next_unresolved_index;
 
     struct {
@@ -1095,6 +1078,7 @@ struct CodeGen {
         TypeTableEntry *entry_unreachable;
         TypeTableEntry *entry_type;
         TypeTableEntry *entry_invalid;
+        TypeTableEntry *entry_namespace;
         TypeTableEntry *entry_num_lit_int;
         TypeTableEntry *entry_num_lit_float;
         TypeTableEntry *entry_undef;
@@ -1126,7 +1110,8 @@ struct CodeGen {
     LLVMTargetMachineRef target_machine;
     LLVMZigDIFile *dummy_di_file;
     bool is_native_target;
-    Buf *root_source_dir;
+    PackageTableEntry *root_package;
+    PackageTableEntry *std_package;
     Buf *root_out_name;
     bool windows_subsystem_windows;
     bool windows_subsystem_console;
@@ -1176,6 +1161,8 @@ struct CodeGen {
     ZigList<const char *> lib_dirs;
 
     uint32_t test_fn_count;
+
+    bool check_unused;
 };
 
 struct VariableTableEntry {
@@ -1202,7 +1189,8 @@ struct BlockContext {
     AstNode *node;
 
     // any variables that are introduced by this scope
-    HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
+    HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> decl_table;
+    HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> var_table;
 
     // if the block is inside a function, this is the function it is in:
     FnTableEntry *fn_entry;
src/analyze.cpp
@@ -14,8 +14,10 @@
 #include "config.h"
 #include "ast_render.hpp"
 
-static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node);
+static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import,
+        BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only);
 static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import,
         BlockContext *context, TypeTableEntry *expected_type, AstNode *node);
 static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type);
@@ -27,13 +29,18 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *
 static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node);
 static TypeTableEntry *resolve_expr_const_val_as_void(CodeGen *g, AstNode *node);
-static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, BlockContext *context,
-        FnTableEntry *fn);
+static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn);
 static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type);
 static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node,
         TypeTableEntry *expected_type, uint64_t x);
-static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node);
-static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node);
+static AstNode *find_decl(BlockContext *context, Buf *name);
+static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node, bool pointer_only);
+static TopLevelDecl *get_as_top_level_decl(AstNode *node);
+static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import,
+        BlockContext *context, AstNode *source_node,
+        AstNodeVariableDeclaration *variable_declaration,
+        bool expr_is_maybe, AstNode *decl_node);
+static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node);
 
 static AstNode *first_executing_node(AstNode *node) {
     switch (node->type) {
@@ -52,7 +59,6 @@ static AstNode *first_executing_node(AstNode *node) {
         case NodeTypeSwitchRange:
             return first_executing_node(node->data.switch_range.start);
         case NodeTypeRoot:
-        case NodeTypeRootExportDecl:
         case NodeTypeFnProto:
         case NodeTypeFnDef:
         case NodeTypeFnDecl:
@@ -69,8 +75,7 @@ static AstNode *first_executing_node(AstNode *node) {
         case NodeTypeCharLiteral:
         case NodeTypeSymbol:
         case NodeTypePrefixOpExpr:
-        case NodeTypeImport:
-        case NodeTypeCImport:
+        case NodeTypeUse:
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeUndefinedLiteral:
@@ -109,43 +114,47 @@ ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
     return err;
 }
 
+ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
+    // if this assert fails, then parseh generated code that
+    // failed semantic analysis, which isn't supposed to happen
+    assert(!node->owner->c_import_node);
+
+    ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
+            node->owner->source_code, node->owner->line_offsets, msg);
+
+    err_msg_add_note(parent_msg, err);
+    return err;
+}
+
 TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
     TypeTableEntry *entry = allocate<TypeTableEntry>(1);
     entry->arrays_by_size.init(2);
     entry->id = id;
+    return entry;
+}
 
-    switch (id) {
-        case TypeTableEntryIdInvalid:
-        case TypeTableEntryIdMetaType:
-        case TypeTableEntryIdVoid:
-        case TypeTableEntryIdBool:
-        case TypeTableEntryIdUnreachable:
-        case TypeTableEntryIdInt:
-        case TypeTableEntryIdFloat:
-        case TypeTableEntryIdPointer:
-        case TypeTableEntryIdArray:
-        case TypeTableEntryIdNumLitFloat:
-        case TypeTableEntryIdNumLitInt:
-        case TypeTableEntryIdMaybe:
-        case TypeTableEntryIdFn:
-        case TypeTableEntryIdErrorUnion:
-        case TypeTableEntryIdPureError:
-        case TypeTableEntryIdUndefLit:
-        case TypeTableEntryIdTypeDecl:
-            // nothing to init
-            break;
-        case TypeTableEntryIdStruct:
-            entry->data.structure.fn_table.init(8);
-            break;
-        case TypeTableEntryIdEnum:
-            entry->data.enumeration.fn_table.init(8);
-            break;
-
+static BlockContext **get_container_block_context_ptr(TypeTableEntry *type_entry) {
+    if (type_entry->id == TypeTableEntryIdStruct) {
+        return &type_entry->data.structure.block_context;
+    } else if (type_entry->id == TypeTableEntryIdEnum) {
+        return &type_entry->data.enumeration.block_context;
     }
+    zig_unreachable();
+}
+
+static BlockContext *get_container_block_context(TypeTableEntry *type_entry) {
+    return *get_container_block_context_ptr(type_entry);
+}
 
+static TypeTableEntry *new_container_type_entry(TypeTableEntryId id, AstNode *source_node,
+        BlockContext *parent_context)
+{
+    TypeTableEntry *entry = new_type_table_entry(id);
+    *get_container_block_context_ptr(entry) = new_block_context(source_node, parent_context);
     return entry;
 }
 
+
 static int bits_needed_for_unsigned(uint64_t x) {
     if (x <= UINT8_MAX) {
         return 8;
@@ -182,6 +191,7 @@ static bool type_is_complete(TypeTableEntry *type_entry) {
         case TypeTableEntryIdPureError:
         case TypeTableEntryIdFn:
         case TypeTableEntryIdTypeDecl:
+        case TypeTableEntryIdNamespace:
             return true;
     }
     zig_unreachable();
@@ -671,7 +681,7 @@ 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);
+    TypeTableEntry *entry = new_container_type_entry(type_id, decl_node, import->block_context);
 
     switch (kind) {
         case ContainerKindStruct:
@@ -730,13 +740,19 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
     return const_val->data.x_type;
 }
 
+static TypeTableEntry *analyze_type_expr_pointer_only(CodeGen *g, ImportTableEntry *import,
+        BlockContext *context, AstNode *node, bool pointer_only)
+{
+    AstNode **node_ptr = node->parent_field;
+    analyze_expression_pointer_only(g, import, context, nullptr, *node_ptr, pointer_only);
+    return resolve_type(g, *node_ptr);
+}
+
 // 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);
+    return analyze_type_expr_pointer_only(g, import, context, node, false);
 }
 
 static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
@@ -750,7 +766,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
     }
 
     FnTypeId fn_type_id = {0};
-    fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport);
+    fn_type_id.is_extern = fn_proto->is_extern || (fn_proto->top_level_decl.visib_mod == VisibModExport);
     fn_type_id.is_naked = is_naked;
     fn_type_id.is_cold = is_cold;
     fn_type_id.param_count = node->data.fn_proto.params.length;
@@ -782,6 +798,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
             case TypeTableEntryIdUndefLit:
             case TypeTableEntryIdMetaType:
             case TypeTableEntryIdUnreachable:
+            case TypeTableEntryIdNamespace:
                 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)));
@@ -880,9 +897,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
     bool is_naked = false;
     bool is_test = false;
 
-    if (fn_proto->directives) {
-        for (int i = 0; i < fn_proto->directives->length; i += 1) {
-            AstNode *directive_node = fn_proto->directives->at(i);
+    if (fn_proto->top_level_decl.directives) {
+        for (int i = 0; i < fn_proto->top_level_decl.directives->length; i += 1) {
+            AstNode *directive_node = fn_proto->top_level_decl.directives->at(i);
             Buf *name = &directive_node->data.directive.name;
 
             if (buf_eql_str(name, "attribute")) {
@@ -907,12 +924,12 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
                             buf_sprintf("invalid function attribute: '%s'", buf_ptr(name)));
                 }
             } else if (buf_eql_str(name, "condition")) {
-                if (fn_proto->visib_mod == VisibModExport) {
+                if (fn_proto->top_level_decl.visib_mod == VisibModExport) {
                     bool include;
                     bool ok = resolve_const_expr_bool(g, import, import->block_context,
                             &directive_node->data.directive.expr, &include);
                     if (ok && !include) {
-                        fn_proto->visib_mod = VisibModPub;
+                        fn_proto->top_level_decl.visib_mod = VisibModPub;
                     }
                 } else {
                     add_node_error(g, directive_node,
@@ -925,12 +942,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
         }
     }
 
-    bool is_internal = (fn_proto->visib_mod != VisibModExport);
+    bool is_internal = (fn_proto->top_level_decl.visib_mod != VisibModExport);
     bool is_c_compat = !is_internal || fn_proto->is_extern;
     fn_table_entry->internal_linkage = !is_c_compat;
-    if (!is_internal) {
-        fn_table_entry->ref_count += 1;
-    }
 
 
 
@@ -972,18 +986,19 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
         LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoUnwindAttribute);
     }
 
-    // Add debug info.
-    unsigned line_number = node->line + 1;
-    unsigned scope_line = line_number;
-    bool is_definition = fn_table_entry->fn_def_node != nullptr;
-    unsigned flags = 0;
-    bool is_optimized = g->is_release_build;
-    LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder,
-        import->block_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "",
-        import->di_file, line_number,
-        fn_type->di_type, fn_table_entry->internal_linkage,
-        is_definition, scope_line, flags, is_optimized, fn_table_entry->fn_value);
     if (fn_table_entry->fn_def_node) {
+        // Add debug info.
+        unsigned line_number = node->line + 1;
+        unsigned scope_line = line_number;
+        bool is_definition = fn_table_entry->fn_def_node != nullptr;
+        unsigned flags = 0;
+        bool is_optimized = g->is_release_build;
+        LLVMZigDISubprogram *subprogram = LLVMZigCreateFunction(g->dbuilder,
+            import->block_context->di_scope, buf_ptr(&fn_table_entry->symbol_name), "",
+            import->di_file, line_number,
+            fn_type->di_type, fn_table_entry->internal_linkage,
+            is_definition, scope_line, flags, is_optimized, fn_table_entry->fn_value);
+
         BlockContext *context = new_block_context(fn_table_entry->fn_def_node, import->block_context);
         fn_table_entry->fn_def_node->data.fn_def.block_context = context;
         context->di_scope = LLVMZigSubprogramToScope(subprogram);
@@ -1295,59 +1310,43 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE
     struct_type->zero_bits = (debug_size_in_bits == 0);
 }
 
-static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
-        AstNode *proto_node)
-{
+static void get_fully_qualified_decl_name(Buf *buf, AstNode *decl_node, uint8_t sep) {
+    TopLevelDecl *tld = get_as_top_level_decl(decl_node);
+    AstNode *parent_decl = tld->parent_decl;
+
+    if (parent_decl) {
+        get_fully_qualified_decl_name(buf, parent_decl, sep);
+        buf_append_char(buf, sep);
+        buf_append_buf(buf, tld->name);
+    } else {
+        buf_init_from_buf(buf, tld->name);
+    }
+}
+
+static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, AstNode *proto_node) {
     if (proto_node->data.fn_proto.skip) {
         return;
     }
+
+    AstNode *parent_decl = proto_node->data.fn_proto.top_level_decl.parent_decl;
+
     AstNode *fn_def_node = proto_node->data.fn_proto.fn_def_node;
-    AstNode *struct_node = proto_node->data.fn_proto.struct_node;
     bool is_extern = proto_node->data.fn_proto.is_extern;
-    TypeTableEntry *struct_type;
-    if (struct_node) {
-        assert(struct_node->type == NodeTypeStructDecl);
-        struct_type = struct_node->data.struct_decl.type_entry;
-    } else {
-        struct_type = nullptr;
-    }
 
     Buf *proto_name = &proto_node->data.fn_proto.name;
 
-    auto fn_table = struct_type ? &struct_type->data.structure.fn_table : &import->fn_table;
-
-    auto entry = fn_table->maybe_get(proto_name);
-    bool skip = false;
-    bool is_pub = (proto_node->data.fn_proto.visib_mod != VisibModPrivate);
-    if (entry) {
-        add_node_error(g, proto_node,
-                buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
-        proto_node->data.fn_proto.skip = true;
-        skip = true;
-    }
     if (!is_extern && proto_node->data.fn_proto.is_var_args) {
         add_node_error(g, proto_node,
                 buf_sprintf("variadic arguments only allowed in extern functions"));
     }
-    if (skip) {
-        return;
-    }
 
     FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
     fn_table_entry->import_entry = import;
     fn_table_entry->proto_node = proto_node;
     fn_table_entry->fn_def_node = fn_def_node;
     fn_table_entry->is_extern = is_extern;
-    fn_table_entry->member_of_struct = struct_type;
 
-    if (struct_type) {
-        buf_resize(&fn_table_entry->symbol_name, 0);
-        buf_appendf(&fn_table_entry->symbol_name, "%s_%s",
-                buf_ptr(&struct_type->name),
-                buf_ptr(proto_name));
-    } else {
-        buf_init_from_buf(&fn_table_entry->symbol_name, proto_name);
-    }
+    get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_');
 
     g->fn_protos.append(fn_table_entry);
 
@@ -1355,39 +1354,13 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
         g->fn_defs.append(fn_table_entry);
     }
 
-    fn_table->put(proto_name, fn_table_entry);
-
-    bool is_main_fn = !struct_type && (import == g->root_import) && buf_eql_str(proto_name, "main");
+    bool is_main_fn = !parent_decl && (import == g->root_import) && buf_eql_str(proto_name, "main");
     if (is_main_fn) {
         g->main_fn = fn_table_entry;
-
-        if (g->bootstrap_import && !g->is_test_build) {
-            g->bootstrap_import->fn_table.put(buf_create_from_str("zig_user_main"), fn_table_entry);
-        }
-    }
-    bool is_test_main_fn = !struct_type && (import == g->test_runner_import) && buf_eql_str(proto_name, "main");
-    if (is_test_main_fn) {
-        assert(g->bootstrap_import);
-        assert(g->is_test_build);
-        g->bootstrap_import->fn_table.put(proto_name, fn_table_entry);
     }
 
     proto_node->data.fn_proto.fn_table_entry = fn_table_entry;
     resolve_function_proto(g, proto_node, fn_table_entry, import);
-
-    if (is_pub && !struct_type) {
-        for (int i = 0; i < import->importers.length; i += 1) {
-            ImporterInfo importer = import->importers.at(i);
-            auto table_entry = importer.import->fn_table.maybe_get(proto_name);
-            if (table_entry) {
-                add_node_error(g, importer.source_node,
-                    buf_sprintf("import of function '%s' overrides existing definition",
-                        buf_ptr(proto_name)));
-            } else {
-                importer.import->fn_table.put(proto_name, fn_table_entry);
-            }
-        }
-    }
 }
 
 static void preview_error_value_decl(CodeGen *g, AstNode *node) {
@@ -1403,110 +1376,40 @@ static void preview_error_value_decl(CodeGen *g, AstNode *node) {
         // duplicate error definitions allowed and they get the same value
         err->value = existing_entry->value->value;
     } else {
+        assert(g->error_value_count < (1 << g->err_tag_type->data.integral.bit_count));
         err->value = g->error_value_count;
         g->error_value_count += 1;
         g->error_table.put(&err->name, err);
     }
 
     node->data.error_value_decl.err = err;
+    node->data.error_value_decl.top_level_decl.resolution = TldResolutionOk;
 }
 
-static void resolve_error_value_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
-    assert(node->type == NodeTypeErrorValueDecl);
-
-    ErrorTableEntry *err = node->data.error_value_decl.err;
-
-    import->error_table.put(&err->name, err);
-
-    bool is_pub = (node->data.error_value_decl.visib_mod != VisibModPrivate);
-    if (is_pub) {
-        for (int i = 0; i < import->importers.length; i += 1) {
-            ImporterInfo importer = import->importers.at(i);
-            importer.import->error_table.put(&err->name, err);
-        }
-    }
-}
-
-static void resolve_c_import_decl(CodeGen *g, ImportTableEntry *parent_import, AstNode *node) {
-    assert(node->type == NodeTypeCImport);
-
-    AstNode *block_node = node->data.c_import.block;
-
-    BlockContext *child_context = new_block_context(node, parent_import->block_context);
-    child_context->c_import_buf = buf_alloc();
-
-    TypeTableEntry *resolved_type = analyze_block_expr(g, parent_import, child_context,
-            g->builtin_types.entry_void, block_node);
-
-    if (resolved_type->id == TypeTableEntryIdInvalid) {
+static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) {
+    TopLevelDecl *tld = get_as_top_level_decl(node);
+    if (tld->resolution != TldResolutionUnresolved) {
         return;
     }
-
-    find_libc_include_path(g);
-
-    ImportTableEntry *child_import = allocate<ImportTableEntry>(1);
-    child_import->fn_table.init(32);
-    child_import->type_table.init(8);
-    child_import->error_table.init(8);
-    child_import->c_import_node = node;
-
-    child_import->importers.append({parent_import, node});
-
-    if (node->data.c_import.visib_mod != VisibModPrivate) {
-        for (int i = 0; i < parent_import->importers.length; i += 1) {
-            ImporterInfo importer = parent_import->importers.at(i);
-            child_import->importers.append(importer);
-        }
-    }
-
-    ZigList<ErrorMsg *> errors = {0};
-
-    int err;
-    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));
-    }
-
-    if (errors.length > 0) {
-        ErrorMsg *parent_err_msg = add_node_error(g, node, buf_sprintf("C import failed"));
-        for (int i = 0; i < errors.length; i += 1) {
-            ErrorMsg *err_msg = errors.at(i);
-            err_msg_add_note(parent_err_msg, err_msg);
-        }
-
-        for (int i = 0; i < child_import->importers.length; i += 1) {
-            child_import->importers.at(i).import->any_imports_failed = true;
-        }
+    if (pointer_only && node->type == NodeTypeStructDecl) {
         return;
     }
 
-    if (g->verbose) {
-        fprintf(stderr, "\nc_import:\n");
-        fprintf(stderr, "-----------\n");
-        ast_render(stderr, child_import->root, 4);
-    }
-
-    child_import->di_file = parent_import->di_file;
-    child_import->block_context = new_block_context(child_import->root, nullptr);
-
-    detect_top_level_decl_deps(g, child_import, child_import->root);
-    analyze_top_level_decls_root(g, child_import, child_import->root);
-}
+    ImportTableEntry *import = tld->import;
+    assert(import);
 
-static void satisfy_dep(CodeGen *g, AstNode *node) {
-    Buf *name = get_resolved_top_level_decl(node)->name;
-    if (name) {
-        g->unresolved_top_level_decls.maybe_remove(name);
+    if (tld->dep_loop_flag) {
+        add_node_error(g, node, buf_sprintf("'%s' depends on itself", buf_ptr(tld->name)));
+        tld->resolution = TldResolutionInvalid;
+        return;
+    } else {
+        tld->dep_loop_flag = true;
     }
-}
 
-static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
     switch (node->type) {
         case NodeTypeFnProto:
             preview_fn_proto(g, import, node);
             break;
-        case NodeTypeRootExportDecl:
-            // handled earlier
-            return;
         case NodeTypeStructDecl:
             {
                 TypeTableEntry *type_entry = node->data.struct_decl.type_entry;
@@ -1526,8 +1429,10 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
             }
         case NodeTypeVariableDeclaration:
             {
-                VariableTableEntry *var = analyze_variable_declaration(g, import, import->block_context,
-                        nullptr, node);
+                AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
+                VariableTableEntry *var = analyze_variable_declaration_raw(g, import, import->block_context,
+                        node, variable_declaration, false, node);
+
                 g->global_vars.append(var);
                 break;
             }
@@ -1547,34 +1452,13 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
                         entry = get_typedecl_type(g, buf_ptr(decl_name), child_type);
                     }
                 }
-
-                import->type_table.put(decl_name, entry);
-
-                bool is_pub = (node->data.type_decl.visib_mod != VisibModPrivate);
-                if (is_pub) {
-                    for (int i = 0; i < import->importers.length; i += 1) {
-                        ImporterInfo importer = import->importers.at(i);
-                        auto table_entry = importer.import->type_table.maybe_get(&entry->name);
-                        if (table_entry) {
-                            add_node_error(g, importer.source_node,
-                                buf_sprintf("import of type '%s' overrides existing definition",
-                                    buf_ptr(&entry->name)));
-                        } else {
-                            importer.import->type_table.put(&entry->name, entry);
-                        }
-                    }
-                }
-
+                node->data.type_decl.child_type_entry = entry;
                 break;
             }
         case NodeTypeErrorValueDecl:
-            resolve_error_value_decl(g, import, node);
             break;
-        case NodeTypeImport:
-            // nothing to do here
-            return;
-        case NodeTypeCImport:
-            resolve_c_import_decl(g, import, node);
+        case NodeTypeUse:
+            zig_panic("TODO resolve_top_level_decl NodeTypeUse");
             break;
         case NodeTypeFnDef:
         case NodeTypeDirective:
@@ -1619,8 +1503,8 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
             zig_unreachable();
     }
 
-
-    satisfy_dep(g, node);
+    tld->resolution = TldResolutionOk;
+    tld->dep_loop_flag = false;
 }
 
 static FnTableEntry *get_context_fn_entry(BlockContext *context) {
@@ -1656,6 +1540,7 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) {
         case TypeTableEntryIdNumLitFloat:
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNamespace:
             return false;
 
         case TypeTableEntryIdBool:
@@ -2063,7 +1948,8 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
     BlockContext *context = allocate<BlockContext>(1);
     context->node = node;
     context->parent = parent;
-    context->variable_table.init(4);
+    context->decl_table.init(1);
+    context->var_table.init(1);
 
     if (parent) {
         context->parent_loop_node = parent->parent_loop_node;
@@ -2085,23 +1971,28 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
     return context;
 }
 
-static VariableTableEntry *find_variable(BlockContext *context, Buf *name, bool local_only) {
-    while (context && (!local_only || context->fn_entry)) {
-        auto entry = context->variable_table.maybe_get(name);
-        if (entry)
+static AstNode *find_decl(BlockContext *context, Buf *name) {
+    while (context) {
+        auto entry = context->decl_table.maybe_get(name);
+        if (entry) {
             return entry->value;
-
+        }
         context = context->parent;
     }
     return nullptr;
 }
 
-static TypeTableEntry *find_container(ImportTableEntry *import, Buf *name) {
-    auto entry = import->type_table.maybe_get(name);
-    if (entry)
-        return entry->value;
-    else
-        return nullptr;
+static VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, Buf *name) {
+    BlockContext *context = orig_context;
+    while (context) {
+        auto entry = context->var_table.maybe_get(name);
+        if (entry) {
+            return entry->value;
+        }
+        context = context->parent;
+    }
+
+    return nullptr;
 }
 
 static TypeEnumField *get_enum_field(TypeTableEntry *enum_type, Buf *name) {
@@ -2155,6 +2046,7 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp
 
 static TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
     assert(type_entry->id == TypeTableEntryIdStruct);
+    assert(type_entry->data.structure.complete);
     for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
         TypeStructField *field = &type_entry->data.structure.fields[i];
         if (buf_eql_buf(field->name, name)) {
@@ -2330,8 +2222,8 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
 {
     assert(node->type == NodeTypeFieldAccessExpr);
 
-    AstNode *struct_expr_node = node->data.field_access_expr.struct_expr;
-    TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, struct_expr_node);
+    AstNode **struct_expr_node = &node->data.field_access_expr.struct_expr;
+    TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, *struct_expr_node);
     Buf *field_name = &node->data.field_access_expr.field_name;
 
     bool wrapped_in_fn_call = node->data.field_access_expr.is_fn_call;
@@ -2342,17 +2234,30 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
         TypeTableEntry *bare_struct_type = (struct_type->id == TypeTableEntryIdStruct) ?
             struct_type : struct_type->data.pointer.child_type;
 
+        if (!bare_struct_type->data.structure.complete) {
+            resolve_struct_type(g, bare_struct_type->data.structure.decl_node->owner, bare_struct_type);
+        }
+
         node->data.field_access_expr.bare_struct_type = bare_struct_type;
         node->data.field_access_expr.type_struct_field = find_struct_type_field(bare_struct_type, field_name);
         if (node->data.field_access_expr.type_struct_field) {
             return node->data.field_access_expr.type_struct_field->type_entry;
         } else if (wrapped_in_fn_call) {
-            auto table_entry = bare_struct_type->data.structure.fn_table.maybe_get(field_name);
-            if (table_entry) {
+            BlockContext *container_block_context = get_container_block_context(bare_struct_type);
+            auto entry = container_block_context->decl_table.maybe_get(field_name);
+            AstNode *fn_decl_node = entry ? entry->value : nullptr;
+            if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) {
+                resolve_top_level_decl(g, fn_decl_node, false);
+                TopLevelDecl *tld = get_as_top_level_decl(fn_decl_node);
+                if (tld->resolution == TldResolutionInvalid) {
+                    return g->builtin_types.entry_invalid;
+                }
+
                 node->data.field_access_expr.is_member_fn = true;
-                return resolve_expr_const_val_as_fn(g, node, context, table_entry->value);
+                FnTableEntry *fn_entry = fn_decl_node->data.fn_proto.fn_table_entry;
+                return resolve_expr_const_val_as_fn(g, node, fn_entry);
             } else {
-                add_node_error(g, node, buf_sprintf("no member named '%s' in '%s'",
+                add_node_error(g, node, buf_sprintf("no function named '%s' in '%s'",
                     buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
                 return g->builtin_types.entry_invalid;
             }
@@ -2372,7 +2277,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
             return g->builtin_types.entry_invalid;
         }
     } else if (struct_type->id == TypeTableEntryIdMetaType) {
-        TypeTableEntry *child_type = resolve_type(g, struct_expr_node);
+        TypeTableEntry *child_type = resolve_type(g, *struct_expr_node);
 
         if (child_type->id == TypeTableEntryIdInvalid) {
             return g->builtin_types.entry_invalid;
@@ -2381,12 +2286,15 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
         } else if (child_type->id == TypeTableEntryIdEnum) {
             return analyze_enum_value_expr(g, import, context, node, nullptr, child_type, field_name);
         } else if (child_type->id == TypeTableEntryIdStruct) {
-            auto entry = child_type->data.structure.fn_table.maybe_get(field_name);
-            if (entry) {
-                return resolve_expr_const_val_as_fn(g, node, context, entry->value);
+            BlockContext *container_block_context = get_container_block_context(child_type);
+            auto entry = container_block_context->decl_table.maybe_get(field_name);
+            AstNode *decl_node = entry ? entry->value : nullptr;
+            if (decl_node) {
+                bool pointer_only = false;
+                return analyze_decl_ref(g, node, decl_node, pointer_only);
             } else {
                 add_node_error(g, node,
-                    buf_sprintf("struct '%s' has no function called '%s'",
+                    buf_sprintf("container '%s' has no member called '%s'",
                         buf_ptr(&child_type->name), buf_ptr(field_name)));
                 return g->builtin_types.entry_invalid;
             }
@@ -2397,6 +2305,26 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
                 buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name)));
             return g->builtin_types.entry_invalid;
         }
+    } else if (struct_type->id == TypeTableEntryIdNamespace) {
+        ConstExprValue *const_val = &get_resolved_expr(*struct_expr_node)->const_val;
+        assert(const_val->ok);
+        ImportTableEntry *namespace_import = const_val->data.x_import;
+        AstNode *decl_node = find_decl(namespace_import->block_context, field_name);
+        if (decl_node) {
+            TopLevelDecl *tld = get_as_top_level_decl(decl_node);
+            if (tld->visib_mod == VisibModPrivate) {
+                ErrorMsg *msg = add_node_error(g, node,
+                    buf_sprintf("'%s' is private", buf_ptr(field_name)));
+                add_error_note(g, msg, decl_node, buf_sprintf("declared here"));
+            }
+            bool pointer_only = false;
+            return analyze_decl_ref(g, node, decl_node, pointer_only);
+        } else {
+            add_node_error(g, node,
+                buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name),
+                    buf_ptr(namespace_import->path)));
+            return g->builtin_types.entry_invalid;
+        }
     } else {
         if (struct_type->id != TypeTableEntryIdInvalid) {
             add_node_error(g, node,
@@ -2500,12 +2428,7 @@ static TypeTableEntry *resolve_expr_const_val_as_other_expr(CodeGen *g, AstNode
     return other_expr->type_entry;
 }
 
-static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, BlockContext *context,
-        FnTableEntry *fn)
-{
-    if (!context->codegen_excluded) {
-        fn->ref_count += 1;
-    }
+static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn) {
     Expr *expr = get_resolved_expr(node);
     expr->const_val.ok = true;
     expr->const_val.data.x_fn = fn;
@@ -2635,7 +2558,7 @@ static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode *
 static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
         BlockContext *context, AstNode *node, Buf *err_name)
 {
-    auto err_table_entry = import->error_table.maybe_get(err_name);
+    auto err_table_entry = g->error_table.maybe_get(err_name);
 
     if (err_table_entry) {
         return resolve_expr_const_val_as_err(g, node, err_table_entry->value);
@@ -2647,9 +2570,48 @@ static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *
     return g->builtin_types.entry_invalid;
 }
 
+static TypeTableEntry *analyze_var_ref(CodeGen *g, AstNode *source_node, VariableTableEntry *var) {
+    get_resolved_expr(source_node)->variable = var;
+    if (var->is_const) {
+        AstNode *decl_node = var->decl_node;
+        if (decl_node->type == NodeTypeVariableDeclaration) {
+            AstNode *expr_node = decl_node->data.variable_declaration.expr;
+            ConstExprValue *other_const_val = &get_resolved_expr(expr_node)->const_val;
+            if (other_const_val->ok) {
+                return resolve_expr_const_val_as_other_expr(g, source_node, expr_node);
+            }
+        }
+    }
+    return var->type;
+}
+
+static TypeTableEntry *analyze_decl_ref(CodeGen *g, AstNode *source_node, AstNode *decl_node,
+        bool pointer_only)
+{
+    resolve_top_level_decl(g, decl_node, pointer_only);
+    TopLevelDecl *tld = get_as_top_level_decl(decl_node);
+    if (tld->resolution == TldResolutionInvalid) {
+        return g->builtin_types.entry_invalid;
+    }
+
+    if (decl_node->type == NodeTypeVariableDeclaration) {
+        VariableTableEntry *var = decl_node->data.variable_declaration.variable;
+        return analyze_var_ref(g, source_node, var);
+    } else if (decl_node->type == NodeTypeFnProto) {
+        FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry;
+        assert(fn_entry->type_entry);
+        return resolve_expr_const_val_as_fn(g, source_node, fn_entry);
+    } else if (decl_node->type == NodeTypeStructDecl) {
+        return resolve_expr_const_val_as_type(g, source_node, decl_node->data.struct_decl.type_entry);
+    } else if (decl_node->type == NodeTypeTypeDecl) {
+        return resolve_expr_const_val_as_type(g, source_node, decl_node->data.type_decl.child_type_entry);
+    } else {
+        zig_unreachable();
+    }
+}
 
 static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        TypeTableEntry *expected_type, AstNode *node)
+        TypeTableEntry *expected_type, AstNode *node, bool pointer_only)
 {
     if (node->data.symbol_expr.override_type_entry) {
         return resolve_expr_const_val_as_type(g, node, node->data.symbol_expr.override_type_entry);
@@ -2662,32 +2624,14 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import,
         return resolve_expr_const_val_as_type(g, node, primitive_table_entry->value);
     }
 
-    VariableTableEntry *var = find_variable(context, variable_name, false);
+    VariableTableEntry *var = find_variable(g, context, variable_name);
     if (var) {
-        node->data.symbol_expr.variable = var;
-        if (var->is_const) {
-            AstNode *decl_node = var->decl_node;
-            if (decl_node->type == NodeTypeVariableDeclaration) {
-                AstNode *expr_node = decl_node->data.variable_declaration.expr;
-                ConstExprValue *other_const_val = &get_resolved_expr(expr_node)->const_val;
-                if (other_const_val->ok) {
-                    return resolve_expr_const_val_as_other_expr(g, node, expr_node);
-                }
-            }
-        }
-        return var->type;
-    }
-
-    TypeTableEntry *container_type = find_container(import, variable_name);
-    if (container_type) {
-        return resolve_expr_const_val_as_type(g, node, container_type);
+        return analyze_var_ref(g, node, var);
     }
 
-    auto fn_table_entry = import->fn_table.maybe_get(variable_name);
-    if (fn_table_entry) {
-        assert(fn_table_entry->value->type_entry);
-        node->data.symbol_expr.fn_entry = fn_table_entry->value;
-        return resolve_expr_const_val_as_fn(g, node, context, fn_table_entry->value);
+    AstNode *decl_node = find_decl(context, variable_name);
+    if (decl_node) {
+        return analyze_decl_ref(g, node, decl_node, pointer_only);
     }
 
     if (import->any_imports_failed) {
@@ -2760,18 +2704,21 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc
     TypeTableEntry *expected_rhs_type = nullptr;
     lhs_node->block_context = block_context;
     if (lhs_node->type == NodeTypeSymbol) {
-        Buf *name = &lhs_node->data.symbol_expr.symbol;
-        if (purpose == LValPurposeAddressOf) {
-            expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node);
-        } else {
-            VariableTableEntry *var = find_variable(block_context, name, false);
+        bool pointer_only = purpose == LValPurposeAddressOf;
+        expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node, pointer_only);
+        if (expected_rhs_type->id == TypeTableEntryIdInvalid) {
+            return g->builtin_types.entry_invalid;
+        }
+        if (purpose != LValPurposeAddressOf) {
+            Buf *name = &lhs_node->data.symbol_expr.symbol;
+            VariableTableEntry *var = find_variable(g, block_context, name);
             if (var) {
                 if (var->is_const) {
                     add_node_error(g, lhs_node, buf_sprintf("cannot assign to constant"));
                     expected_rhs_type = g->builtin_types.entry_invalid;
                 } else {
                     expected_rhs_type = var->type;
-                    lhs_node->data.symbol_expr.variable = var;
+                    get_resolved_expr(lhs_node)->variable = var;
                 }
             } else {
                 add_node_error(g, lhs_node,
@@ -3176,29 +3123,36 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Impor
 
     if (name) {
         buf_init_from_buf(&variable_entry->name, name);
-        VariableTableEntry *existing_var;
 
-        existing_var = find_variable(context, name, context->fn_entry != nullptr);
-
-        if (existing_var) {
-            add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
-            variable_entry->type = g->builtin_types.entry_invalid;
-        } else {
-            auto primitive_table_entry = g->primitive_type_table.maybe_get(name);
-            TypeTableEntry *type;
-            if (primitive_table_entry) {
-                type = primitive_table_entry->value;
-            } else {
-                type = find_container(import, name);
-            }
-            if (type) {
-                add_node_error(g, source_node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
+        if (type_entry->id != TypeTableEntryIdInvalid) {
+            VariableTableEntry *existing_var = find_variable(g, context, name);
+            if (existing_var) {
+                ErrorMsg *msg = add_node_error(g, source_node,
+                        buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
+                add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration is here"));
                 variable_entry->type = g->builtin_types.entry_invalid;
+            } else {
+                auto primitive_table_entry = g->primitive_type_table.maybe_get(name);
+                if (primitive_table_entry) {
+                    TypeTableEntry *type = primitive_table_entry->value;
+                    add_node_error(g, source_node,
+                            buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name)));
+                    variable_entry->type = g->builtin_types.entry_invalid;
+                } else {
+                    AstNode *decl_node = find_decl(context, name);
+                    if (decl_node && decl_node->type != NodeTypeVariableDeclaration) {
+                        ErrorMsg *msg = add_node_error(g, source_node,
+                                buf_sprintf("redefinition of '%s'", buf_ptr(name)));
+                        add_error_note(g, msg, decl_node, buf_sprintf("previous definition is here"));
+                        variable_entry->type = g->builtin_types.entry_invalid;
+                    }
+                }
             }
         }
 
-        context->variable_table.put(&variable_entry->name, variable_entry);
+        context->var_table.put(&variable_entry->name, variable_entry);
     } else {
+        // TODO replace _anon with @anon and make sure all tests still pass
         buf_init_from_str(&variable_entry->name, "_anon");
     }
     if (context->fn_entry) {
@@ -3248,10 +3202,10 @@ static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *i
 static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import,
         BlockContext *context, AstNode *source_node,
         AstNodeVariableDeclaration *variable_declaration,
-        bool expr_is_maybe)
+        bool expr_is_maybe, AstNode *decl_node)
 {
     bool is_const = variable_declaration->is_const;
-    bool is_export = (variable_declaration->visib_mod == VisibModExport);
+    bool is_export = (variable_declaration->top_level_decl.visib_mod == VisibModExport);
     bool is_extern = variable_declaration->is_extern;
 
     TypeTableEntry *explicit_type = nullptr;
@@ -3312,22 +3266,6 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
 
     variable_declaration->variable = var;
 
-
-    bool is_pub = (variable_declaration->visib_mod != VisibModPrivate);
-    if (is_pub) {
-        for (int i = 0; i < import->importers.length; i += 1) {
-            ImporterInfo importer = import->importers.at(i);
-            auto table_entry = importer.import->block_context->variable_table.maybe_get(&var->name);
-            if (table_entry) {
-                add_node_error(g, importer.source_node,
-                    buf_sprintf("import of variable '%s' overrides existing definition",
-                        buf_ptr(&var->name)));
-            } else {
-                importer.import->block_context->variable_table.put(&var->name, var);
-            }
-        }
-    }
-
     return var;
 }
 
@@ -3335,7 +3273,7 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE
         BlockContext *context, TypeTableEntry *expected_type, AstNode *node)
 {
     AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
-    return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, false);
+    return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, false, nullptr);
 }
 
 static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *import,
@@ -3516,7 +3454,8 @@ static TypeTableEntry *analyze_for_expr(CodeGen *g, ImportTableEntry *import, Bl
     AstNode *elem_var_node = node->data.for_expr.elem_node;
     elem_var_node->block_context = child_context;
     Buf *elem_var_name = &elem_var_node->data.symbol_expr.symbol;
-    node->data.for_expr.elem_var = add_local_var(g, elem_var_node, import, child_context, elem_var_name, child_type, true);
+    node->data.for_expr.elem_var = add_local_var(g, elem_var_node, import, child_context, elem_var_name,
+            child_type, true);
 
     AstNode *index_var_node = node->data.for_expr.index_node;
     if (index_var_node) {
@@ -3673,7 +3612,8 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import,
 
     BlockContext *child_context = new_block_context(node, parent_context);
 
-    analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true);
+    analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true,
+        nullptr);
     VariableTableEntry *var = node->data.if_var_expr.var_decl.variable;
     if (var->type->id == TypeTableEntryIdInvalid) {
         return g->builtin_types.entry_invalid;
@@ -4069,39 +4009,178 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
     return g->builtin_types.entry_invalid;
 }
 
-static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        TypeTableEntry *expected_type, AstNode *node)
+static TypeTableEntry *resolve_expr_const_val_as_import(CodeGen *g, AstNode *node, ImportTableEntry *import) {
+    Expr *expr = get_resolved_expr(node);
+    expr->const_val.ok = true;
+    expr->const_val.data.x_import = import;
+    return g->builtin_types.entry_namespace;
+}
+
+static TypeTableEntry *analyze_import(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        AstNode *node)
 {
     assert(node->type == NodeTypeFnCallExpr);
 
-    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-    Buf *name = &fn_ref_expr->data.symbol_expr.symbol;
-
-    auto entry = g->builtin_fn_table.maybe_get(name);
+    if (context != import->block_context) {
+        add_node_error(g, node, buf_sprintf("@import valid only at top level scope"));
+        return g->builtin_types.entry_invalid;
+    }
 
-    if (!entry) {
-        add_node_error(g, node,
-                buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
+    AstNode *first_param_node = node->data.fn_call_expr.params.at(0);
+    Buf *import_target_str = resolve_const_expr_str(g, import, context, first_param_node->parent_field);
+    if (!import_target_str) {
         return g->builtin_types.entry_invalid;
     }
 
-    BuiltinFnEntry *builtin_fn = entry->value;
-    int actual_param_count = node->data.fn_call_expr.params.length;
+    Buf *import_target_path;
+    Buf *search_dir;
+    assert(import->package);
+    PackageTableEntry *target_package;
+    auto package_entry = import->package->package_table.maybe_get(import_target_str);
+    if (package_entry) {
+        target_package = package_entry->value;
+        import_target_path = &target_package->root_src_path;
+        search_dir = &target_package->root_src_dir;
+    } else {
+        // try it as a filename
+        target_package = import->package;
+        import_target_path = import_target_str;
+        search_dir = &import->package->root_src_dir;
+    }
 
-    node->data.fn_call_expr.builtin_fn = builtin_fn;
+    Buf full_path = BUF_INIT;
+    os_path_join(search_dir, import_target_path, &full_path);
 
-    if (builtin_fn->param_count != actual_param_count) {
-        add_node_error(g, node,
-                buf_sprintf("expected %d arguments, got %d",
-                    builtin_fn->param_count, actual_param_count));
+    Buf *import_code = buf_alloc();
+    Buf *abs_full_path = buf_alloc();
+    int err;
+    if ((err = os_path_real(&full_path, abs_full_path))) {
+        if (err == ErrorFileNotFound) {
+            add_node_error(g, node,
+                    buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
+            return g->builtin_types.entry_invalid;
+        } else {
+            g->error_during_imports = true;
+            add_node_error(g, node,
+                    buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
+            return g->builtin_types.entry_invalid;
+        }
+    }
+
+    auto import_entry = g->import_table.maybe_get(abs_full_path);
+    if (import_entry) {
+        return resolve_expr_const_val_as_import(g, node, import_entry->value);
+    }
+
+    if ((err = os_fetch_file_path(abs_full_path, import_code))) {
+        if (err == ErrorFileNotFound) {
+            add_node_error(g, node,
+                    buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
+            return g->builtin_types.entry_invalid;
+        } else {
+            add_node_error(g, node,
+                    buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
+            return g->builtin_types.entry_invalid;
+        }
+    }
+    ImportTableEntry *target_import = add_source_file(g, target_package,
+            abs_full_path, search_dir, import_target_path, import_code);
+
+    scan_decls(g, target_import, target_import->block_context, target_import->root);
+
+    return resolve_expr_const_val_as_import(g, node, target_import);
+}
+
+static TypeTableEntry *analyze_c_import(CodeGen *g, ImportTableEntry *parent_import,
+        BlockContext *parent_context, AstNode *node)
+{
+    assert(node->type == NodeTypeFnCallExpr);
+
+    if (parent_context != parent_import->block_context) {
+        add_node_error(g, node, buf_sprintf("@c_import valid only at top level scope"));
         return g->builtin_types.entry_invalid;
     }
 
-    builtin_fn->ref_count += 1;
+    AstNode *block_node = node->data.fn_call_expr.params.at(0);
 
-    switch (builtin_fn->id) {
-        case BuiltinFnIdInvalid:
-            zig_unreachable();
+    BlockContext *child_context = new_block_context(node, parent_context);
+    child_context->c_import_buf = buf_alloc();
+
+    TypeTableEntry *resolved_type = analyze_expression(g, parent_import, child_context,
+            g->builtin_types.entry_void, block_node);
+
+    if (resolved_type->id == TypeTableEntryIdInvalid) {
+        return resolved_type;
+    }
+
+    find_libc_include_path(g);
+
+    ImportTableEntry *child_import = allocate<ImportTableEntry>(1);
+    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, node))) {
+        zig_panic("unable to parse h file: %s\n", err_str(err));
+    }
+
+    if (errors.length > 0) {
+        ErrorMsg *parent_err_msg = add_node_error(g, node, buf_sprintf("C import failed"));
+        for (int i = 0; i < errors.length; i += 1) {
+            ErrorMsg *err_msg = errors.at(i);
+            err_msg_add_note(parent_err_msg, err_msg);
+        }
+
+        return g->builtin_types.entry_invalid;
+    }
+
+    if (g->verbose) {
+        fprintf(stderr, "\nc_import:\n");
+        fprintf(stderr, "-----------\n");
+        ast_render(stderr, child_import->root, 4);
+    }
+
+    child_import->di_file = parent_import->di_file;
+    child_import->block_context = new_block_context(child_import->root, nullptr);
+
+    scan_decls(g, child_import, child_import->block_context, child_import->root);
+    return resolve_expr_const_val_as_import(g, node, child_import);
+}
+
+static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node)
+{
+    assert(node->type == NodeTypeFnCallExpr);
+
+    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
+    Buf *name = &fn_ref_expr->data.symbol_expr.symbol;
+
+    auto entry = g->builtin_fn_table.maybe_get(name);
+
+    if (!entry) {
+        add_node_error(g, node,
+                buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
+        return g->builtin_types.entry_invalid;
+    }
+
+    BuiltinFnEntry *builtin_fn = entry->value;
+    int actual_param_count = node->data.fn_call_expr.params.length;
+
+    node->data.fn_call_expr.builtin_fn = builtin_fn;
+
+    if (builtin_fn->param_count != actual_param_count) {
+        add_node_error(g, node,
+                buf_sprintf("expected %d arguments, got %d",
+                    builtin_fn->param_count, actual_param_count));
+        return g->builtin_types.entry_invalid;
+    }
+
+    builtin_fn->ref_count += 1;
+
+    switch (builtin_fn->id) {
+        case BuiltinFnIdInvalid:
+            zig_unreachable();
         case BuiltinFnIdAddWithOverflow:
         case BuiltinFnIdSubWithOverflow:
         case BuiltinFnIdMulWithOverflow:
@@ -4252,6 +4331,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
                     case TypeTableEntryIdNumLitFloat:
                     case TypeTableEntryIdNumLitInt:
                     case TypeTableEntryIdUndefLit:
+                    case TypeTableEntryIdNamespace:
                         add_node_error(g, expr_node,
                                 buf_sprintf("type '%s' not eligible for @typeof", buf_ptr(&type_entry->name)));
                         return g->builtin_types.entry_invalid;
@@ -4391,6 +4471,10 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
                     return g->builtin_types.entry_invalid;
                 }
             }
+        case BuiltinFnIdImport:
+            return analyze_import(g, import, context, node);
+        case BuiltinFnIdCImport:
+            return analyze_c_import(g, import, context, node);
 
     }
     zig_unreachable();
@@ -4456,12 +4540,7 @@ static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import,
 
     node->data.fn_call_expr.fn_entry = fn_table_entry;
 
-    if (!context->codegen_excluded) {
-        fn_table_entry->ref_count += 1;
-    }
-
     return analyze_fn_call_ptr(g, import, context, expected_type, node, fn_table_entry->type_entry, struct_type);
-
 }
 
 static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
@@ -4515,10 +4594,16 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
                     }
                 } else if (child_type->id == TypeTableEntryIdStruct) {
                     Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name;
-                    auto entry = child_type->data.structure.fn_table.maybe_get(field_name);
-                    if (entry) {
-                        return analyze_fn_call_raw(g, import, context, expected_type, node,
-                                entry->value, nullptr);
+                    BlockContext *container_block_context = get_container_block_context(child_type);
+                    auto entry = container_block_context->decl_table.maybe_get(field_name);
+                    AstNode *decl_node = entry ? entry->value : nullptr;
+                    if (decl_node && decl_node->type == NodeTypeFnProto) {
+                        bool pointer_only = false;
+                        resolve_top_level_decl(g, decl_node, pointer_only);
+
+                        FnTableEntry *fn_entry = decl_node->data.fn_proto.fn_table_entry;
+                        assert(fn_entry);
+                        return analyze_fn_call_raw(g, import, context, expected_type, node, fn_entry, nullptr);
                     } else {
                         add_node_error(g, node,
                             buf_sprintf("struct '%s' has no function called '%s'",
@@ -4565,19 +4650,19 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
         TypeTableEntry *expected_type, AstNode *node)
 {
     PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op;
-    AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
+    AstNode **expr_node = &node->data.prefix_op_expr.primary_expr;
     switch (prefix_op) {
         case PrefixOpInvalid:
             zig_unreachable();
         case PrefixOpBoolNot:
             {
                 TypeTableEntry *type_entry = analyze_expression(g, import, context, g->builtin_types.entry_bool,
-                        expr_node);
+                        *expr_node);
                 if (type_entry->id == TypeTableEntryIdInvalid) {
                     return g->builtin_types.entry_bool;
                 }
 
-                ConstExprValue *target_const_val = &get_resolved_expr(expr_node)->const_val;
+                ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val;
                 if (!target_const_val->ok) {
                     return g->builtin_types.entry_bool;
                 }
@@ -4588,7 +4673,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
         case PrefixOpBinNot:
             {
                 TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type,
-                        expr_node);
+                        *expr_node);
                 if (expr_type->id == TypeTableEntryIdInvalid) {
                     return expr_type;
                 } else if (expr_type->id == TypeTableEntryIdInt ||
@@ -4596,7 +4681,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                 {
                     return expr_type;
                 } else {
-                    add_node_error(g, expr_node, buf_sprintf("invalid binary not type: '%s'",
+                    add_node_error(g, *expr_node, buf_sprintf("invalid binary not type: '%s'",
                             buf_ptr(&expr_type->name)));
                     return g->builtin_types.entry_invalid;
                 }
@@ -4604,8 +4689,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
             }
         case PrefixOpNegation:
             {
-                TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type,
-                        expr_node);
+                TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type, *expr_node);
                 if (expr_type->id == TypeTableEntryIdInvalid) {
                     return expr_type;
                 } else if ((expr_type->id == TypeTableEntryIdInt &&
@@ -4614,7 +4698,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                             expr_type->id == TypeTableEntryIdNumLitInt ||
                             expr_type->id == TypeTableEntryIdNumLitFloat)
                 {
-                    ConstExprValue *target_const_val = &get_resolved_expr(expr_node)->const_val;
+                    ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val;
                     if (!target_const_val->ok) {
                         return expr_type;
                     }
@@ -4635,12 +4719,13 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                 bool is_const = (prefix_op == PrefixOpConstAddressOf);
 
                 TypeTableEntry *child_type = analyze_lvalue(g, import, context,
-                        expr_node, LValPurposeAddressOf, is_const);
+                        *expr_node, LValPurposeAddressOf, is_const);
 
                 if (child_type->id == TypeTableEntryIdInvalid) {
                     return g->builtin_types.entry_invalid;
                 } else if (child_type->id == TypeTableEntryIdMetaType) {
-                    TypeTableEntry *meta_type = analyze_type_expr(g, import, context, expr_node);
+                    TypeTableEntry *meta_type = analyze_type_expr_pointer_only(g, import, context,
+                            *expr_node, true);
                     if (meta_type->id == TypeTableEntryIdInvalid) {
                         return g->builtin_types.entry_invalid;
                     } else if (meta_type->id == TypeTableEntryIdUnreachable) {
@@ -4653,7 +4738,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                 } else if (child_type->id == TypeTableEntryIdNumLitInt ||
                            child_type->id == TypeTableEntryIdNumLitFloat)
                 {
-                    add_node_error(g, expr_node,
+                    add_node_error(g, *expr_node,
                         buf_sprintf("unable to get address of type '%s'", buf_ptr(&child_type->name)));
                     return g->builtin_types.entry_invalid;
                 } else {
@@ -4662,13 +4747,13 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
             }
         case PrefixOpDereference:
             {
-                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
                 if (type_entry->id == TypeTableEntryIdInvalid) {
                     return type_entry;
                 } else if (type_entry->id == TypeTableEntryIdPointer) {
                     return type_entry->data.pointer.child_type;
                 } else {
-                    add_node_error(g, expr_node,
+                    add_node_error(g, *expr_node,
                         buf_sprintf("indirection requires pointer operand ('%s' invalid)",
                             buf_ptr(&type_entry->name)));
                     return g->builtin_types.entry_invalid;
@@ -4676,12 +4761,12 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
             }
         case PrefixOpMaybe:
             {
-                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
 
                 if (type_entry->id == TypeTableEntryIdInvalid) {
                     return type_entry;
                 } else if (type_entry->id == TypeTableEntryIdMetaType) {
-                    TypeTableEntry *meta_type = resolve_type(g, expr_node);
+                    TypeTableEntry *meta_type = resolve_type(g, *expr_node);
                     if (meta_type->id == TypeTableEntryIdInvalid) {
                         return g->builtin_types.entry_invalid;
                     } else if (meta_type->id == TypeTableEntryIdUnreachable) {
@@ -4691,10 +4776,10 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                         return resolve_expr_const_val_as_type(g, node, get_maybe_type(g, meta_type));
                     }
                 } else if (type_entry->id == TypeTableEntryIdUnreachable) {
-                    add_node_error(g, expr_node, buf_sprintf("unable to wrap unreachable in maybe type"));
+                    add_node_error(g, *expr_node, buf_sprintf("unable to wrap unreachable in maybe type"));
                     return g->builtin_types.entry_invalid;
                 } else {
-                    ConstExprValue *target_const_val = &get_resolved_expr(expr_node)->const_val;
+                    ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val;
                     TypeTableEntry *maybe_type = get_maybe_type(g, type_entry);
                     if (!target_const_val->ok) {
                         return maybe_type;
@@ -4704,12 +4789,12 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
             }
         case PrefixOpError:
             {
-                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
 
                 if (type_entry->id == TypeTableEntryIdInvalid) {
                     return type_entry;
                 } else if (type_entry->id == TypeTableEntryIdMetaType) {
-                    TypeTableEntry *meta_type = resolve_type(g, expr_node);
+                    TypeTableEntry *meta_type = resolve_type(g, *expr_node);
                     if (meta_type->id == TypeTableEntryIdInvalid) {
                         return meta_type;
                     } else if (meta_type->id == TypeTableEntryIdUnreachable) {
@@ -4719,7 +4804,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                         return resolve_expr_const_val_as_type(g, node, get_error_type(g, meta_type));
                     }
                 } else if (type_entry->id == TypeTableEntryIdUnreachable) {
-                    add_node_error(g, expr_node, buf_sprintf("unable to wrap unreachable in error type"));
+                    add_node_error(g, *expr_node, buf_sprintf("unable to wrap unreachable in error type"));
                     return g->builtin_types.entry_invalid;
                 } else {
                     // TODO eval const expr
@@ -4729,28 +4814,28 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
             }
         case PrefixOpUnwrapError:
             {
-                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
 
                 if (type_entry->id == TypeTableEntryIdInvalid) {
                     return type_entry;
                 } else if (type_entry->id == TypeTableEntryIdErrorUnion) {
                     return type_entry->data.error.child_type;
                 } else {
-                    add_node_error(g, expr_node,
+                    add_node_error(g, *expr_node,
                         buf_sprintf("expected error type, got '%s'", buf_ptr(&type_entry->name)));
                     return g->builtin_types.entry_invalid;
                 }
             }
         case PrefixOpUnwrapMaybe:
             {
-                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
 
                 if (type_entry->id == TypeTableEntryIdInvalid) {
                     return type_entry;
                 } else if (type_entry->id == TypeTableEntryIdMaybe) {
                     return type_entry->data.maybe.child_type;
                 } else {
-                    add_node_error(g, expr_node,
+                    add_node_error(g, *expr_node,
                         buf_sprintf("expected maybe type, got '%s'", buf_ptr(&type_entry->name)));
                     return g->builtin_types.entry_invalid;
                 }
@@ -5094,7 +5179,7 @@ static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, Bl
             }
         } else {
             Buf *variable_name = &asm_output->variable_name;
-            VariableTableEntry *var = find_variable(context, variable_name, false);
+            VariableTableEntry *var = find_variable(g, context, variable_name);
             if (var) {
                 asm_output->variable = var;
                 return var->type;
@@ -5120,10 +5205,8 @@ static TypeTableEntry *analyze_goto(CodeGen *g, ImportTableEntry *import, BlockC
     return g->builtin_types.entry_unreachable;
 }
 
-// When you call analyze_expression, the node you pass might no longer be the child node
-// you thought it was due to implicit casting rewriting the AST.
-static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        TypeTableEntry *expected_type, AstNode *node)
+static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEntry *import,
+        BlockContext *context, TypeTableEntry *expected_type, AstNode *node, bool pointer_only)
 {
     assert(!expected_type || expected_type->id != TypeTableEntryIdInvalid);
     TypeTableEntry *return_type = nullptr;
@@ -5197,7 +5280,7 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import,
             return_type = analyze_undefined_literal_expr(g, import, context, expected_type, node);
             break;
         case NodeTypeSymbol:
-            return_type = analyze_symbol_expr(g, import, context, expected_type, node);
+            return_type = analyze_symbol_expr(g, import, context, expected_type, node, pointer_only);
             break;
         case NodeTypePrefixOpExpr:
             return_type = analyze_prefix_op_expr(g, import, context, expected_type, node);
@@ -5235,10 +5318,8 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import,
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
         case NodeTypeRoot:
-        case NodeTypeRootExportDecl:
         case NodeTypeFnDef:
-        case NodeTypeImport:
-        case NodeTypeCImport:
+        case NodeTypeUse:
         case NodeTypeLabel:
         case NodeTypeStructDecl:
         case NodeTypeStructField:
@@ -5263,7 +5344,17 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import,
     return resolved_type;
 }
 
-static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node) {
+// When you call analyze_expression, the node you pass might no longer be the child node
+// you thought it was due to implicit casting rewriting the AST.
+static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node)
+{
+    return analyze_expression_pointer_only(g, import, context, expected_type, node, false);
+}
+
+static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
+    ImportTableEntry *import = fn_table_entry->import_entry;
+    AstNode *node = fn_table_entry->fn_def_node;
     assert(node->type == NodeTypeFnDef);
 
     AstNode *fn_proto_node = node->data.fn_def.fn_proto;
@@ -5277,7 +5368,6 @@ 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;
     for (int i = 0; i < fn_proto->params.length; i += 1) {
@@ -5302,7 +5392,8 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
             add_node_error(g, param_decl_node, buf_sprintf("missing parameter name"));
         }
 
-        VariableTableEntry *var = add_local_var(g, param_decl_node, import, context, &param_decl->name, type, true);
+        VariableTableEntry *var = add_local_var(g, param_decl_node, import, context, &param_decl->name,
+                type, true);
         var->src_arg_index = i;
         param_decl_node->data.param_decl.variable = var;
 
@@ -5315,375 +5406,71 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
     node->data.fn_def.implicit_return_type = block_return_type;
 }
 
-static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
-    switch (node->type) {
-        case NodeTypeFnDef:
-            analyze_top_level_fn_def(g, import, node);
-            break;
-        case NodeTypeStructDecl:
-            {
-                for (int i = 0; i < node->data.struct_decl.fns.length; i += 1) {
-                    AstNode *fn_def_node = node->data.struct_decl.fns.at(i);
-                    analyze_top_level_fn_def(g, import, fn_def_node);
-                }
-                break;
-            }
-        case NodeTypeRootExportDecl:
-        case NodeTypeImport:
-        case NodeTypeCImport:
-        case NodeTypeVariableDeclaration:
-        case NodeTypeErrorValueDecl:
-        case NodeTypeFnProto:
-        case NodeTypeTypeDecl:
-            // already took care of these
-            break;
-        case NodeTypeDirective:
-        case NodeTypeParamDecl:
-        case NodeTypeFnDecl:
-        case NodeTypeReturnExpr:
-        case NodeTypeDefer:
-        case NodeTypeRoot:
-        case NodeTypeBlock:
-        case NodeTypeBinOpExpr:
-        case NodeTypeUnwrapErrorExpr:
-        case NodeTypeFnCallExpr:
-        case NodeTypeArrayAccessExpr:
-        case NodeTypeSliceExpr:
-        case NodeTypeNumberLiteral:
-        case NodeTypeStringLiteral:
-        case NodeTypeCharLiteral:
-        case NodeTypeBoolLiteral:
-        case NodeTypeNullLiteral:
-        case NodeTypeUndefinedLiteral:
-        case NodeTypeSymbol:
-        case NodeTypePrefixOpExpr:
-        case NodeTypeIfBoolExpr:
-        case NodeTypeIfVarExpr:
-        case NodeTypeWhileExpr:
-        case NodeTypeForExpr:
-        case NodeTypeSwitchExpr:
-        case NodeTypeSwitchProng:
-        case NodeTypeSwitchRange:
-        case NodeTypeLabel:
-        case NodeTypeGoto:
-        case NodeTypeBreak:
-        case NodeTypeContinue:
-        case NodeTypeAsmExpr:
-        case NodeTypeFieldAccessExpr:
-        case NodeTypeStructField:
-        case NodeTypeStructValueField:
-        case NodeTypeContainerInitExpr:
-        case NodeTypeArrayType:
-        case NodeTypeErrorType:
-        case NodeTypeTypeLiteral:
-            zig_unreachable();
+static void add_top_level_decl(CodeGen *g, ImportTableEntry *import, BlockContext *block_context,
+        AstNode *node, Buf *name)
+{
+    assert(import);
+
+    TopLevelDecl *tld = get_as_top_level_decl(node);
+    tld->import = import;
+    tld->name = name;
+
+    if (g->check_unused || g->is_test_build || tld->visib_mod == VisibModExport) {
+        g->export_queue.append(node);
     }
-}
 
-static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node,
-        TopLevelDecl *decl_node)
-{
-    switch (node->type) {
-        case NodeTypeNumberLiteral:
-        case NodeTypeStringLiteral:
-        case NodeTypeCharLiteral:
-        case NodeTypeBoolLiteral:
-        case NodeTypeNullLiteral:
-        case NodeTypeUndefinedLiteral:
-        case NodeTypeGoto:
-        case NodeTypeBreak:
-        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) {
-                    table_entry = import->type_table.maybe_get(name);
-                }
-                if (!table_entry || !type_is_complete(table_entry->value)) {
-                    decl_node->deps.put(name, node);
-                }
-                break;
-            }
-        case NodeTypeBinOpExpr:
-            collect_expr_decl_deps(g, import, node->data.bin_op_expr.op1, decl_node);
-            collect_expr_decl_deps(g, import, node->data.bin_op_expr.op2, decl_node);
-            break;
-        case NodeTypeUnwrapErrorExpr:
-            collect_expr_decl_deps(g, import, node->data.unwrap_err_expr.op1, decl_node);
-            collect_expr_decl_deps(g, import, node->data.unwrap_err_expr.op2, decl_node);
-            break;
-        case NodeTypeReturnExpr:
-            collect_expr_decl_deps(g, import, node->data.return_expr.expr, decl_node);
-            break;
-        case NodeTypeDefer:
-            collect_expr_decl_deps(g, import, node->data.defer.expr, decl_node);
-            break;
-        case NodeTypePrefixOpExpr:
-            collect_expr_decl_deps(g, import, node->data.prefix_op_expr.primary_expr, decl_node);
-            break;
-        case NodeTypeFnCallExpr:
-            if (!node->data.fn_call_expr.is_builtin) {
-                collect_expr_decl_deps(g, import, node->data.fn_call_expr.fn_ref_expr, decl_node);
-            }
-            for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
-                AstNode *arg_node = node->data.fn_call_expr.params.at(i);
-                collect_expr_decl_deps(g, import, arg_node, decl_node);
-            }
-            break;
-        case NodeTypeArrayAccessExpr:
-            collect_expr_decl_deps(g, import, node->data.array_access_expr.array_ref_expr, decl_node);
-            collect_expr_decl_deps(g, import, node->data.array_access_expr.subscript, decl_node);
-            break;
-        case NodeTypeSliceExpr:
-            collect_expr_decl_deps(g, import, node->data.slice_expr.array_ref_expr, decl_node);
-            collect_expr_decl_deps(g, import, node->data.slice_expr.start, decl_node);
-            if (node->data.slice_expr.end) {
-                collect_expr_decl_deps(g, import, node->data.slice_expr.end, decl_node);
-            }
-            break;
-        case NodeTypeFieldAccessExpr:
-            collect_expr_decl_deps(g, import, node->data.field_access_expr.struct_expr, decl_node);
-            break;
-        case NodeTypeIfBoolExpr:
-            collect_expr_decl_deps(g, import, node->data.if_bool_expr.condition, decl_node);
-            collect_expr_decl_deps(g, import, node->data.if_bool_expr.then_block, decl_node);
-            if (node->data.if_bool_expr.else_node) {
-                collect_expr_decl_deps(g, import, node->data.if_bool_expr.else_node, decl_node);
-            }
-            break;
-        case NodeTypeIfVarExpr:
-            if (node->data.if_var_expr.var_decl.type) {
-                collect_expr_decl_deps(g, import, node->data.if_var_expr.var_decl.type, decl_node);
-            }
-            if (node->data.if_var_expr.var_decl.expr) {
-                collect_expr_decl_deps(g, import, node->data.if_var_expr.var_decl.expr, decl_node);
-            }
-            collect_expr_decl_deps(g, import, node->data.if_var_expr.then_block, decl_node);
-            if (node->data.if_bool_expr.else_node) {
-                collect_expr_decl_deps(g, import, node->data.if_var_expr.else_node, decl_node);
-            }
-            break;
-        case NodeTypeWhileExpr:
-            collect_expr_decl_deps(g, import, node->data.while_expr.condition, decl_node);
-            collect_expr_decl_deps(g, import, node->data.while_expr.body, decl_node);
-            break;
-        case NodeTypeForExpr:
-            collect_expr_decl_deps(g, import, node->data.for_expr.array_expr, decl_node);
-            collect_expr_decl_deps(g, import, node->data.for_expr.body, decl_node);
-            break;
-        case NodeTypeBlock:
-            for (int i = 0; i < node->data.block.statements.length; i += 1) {
-                AstNode *stmt = node->data.block.statements.at(i);
-                collect_expr_decl_deps(g, import, stmt, decl_node);
-            }
-            break;
-        case NodeTypeAsmExpr:
-            for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) {
-                AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
-                if (asm_output->return_type) {
-                    collect_expr_decl_deps(g, import, asm_output->return_type, decl_node);
-                } else {
-                    decl_node->deps.put(&asm_output->variable_name, node);
-                }
-            }
-            for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1) {
-                AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
-                collect_expr_decl_deps(g, import, asm_input->expr, decl_node);
-            }
-            break;
-        case NodeTypeContainerInitExpr:
-            collect_expr_decl_deps(g, import, node->data.container_init_expr.type, decl_node);
-            for (int i = 0; i < node->data.container_init_expr.entries.length; i += 1) {
-                AstNode *child_node = node->data.container_init_expr.entries.at(i);
-                collect_expr_decl_deps(g, import, child_node, decl_node);
-            }
-            break;
-        case NodeTypeStructValueField:
-            collect_expr_decl_deps(g, import, node->data.struct_val_field.expr, decl_node);
-            break;
-        case NodeTypeArrayType:
-            if (node->data.array_type.size) {
-                collect_expr_decl_deps(g, import, node->data.array_type.size, decl_node);
-            }
-            collect_expr_decl_deps(g, import, node->data.array_type.child_type, decl_node);
-            break;
-        case NodeTypeSwitchExpr:
-            collect_expr_decl_deps(g, import, node->data.switch_expr.expr, decl_node);
-            for (int i = 0; i < node->data.switch_expr.prongs.length; i += 1) {
-                AstNode *prong = node->data.switch_expr.prongs.at(i);
-                collect_expr_decl_deps(g, import, prong, decl_node);
-            }
-            break;
-        case NodeTypeSwitchProng:
-            for (int i = 0; i < node->data.switch_prong.items.length; i += 1) {
-                AstNode *child = node->data.switch_prong.items.at(i);
-                collect_expr_decl_deps(g, import, child, decl_node);
-            }
-            collect_expr_decl_deps(g, import, node->data.switch_prong.expr, decl_node);
-            break;
-        case NodeTypeSwitchRange:
-            collect_expr_decl_deps(g, import, node->data.switch_range.start, decl_node);
-            collect_expr_decl_deps(g, import, node->data.switch_range.end, decl_node);
-            break;
-        case NodeTypeFnProto:
-            // remember that fn proto node is used for function definitions as well
-            // as types
-            for (int i = 0; i < node->data.fn_proto.params.length; i += 1) {
-                AstNode *param = node->data.fn_proto.params.at(i);
-                collect_expr_decl_deps(g, import, param, decl_node);
-            }
-            if (node->data.fn_proto.directives) {
-                for (int i = 0; i < node->data.fn_proto.directives->length; i += 1) {
-                    AstNode *directive = node->data.fn_proto.directives->at(i);
-                    collect_expr_decl_deps(g, import, directive, decl_node);
-                }
-            }
-            collect_expr_decl_deps(g, import, node->data.fn_proto.return_type, decl_node);
-            break;
-        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 NodeTypeDirective:
-            collect_expr_decl_deps(g, import, node->data.directive.expr, decl_node);
-            break;
-        case NodeTypeVariableDeclaration:
-        case NodeTypeRootExportDecl:
-        case NodeTypeFnDef:
-        case NodeTypeRoot:
-        case NodeTypeFnDecl:
-        case NodeTypeImport:
-        case NodeTypeCImport:
-        case NodeTypeLabel:
-        case NodeTypeStructDecl:
-        case NodeTypeStructField:
-            zig_unreachable();
+    node->block_context = block_context;
+
+    auto entry = block_context->decl_table.maybe_get(name);
+    if (entry) {
+        AstNode *other_decl_node = entry->value;
+        ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(name)));
+        add_error_note(g, msg, other_decl_node, buf_sprintf("previous definition is here"));
+    } else {
+        block_context->decl_table.put(name, node);
     }
 }
 
-static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node) {
+static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) {
     switch (node->type) {
         case NodeTypeRoot:
             for (int i = 0; i < import->root->data.root.top_level_decls.length; i += 1) {
                 AstNode *child = import->root->data.root.top_level_decls.at(i);
-                detect_top_level_decl_deps(g, import, child);
+                scan_decls(g, import, context, child);
             }
             break;
         case NodeTypeStructDecl:
             {
                 Buf *name = &node->data.struct_decl.name;
-                auto table_entry = g->primitive_type_table.maybe_get(name);
-                if (!table_entry) {
-                    table_entry = import->type_table.maybe_get(name);
-                }
-                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)));
-                } else {
-                    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));
-                    }
-
-                    import->type_table.put(&entry->name, entry);
-                    node->data.struct_decl.type_entry = entry;
-
-                    bool is_pub = (node->data.struct_decl.visib_mod != VisibModPrivate);
-                    if (is_pub) {
-                        for (int i = 0; i < import->importers.length; i += 1) {
-                            ImporterInfo importer = import->importers.at(i);
-                            auto table_entry = importer.import->type_table.maybe_get(&entry->name);
-                            if (table_entry) {
-                                add_node_error(g, importer.source_node,
-                                    buf_sprintf("import of type '%s' overrides existing definition",
-                                        buf_ptr(&entry->name)));
-                            } else {
-                                importer.import->type_table.put(&entry->name, entry);
-                            }
-                        }
-                    }
-                }
-
-                // determine which other top level declarations this struct depends on.
-                TopLevelDecl *decl_node = &node->data.struct_decl.top_level_decl;
-                decl_node->deps.init(1);
-                for (int i = 0; i < node->data.struct_decl.fields.length; i += 1) {
-                    AstNode *field_node = node->data.struct_decl.fields.at(i);
-                    AstNode *type_node = field_node->data.struct_field.type;
-                    collect_expr_decl_deps(g, import, type_node, decl_node);
-                }
-                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);
-                }
+                TypeTableEntry *container_type = get_partial_container_type(g, import,
+                        node->data.struct_decl.kind, node, buf_ptr(name));
+                node->data.struct_decl.type_entry = container_type;
+                add_top_level_decl(g, import, context, node, name);
 
                 // handle the member function definitions independently
                 for (int i = 0; i < node->data.struct_decl.fns.length; i += 1) {
-                    AstNode *fn_def_node = node->data.struct_decl.fns.at(i);
-                    AstNode *fn_proto_node = fn_def_node->data.fn_def.fn_proto;
-                    fn_proto_node->data.fn_proto.struct_node = node;
-                    detect_top_level_decl_deps(g, import, fn_def_node);
+                    AstNode *child_node = node->data.struct_decl.fns.at(i);
+                    get_as_top_level_decl(child_node)->parent_decl = node;
+                    BlockContext *child_context = get_container_block_context(container_type);
+                    scan_decls(g, import, child_context, child_node);
                 }
 
                 break;
             }
         case NodeTypeFnDef:
             node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = node;
-            detect_top_level_decl_deps(g, import, node->data.fn_def.fn_proto);
+            scan_decls(g, import, context, node->data.fn_def.fn_proto);
             break;
         case NodeTypeVariableDeclaration:
             {
-                // determine which other top level declarations this variable declaration depends on.
-                TopLevelDecl *decl_node = &node->data.variable_declaration.top_level_decl;
-                decl_node->deps.init(1);
-                if (node->data.variable_declaration.type) {
-                    collect_expr_decl_deps(g, import, node->data.variable_declaration.type, decl_node);
-                }
-                if (node->data.variable_declaration.expr) {
-                    collect_expr_decl_deps(g, import, node->data.variable_declaration.expr, decl_node);
-                }
                 Buf *name = &node->data.variable_declaration.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);
-                }
+                add_top_level_decl(g, import, context, node, name);
                 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);
-                }
+                add_top_level_decl(g, import, context, node, name);
                 break;
             }
         case NodeTypeFnProto:
@@ -5695,60 +5482,22 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
                     add_node_error(g, node, buf_sprintf("missing function name"));
                     break;
                 }
-                Buf *qualified_name;
-                AstNode *struct_node = node->data.fn_proto.struct_node;
-                if (struct_node) {
-                    Buf *struct_name = &struct_node->data.struct_decl.name;
-                    qualified_name = buf_sprintf("%s.%s", buf_ptr(struct_name), buf_ptr(fn_name));
-                } else {
-                    qualified_name = fn_name;
-                }
-
-
-                // determine which other top level declarations this function prototype depends on.
-                TopLevelDecl *decl_node = &node->data.fn_proto.top_level_decl;
-                decl_node->deps.init(1);
 
-                collect_expr_decl_deps(g, import, node, decl_node);
-
-                decl_node->name = qualified_name;
-                decl_node->import = import;
-                if (decl_node->deps.size() > 0) {
-                    if (g->unresolved_top_level_decls.maybe_get(qualified_name)) {
-                        node->data.fn_proto.skip = true;
-                        add_node_error(g, node, buf_sprintf("redefinition of '%s'", buf_ptr(fn_name)));
-                    } else {
-                        g->unresolved_top_level_decls.put(qualified_name, node);
-                    }
-                } else {
-                    resolve_top_level_decl(g, import, node);
-                }
+                add_top_level_decl(g, import, context, node, fn_name);
                 break;
             }
-        case NodeTypeRootExportDecl:
-            resolve_top_level_decl(g, import, node);
-            break;
-        case NodeTypeImport:
-            // already taken care of
-            break;
-        case NodeTypeCImport:
+        case NodeTypeUse:
             {
-                TopLevelDecl *decl_node = &node->data.c_import.top_level_decl;
-                decl_node->deps.init(1);
-                collect_expr_decl_deps(g, import, node->data.c_import.block, decl_node);
-
-                decl_node->name = buf_sprintf("c_import_%" PRIu32, node->create_index);
-                decl_node->import = import;
-                if (decl_node->deps.size() > 0) {
-                    g->unresolved_top_level_decls.put(decl_node->name, node);
-                } else {
-                    resolve_top_level_decl(g, import, node);
-                }
+                TopLevelDecl *tld = get_as_top_level_decl(node);
+                tld->import = import;
+                node->block_context = context;
+                g->use_queue.append(node);
+                tld->import->use_decls.append(node);
                 break;
             }
         case NodeTypeErrorValueDecl:
             // error value declarations do not depend on other top level decls
-            resolve_top_level_decl(g, import, node);
+            preview_error_value_decl(g, node);
             break;
         case NodeTypeDirective:
         case NodeTypeParamDecl:
@@ -5792,152 +5541,176 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
     }
 }
 
-static void recursive_resolve_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
-    auto it = get_resolved_top_level_decl(node)->deps.entry_iterator();
-    for (;;) {
-        auto *entry = it.next();
-        if (!entry)
-            break;
+static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode *dst_use_node) {
+    TopLevelDecl *tld = get_as_top_level_decl(dst_use_node);
+    AstNode *use_target_node = src_use_node->data.use.expr;
+    Expr *expr = get_resolved_expr(use_target_node);
 
-        auto unresolved_entry = g->unresolved_top_level_decls.maybe_get(entry->key);
-        if (!unresolved_entry) {
-            continue;
-        }
+    if (expr->type_entry->id == TypeTableEntryIdInvalid) {
+        return;
+    }
+
+    ConstExprValue *const_val = &expr->const_val;
+    assert(const_val->ok);
+
+    ImportTableEntry *target_import = const_val->data.x_import;
+    assert(target_import);
 
-        AstNode *child_node = unresolved_entry->value;
+    if (target_import->any_imports_failed) {
+        tld->import->any_imports_failed = true;
+    }
 
-        if (get_resolved_top_level_decl(child_node)->in_current_deps) {
-            // dependency loop. we'll let the fact that it's not in the respective
-            // table cause an error in resolve_top_level_decl.
+    for (int i = 0; i < target_import->root->data.root.top_level_decls.length; i += 1) {
+        AstNode *decl_node = target_import->root->data.root.top_level_decls.at(i);
+        if (decl_node->type == NodeTypeFnDef) {
+            decl_node = decl_node->data.fn_def.fn_proto;
+        }
+        TopLevelDecl *target_tld = get_as_top_level_decl(decl_node);
+        if (!target_tld->name) {
             continue;
         }
+        if (target_tld->visib_mod != VisibModPrivate) {
+            auto existing_entry = tld->import->block_context->decl_table.maybe_get(target_tld->name);
+            if (existing_entry) {
+                AstNode *existing_decl = existing_entry->value;
+                if (existing_decl != decl_node) {
+                    ErrorMsg *msg = add_node_error(g, dst_use_node,
+                            buf_sprintf("import of '%s' overrides existing definition",
+                                buf_ptr(target_tld->name)));
+                    add_error_note(g, msg, existing_decl, buf_sprintf("previous definition here"));
+                    add_error_note(g, msg, decl_node, buf_sprintf("imported definition here"));
+                }
+            } else {
+                tld->import->block_context->decl_table.put(target_tld->name, decl_node);
+            }
+        }
+    }
 
-        // set temporary flag
-        TopLevelDecl *top_level_decl = get_resolved_top_level_decl(child_node);
-        top_level_decl->in_current_deps = true;
+    for (int i = 0; i < target_import->use_decls.length; i += 1) {
+        AstNode *use_decl_node = target_import->use_decls.at(i);
+        TopLevelDecl *target_tld = get_as_top_level_decl(use_decl_node);
+        if (target_tld->visib_mod != VisibModPrivate) {
+            add_symbols_from_import(g, use_decl_node, dst_use_node);
+        }
+    }
 
-        recursive_resolve_decl(g, top_level_decl->import, child_node);
+}
 
-        // unset temporary flag
-        top_level_decl->in_current_deps = false;
-    }
+static void resolve_use_decl(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeUse);
+    add_symbols_from_import(g, node, node);
+}
 
-    resolve_top_level_decl(g, import, node);
+static void preview_use_decl(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeUse);
+    TopLevelDecl *tld = get_as_top_level_decl(node);
+    TypeTableEntry *use_expr_type = analyze_expression(g, tld->import, tld->import->block_context,
+            g->builtin_types.entry_namespace, node->data.use.expr);
+    if (use_expr_type->id == TypeTableEntryIdInvalid) {
+        tld->import->any_imports_failed = true;
+    }
 }
 
-static void resolve_top_level_declarations_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
-    assert(node->type == NodeTypeRoot);
+ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package,
+        Buf *abs_full_path, Buf *src_dirname, Buf *src_basename, Buf *source_code)
+{
+    Buf *full_path = buf_alloc();
+    os_path_join(src_dirname, src_basename, full_path);
 
-    while (g->unresolved_top_level_decls.size() > 0) {
-        // for the sake of determinism, find the element with the lowest
-        // insert index and resolve that one.
-        AstNode *decl_node = nullptr;
-        auto it = g->unresolved_top_level_decls.entry_iterator();
-        for (;;) {
-            auto *entry = it.next();
-            if (!entry)
-                break;
+    if (g->verbose) {
+        fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(full_path));
+        fprintf(stderr, "----------------\n");
+        fprintf(stderr, "%s\n", buf_ptr(source_code));
 
-            AstNode *this_node = entry->value;
-            if (!decl_node || this_node->create_index < decl_node->create_index) {
-                decl_node = this_node;
-            }
+        fprintf(stderr, "\nTokens:\n");
+        fprintf(stderr, "---------\n");
+    }
 
-        }
-        // set temporary flag
-        TopLevelDecl *top_level_decl = get_resolved_top_level_decl(decl_node);
-        top_level_decl->in_current_deps = true;
+    Tokenization tokenization = {0};
+    tokenize(source_code, &tokenization);
 
-        recursive_resolve_decl(g, top_level_decl->import, decl_node);
+    if (tokenization.err) {
+        ErrorMsg *err = err_msg_create_with_line(full_path, tokenization.err_line, tokenization.err_column,
+                source_code, tokenization.line_offsets, tokenization.err);
 
-        // unset temporary flag
-        top_level_decl->in_current_deps = false;
+        print_err_msg(err, g->err_color);
+        exit(1);
     }
-}
 
-static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
-    assert(node->type == NodeTypeRoot);
+    if (g->verbose) {
+        print_tokens(source_code, tokenization.tokens);
 
-    for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
-        AstNode *child = node->data.root.top_level_decls.at(i);
-        analyze_top_level_decl(g, import, child);
+        fprintf(stderr, "\nAST:\n");
+        fprintf(stderr, "------\n");
     }
-}
 
-void semantic_analyze(CodeGen *g) {
-    {
-        auto it = g->import_table.entry_iterator();
-        for (;;) {
-            auto *entry = it.next();
-            if (!entry)
-                break;
+    ImportTableEntry *import_entry = allocate<ImportTableEntry>(1);
+    import_entry->package = package;
+    import_entry->source_code = source_code;
+    import_entry->line_offsets = tokenization.line_offsets;
+    import_entry->path = full_path;
 
-            ImportTableEntry *import = entry->value;
+    import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color,
+            &g->next_node_index);
+    assert(import_entry->root);
+    if (g->verbose) {
+        ast_print(stderr, import_entry->root, 0);
+    }
 
-            for (int i = 0; i < import->root->data.root.top_level_decls.length; i += 1) {
-                AstNode *child = import->root->data.root.top_level_decls.at(i);
-                if (child->type == NodeTypeImport) {
-                    if (child->data.import.directives) {
-                        for (int i = 0; i < child->data.import.directives->length; i += 1) {
-                            AstNode *directive_node = child->data.import.directives->at(i);
-                            Buf *name = &directive_node->data.directive.name;
-                            add_node_error(g, directive_node,
-                                    buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
-                        }
-                    }
+    import_entry->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname));
+    g->import_table.put(abs_full_path, import_entry);
+    g->import_queue.append(import_entry);
 
-                    ImportTableEntry *target_import = child->data.import.import;
-                    assert(target_import);
+    import_entry->block_context = new_block_context(import_entry->root, nullptr);
+    import_entry->block_context->di_scope = LLVMZigFileToScope(import_entry->di_file);
 
-                    target_import->importers.append({import, child});
-                } else if (child->type == NodeTypeErrorValueDecl) {
-                    preview_error_value_decl(g, child);
-                }
+
+    assert(import_entry->root->type == NodeTypeRoot);
+    for (int decl_i = 0; decl_i < import_entry->root->data.root.top_level_decls.length; decl_i += 1) {
+        AstNode *top_level_decl = import_entry->root->data.root.top_level_decls.at(decl_i);
+
+        if (top_level_decl->type == NodeTypeFnDef) {
+            AstNode *proto_node = top_level_decl->data.fn_def.fn_proto;
+            assert(proto_node->type == NodeTypeFnProto);
+            Buf *proto_name = &proto_node->data.fn_proto.name;
+
+            bool is_private = (proto_node->data.fn_proto.top_level_decl.visib_mod == VisibModPrivate);
+
+            if (buf_eql_str(proto_name, "main") && !is_private) {
+                g->have_exported_main = true;
             }
         }
     }
 
-    {
-        g->err_tag_type = get_smallest_unsigned_int_type(g, g->error_value_count);
-
-        g->builtin_types.entry_pure_error->type_ref = g->err_tag_type->type_ref;
-        g->builtin_types.entry_pure_error->di_type = g->err_tag_type->di_type;
-    }
+    return import_entry;
+}
 
-    {
-        auto it = g->import_table.entry_iterator();
-        for (;;) {
-            auto *entry = it.next();
-            if (!entry)
-                break;
 
-            ImportTableEntry *import = entry->value;
+void semantic_analyze(CodeGen *g) {
+    for (; g->import_queue_index < g->import_queue.length; g->import_queue_index += 1) {
+        ImportTableEntry *import = g->import_queue.at(g->import_queue_index);
+        scan_decls(g, import, import->block_context, import->root);
+    }
 
-            detect_top_level_decl_deps(g, import, import->root);
-        }
+    for (; g->use_queue_index < g->use_queue.length; g->use_queue_index += 1) {
+        AstNode *use_decl_node = g->use_queue.at(g->use_queue_index);
+        preview_use_decl(g, use_decl_node);
     }
 
-    {
-        auto it = g->import_table.entry_iterator();
-        for (;;) {
-            auto *entry = it.next();
-            if (!entry)
-                break;
+    for (int i = 0; i < g->use_queue.length; i += 1) {
+        AstNode *use_decl_node = g->use_queue.at(i);
+        resolve_use_decl(g, use_decl_node);
+    }
 
-            ImportTableEntry *import = entry->value;
-            resolve_top_level_declarations_root(g, import, import->root);
-        }
+    for (; g->export_queue_index < g->export_queue.length; g->export_queue_index += 1) {
+        AstNode *decl_node = g->export_queue.at(g->export_queue_index);
+        bool pointer_only = false;
+        resolve_top_level_decl(g, decl_node, pointer_only);
     }
-    {
-        auto it = g->import_table.entry_iterator();
-        for (;;) {
-            auto *entry = it.next();
-            if (!entry)
-                break;
 
-            ImportTableEntry *import = entry->value;
-            analyze_top_level_decls_root(g, import, import->root);
-        }
+    for (int i = 0; i < g->fn_defs.length; i += 1) {
+        FnTableEntry *fn_entry = g->fn_defs.at(i);
+        analyze_fn_body(g, fn_entry);
     }
 }
 
@@ -6012,13 +5785,11 @@ Expr *get_resolved_expr(AstNode *node) {
         case NodeTypeSwitchProng:
         case NodeTypeSwitchRange:
         case NodeTypeRoot:
-        case NodeTypeRootExportDecl:
         case NodeTypeFnDef:
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
         case NodeTypeDirective:
-        case NodeTypeImport:
-        case NodeTypeCImport:
+        case NodeTypeUse:
         case NodeTypeStructDecl:
         case NodeTypeStructField:
         case NodeTypeStructValueField:
@@ -6029,18 +5800,20 @@ Expr *get_resolved_expr(AstNode *node) {
     zig_unreachable();
 }
 
-TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
+static TopLevelDecl *get_as_top_level_decl(AstNode *node) {
     switch (node->type) {
         case NodeTypeVariableDeclaration:
             return &node->data.variable_declaration.top_level_decl;
         case NodeTypeFnProto:
             return &node->data.fn_proto.top_level_decl;
+        case NodeTypeFnDef:
+            return &node->data.fn_def.fn_proto->data.fn_proto.top_level_decl;
         case NodeTypeStructDecl:
             return &node->data.struct_decl.top_level_decl;
         case NodeTypeErrorValueDecl:
             return &node->data.error_value_decl.top_level_decl;
-        case NodeTypeCImport:
-            return &node->data.c_import.top_level_decl;
+        case NodeTypeUse:
+            return &node->data.use.top_level_decl;
         case NodeTypeTypeDecl:
             return &node->data.type_decl.top_level_decl;
         case NodeTypeNumberLiteral:
@@ -6063,8 +5836,6 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
         case NodeTypeAsmExpr:
         case NodeTypeContainerInitExpr:
         case NodeTypeRoot:
-        case NodeTypeRootExportDecl:
-        case NodeTypeFnDef:
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
         case NodeTypeBlock:
@@ -6072,7 +5843,6 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
         case NodeTypeStringLiteral:
         case NodeTypeCharLiteral:
         case NodeTypeSymbol:
-        case NodeTypeImport:
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeUndefinedLiteral:
@@ -6140,6 +5910,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
         case TypeTableEntryIdNumLitFloat:
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNamespace:
              zig_unreachable();
         case TypeTableEntryIdUnreachable:
         case TypeTableEntryIdVoid:
@@ -6257,6 +6028,7 @@ static TypeTableEntry *type_of_first_thing_in_memory(TypeTableEntry *type_entry)
         case TypeTableEntryIdUnreachable:
         case TypeTableEntryIdMetaType:
         case TypeTableEntryIdVoid:
+        case TypeTableEntryIdNamespace:
             zig_unreachable();
         case TypeTableEntryIdArray:
             return type_of_first_thing_in_memory(type_entry->data.array.child_type);
src/analyze.hpp
@@ -12,11 +12,11 @@
 
 void semantic_analyze(CodeGen *g);
 ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
+ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg);
 TypeTableEntry *new_type_table_entry(TypeTableEntryId id);
 TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);
 BlockContext *new_block_context(AstNode *node, BlockContext *parent);
 Expr *get_resolved_expr(AstNode *node);
-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);
@@ -37,4 +37,8 @@ TypeTableEntry *get_underlying_type(TypeTableEntry *type_entry);
 bool type_has_bits(TypeTableEntry *type_entry);
 uint64_t get_memcpy_align(CodeGen *g, TypeTableEntry *type_entry);
 
+
+ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package,
+        Buf *abs_full_path, Buf *src_dirname, Buf *src_basename, Buf *source_code);
+
 #endif
src/ast_render.cpp
@@ -101,8 +101,6 @@ static const char *node_type_str(NodeType node_type) {
     switch (node_type) {
         case NodeTypeRoot:
             return "Root";
-        case NodeTypeRootExportDecl:
-            return "RootExportDecl";
         case NodeTypeFnDef:
             return "FnDef";
         case NodeTypeFnDecl:
@@ -145,10 +143,8 @@ static const char *node_type_str(NodeType node_type) {
             return "Symbol";
         case NodeTypePrefixOpExpr:
             return "PrefixOpExpr";
-        case NodeTypeImport:
-            return "Import";
-        case NodeTypeCImport:
-            return "CImport";
+        case NodeTypeUse:
+            return "Use";
         case NodeTypeBoolLiteral:
             return "BoolLiteral";
         case NodeTypeNullLiteral:
@@ -214,11 +210,6 @@ void ast_print(FILE *f, AstNode *node, int indent) {
                 ast_print(f, child, indent + 2);
             }
             break;
-        case NodeTypeRootExportDecl:
-            fprintf(f, "%s %s '%s'\n", node_type_str(node->type),
-                    buf_ptr(&node->data.root_export_decl.type),
-                    buf_ptr(&node->data.root_export_decl.name));
-            break;
         case NodeTypeFnDef:
             {
                 fprintf(f, "%s\n", node_type_str(node->type));
@@ -372,12 +363,9 @@ void ast_print(FILE *f, AstNode *node, int indent) {
         case NodeTypeSymbol:
             fprintf(f, "Symbol %s\n", buf_ptr(&node->data.symbol_expr.symbol));
             break;
-        case NodeTypeImport:
-            fprintf(f, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.import.path));
-            break;
-        case NodeTypeCImport:
+        case NodeTypeUse:
             fprintf(f, "%s\n", node_type_str(node->type));
-            ast_print(f, node->data.c_import.block, indent + 2);
+            ast_print(f, node->data.use.expr, indent + 2);
             break;
         case NodeTypeBoolLiteral:
             fprintf(f, "%s '%s'\n", node_type_str(node->type),
@@ -556,7 +544,7 @@ static void render_node(AstRender *ar, AstNode *node) {
                 print_indent(ar);
                 render_node(ar, child);
 
-                if (child->type == NodeTypeImport ||
+                if (child->type == NodeTypeUse ||
                     child->type == NodeTypeVariableDeclaration ||
                     child->type == NodeTypeTypeDecl ||
                     child->type == NodeTypeErrorValueDecl ||
@@ -567,12 +555,10 @@ static void render_node(AstRender *ar, AstNode *node) {
                 fprintf(ar->f, "\n");
             }
             break;
-        case NodeTypeRootExportDecl:
-            zig_panic("TODO");
         case NodeTypeFnProto:
             {
                 const char *fn_name = buf_ptr(&node->data.fn_proto.name);
-                const char *pub_str = visib_mod_string(node->data.fn_proto.visib_mod);
+                const char *pub_str = visib_mod_string(node->data.fn_proto.top_level_decl.visib_mod);
                 const char *extern_str = extern_string(node->data.fn_proto.is_extern);
                 const char *inline_str = inline_string(node->data.fn_proto.is_inline);
                 fprintf(ar->f, "%s%s%sfn %s(", pub_str, inline_str, extern_str, fn_name);
@@ -605,15 +591,19 @@ static void render_node(AstRender *ar, AstNode *node) {
                 break;
             }
         case NodeTypeFnDef:
-            if (node->data.fn_def.fn_proto->data.fn_proto.directives) {
-                for (int i = 0; i < node->data.fn_def.fn_proto->data.fn_proto.directives->length; i += 1) {
-                    render_node(ar, node->data.fn_def.fn_proto->data.fn_proto.directives->at(i));
+            {
+                ZigList<AstNode *> *directives =
+                    node->data.fn_def.fn_proto->data.fn_proto.top_level_decl.directives;
+                if (directives) {
+                    for (int i = 0; i < directives->length; i += 1) {
+                        render_node(ar, directives->at(i));
+                    }
                 }
+                render_node(ar, node->data.fn_def.fn_proto);
+                fprintf(ar->f, " ");
+                render_node(ar, node->data.fn_def.body);
+                break;
             }
-            render_node(ar, node->data.fn_def.fn_proto);
-            fprintf(ar->f, " ");
-            render_node(ar, node->data.fn_def.body);
-            break;
         case NodeTypeFnDecl:
             zig_panic("TODO");
         case NodeTypeParamDecl:
@@ -642,7 +632,7 @@ static void render_node(AstRender *ar, AstNode *node) {
             zig_panic("TODO");
         case NodeTypeVariableDeclaration:
             {
-                const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
+                const char *pub_str = visib_mod_string(node->data.variable_declaration.top_level_decl.visib_mod);
                 const char *extern_str = extern_string(node->data.variable_declaration.is_extern);
                 const char *var_name = buf_ptr(&node->data.variable_declaration.symbol);
                 const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);
@@ -659,7 +649,7 @@ static void render_node(AstRender *ar, AstNode *node) {
             }
         case NodeTypeTypeDecl:
             {
-                const char *pub_str = visib_mod_string(node->data.type_decl.visib_mod);
+                const char *pub_str = visib_mod_string(node->data.type_decl.top_level_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);
@@ -748,9 +738,7 @@ static void render_node(AstRender *ar, AstNode *node) {
                 fprintf(ar->f, ".%s", buf_ptr(rhs));
                 break;
             }
-        case NodeTypeImport:
-            zig_panic("TODO");
-        case NodeTypeCImport:
+        case NodeTypeUse:
             zig_panic("TODO");
         case NodeTypeBoolLiteral:
             zig_panic("TODO");
@@ -785,7 +773,7 @@ static void render_node(AstRender *ar, AstNode *node) {
         case NodeTypeStructDecl:
             {
                 const char *struct_name = buf_ptr(&node->data.struct_decl.name);
-                const char *pub_str = visib_mod_string(node->data.struct_decl.visib_mod);
+                const char *pub_str = visib_mod_string(node->data.struct_decl.top_level_decl.visib_mod);
                 const char *container_str = container_string(node->data.struct_decl.kind);
                 fprintf(ar->f, "%s%s %s {\n", pub_str, container_str, struct_name);
                 ar->indent += ar->indent_size;
src/codegen.cpp
@@ -47,19 +47,30 @@ static void init_darwin_native(CodeGen *g) {
     }
 }
 
+static PackageTableEntry *new_package(const char *root_src_dir, const char *root_src_path) {
+    PackageTableEntry *entry = allocate<PackageTableEntry>(1);
+    entry->package_table.init(4);
+    buf_init_from_str(&entry->root_src_dir, root_src_dir);
+    buf_init_from_str(&entry->root_src_path, root_src_path);
+    return entry;
+}
+
 CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) {
     CodeGen *g = allocate<CodeGen>(1);
     g->import_table.init(32);
     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->error_table.init(16);
     g->is_release_build = false;
     g->is_test_build = false;
-    g->root_source_dir = root_source_dir;
     g->error_value_count = 1;
 
+    g->root_package = new_package(buf_ptr(root_source_dir), "");
+    g->std_package = new_package(ZIG_STD_DIR, "index.zig");
+    g->root_package->package_table.put(buf_create_from_str("std"), g->std_package);
+
+
     if (target) {
         // cross compiling, so we can't rely on all the configured stuff since
         // that's for native compilation
@@ -117,6 +128,10 @@ void codegen_set_verbose(CodeGen *g, bool verbose) {
     g->verbose = verbose;
 }
 
+void codegen_set_check_unused(CodeGen *g, bool check_unused) {
+    g->check_unused = check_unused;
+}
+
 void codegen_set_errmsg_color(CodeGen *g, ErrColor err_color) {
     g->err_color = err_color;
 }
@@ -157,6 +172,14 @@ void codegen_add_lib_dir(CodeGen *g, const char *dir) {
     g->lib_dirs.append(dir);
 }
 
+void codegen_add_link_lib(CodeGen *g, const char *lib) {
+    if (strcmp(lib, "c") == 0) {
+        g->link_libc = true;
+    } else {
+        g->link_libs.append(buf_create_from_str(lib));
+    }
+}
+
 void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole) {
     g->windows_subsystem_windows = mwindows;
     g->windows_subsystem_console = mconsole;
@@ -316,6 +339,8 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
         case BuiltinFnIdCInclude:
         case BuiltinFnIdCDefine:
         case BuiltinFnIdCUndef:
+        case BuiltinFnIdImport:
+        case BuiltinFnIdCImport:
             zig_unreachable();
         case BuiltinFnIdCtz:
         case BuiltinFnIdClz:
@@ -844,7 +869,7 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **ou
 
     LLVMValueRef struct_ptr;
     if (struct_expr_node->type == NodeTypeSymbol) {
-        VariableTableEntry *var = struct_expr_node->data.symbol_expr.variable;
+        VariableTableEntry *var = get_resolved_expr(struct_expr_node)->variable;
         assert(var);
 
         if (var->is_ptr && var->type->id == TypeTableEntryIdPointer) {
@@ -983,6 +1008,17 @@ static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lva
     }
 }
 
+static LLVMValueRef gen_variable(CodeGen *g, AstNode *source_node, VariableTableEntry *variable) {
+    if (!type_has_bits(variable->type)) {
+        return nullptr;
+    } else if (variable->is_ptr) {
+        assert(variable->value_ref);
+        return get_handle_value(g, source_node, variable->value_ref, variable->type);
+    } else {
+        return variable->value_ref;
+    }
+}
+
 static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
     assert(node->type == NodeTypeFieldAccessExpr);
 
@@ -1016,6 +1052,10 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lva
         } else {
             zig_unreachable();
         }
+    } else if (struct_type->id == TypeTableEntryIdNamespace) {
+        VariableTableEntry *variable = get_resolved_expr(node)->variable;
+        assert(variable);
+        return gen_variable(g, node, variable);
     } else {
         zig_unreachable();
     }
@@ -1027,7 +1067,7 @@ static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node,
     LLVMValueRef target_ref;
 
     if (node->type == NodeTypeSymbol) {
-        VariableTableEntry *var = node->data.symbol_expr.variable;
+        VariableTableEntry *var = get_resolved_expr(node)->variable;
         assert(var);
 
         *out_type_entry = var->type;
@@ -2468,21 +2508,18 @@ static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
 
 static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeSymbol);
-    VariableTableEntry *variable = node->data.symbol_expr.variable;
+    VariableTableEntry *variable = get_resolved_expr(node)->variable;
     if (variable) {
-        if (!type_has_bits(variable->type)) {
-            return nullptr;
-        } else if (variable->is_ptr) {
-            assert(variable->value_ref);
-            return get_handle_value(g, node, variable->value_ref, variable->type);
-        } else {
-            return variable->value_ref;
-        }
+        return gen_variable(g, node, variable);
     }
 
+    zig_unreachable();
+
+    /* TODO delete
     FnTableEntry *fn_entry = node->data.symbol_expr.fn_entry;
     assert(fn_entry);
     return fn_entry->fn_value;
+    */
 }
 
 static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
@@ -2703,14 +2740,12 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
             // caught by constant expression eval codegen
             zig_unreachable();
         case NodeTypeRoot:
-        case NodeTypeRootExportDecl:
         case NodeTypeFnProto:
         case NodeTypeFnDef:
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
         case NodeTypeDirective:
-        case NodeTypeImport:
-        case NodeTypeCImport:
+        case NodeTypeUse:
         case NodeTypeStructDecl:
         case NodeTypeStructField:
         case NodeTypeStructValueField:
@@ -2891,6 +2926,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdUndefLit:
         case TypeTableEntryIdVoid:
+        case TypeTableEntryIdNamespace:
             zig_unreachable();
 
     }
@@ -2943,14 +2979,14 @@ static bool skip_fn_codegen(CodeGen *g, FnTableEntry *fn_entry) {
         if (fn_entry == g->main_fn) {
             return true;
         }
-        return fn_entry->ref_count == 0;
+        return false;
     }
 
     if (fn_entry->is_test) {
         return true;
     }
 
-    return fn_entry->ref_count == 0;
+    return false;
 }
 
 static LLVMValueRef gen_test_fn_val(CodeGen *g, FnTableEntry *fn_entry) {
@@ -3296,6 +3332,12 @@ static void define_builtin_types(CodeGen *g) {
         entry->zero_bits = true;
         g->builtin_types.entry_invalid = entry;
     }
+    {
+        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNamespace);
+        buf_init_from_str(&entry->name, "(namespace)");
+        entry->zero_bits = true;
+        g->builtin_types.entry_namespace = entry;
+    }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitFloat);
         buf_init_from_str(&entry->name, "(float literal)");
@@ -3499,14 +3541,6 @@ static void define_builtin_types(CodeGen *g) {
         g->builtin_types.entry_type = entry;
         g->primitive_type_table.put(&entry->name, entry);
     }
-    {
-        // partially complete the error type. we complete it later after we know
-        // error_value_count.
-        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError);
-        buf_init_from_str(&entry->name, "error");
-        g->builtin_types.entry_pure_error = entry;
-        g->primitive_type_table.put(&entry->name, entry);
-    }
 
     g->builtin_types.entry_u8 = get_int_type(g, false, 8);
     g->builtin_types.entry_u16 = get_int_type(g, false, 16);
@@ -3517,6 +3551,21 @@ static void define_builtin_types(CodeGen *g) {
     g->builtin_types.entry_i32 = get_int_type(g, true, 32);
     g->builtin_types.entry_i64 = get_int_type(g, true, 64);
 
+    {
+        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError);
+        buf_init_from_str(&entry->name, "error");
+
+        // TODO allow overriding this type and keep track of max value and emit an
+        // error if there are too many errors declared
+        g->err_tag_type = g->builtin_types.entry_u16;
+
+        g->builtin_types.entry_pure_error = entry;
+        entry->type_ref = g->err_tag_type->type_ref;
+        entry->di_type = g->err_tag_type->di_type;
+
+        g->primitive_type_table.put(&entry->name, entry);
+    }
+
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnum);
         entry->zero_bits = true; // only allowed at compile time
@@ -3685,12 +3734,11 @@ static void define_builtin_fns(CodeGen *g) {
     create_builtin_fn_with_arg_count(g, BuiltinFnIdConstEval, "const_eval", 1);
     create_builtin_fn_with_arg_count(g, BuiltinFnIdCtz, "ctz", 2);
     create_builtin_fn_with_arg_count(g, BuiltinFnIdClz, "clz", 2);
+    create_builtin_fn_with_arg_count(g, BuiltinFnIdImport, "import", 1);
+    create_builtin_fn_with_arg_count(g, BuiltinFnIdCImport, "c_import", 1);
 }
 
 static void init(CodeGen *g, Buf *source_path) {
-    g->lib_search_paths.append(g->root_source_dir);
-    g->lib_search_paths.append(buf_create_from_str(ZIG_STD_DIR));
-
     g->module = LLVMModuleCreateWithName(buf_ptr(source_path));
 
     get_target_triple(&g->triple_str, &g->zig_target);
@@ -3741,7 +3789,7 @@ static void init(CodeGen *g, Buf *source_path) {
     const char *flags = "";
     unsigned runtime_version = 0;
     g->compile_unit = LLVMZigCreateCompileUnit(g->dbuilder, LLVMZigLang_DW_LANG_C99(),
-            buf_ptr(source_path), buf_ptr(g->root_source_dir),
+            buf_ptr(source_path), buf_ptr(&g->root_package->root_src_dir),
             buf_ptr(producer), is_optimized, flags, runtime_version,
             "", 0, !g->strip_debug_symbols);
 
@@ -3761,9 +3809,6 @@ void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source
     ImportTableEntry *import = allocate<ImportTableEntry>(1);
     import->source_code = source_code;
     import->path = full_path;
-    import->fn_table.init(32);
-    import->type_table.init(8);
-    import->error_table.init(8);
     g->root_import = import;
 
     init(g, full_path);
@@ -3791,214 +3836,7 @@ void codegen_render_ast(CodeGen *g, FILE *f, int indent_size) {
 }
 
 
-static int parse_version_string(Buf *buf, int *major, int *minor, int *patch) {
-    char *dot1 = strstr(buf_ptr(buf), ".");
-    if (!dot1)
-        return ErrorInvalidFormat;
-    char *dot2 = strstr(dot1 + 1, ".");
-    if (!dot2)
-        return ErrorInvalidFormat;
-
-    *major = (int)strtol(buf_ptr(buf), nullptr, 10);
-    *minor = (int)strtol(dot1 + 1, nullptr, 10);
-    *patch = (int)strtol(dot2 + 1, nullptr, 10);
-
-    return ErrorNone;
-}
-
-static void set_root_export_version(CodeGen *g, Buf *version_buf, AstNode *node) {
-    int err;
-    if ((err = parse_version_string(version_buf, &g->version_major, &g->version_minor, &g->version_patch))) {
-        add_node_error(g, node,
-                buf_sprintf("invalid version string"));
-    }
-}
-
-
-static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path,
-        Buf *src_dirname, Buf *src_basename, Buf *source_code)
-{
-    int err;
-    Buf *full_path = buf_alloc();
-    os_path_join(src_dirname, src_basename, full_path);
-
-    if (g->verbose) {
-        fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(full_path));
-        fprintf(stderr, "----------------\n");
-        fprintf(stderr, "%s\n", buf_ptr(source_code));
-
-        fprintf(stderr, "\nTokens:\n");
-        fprintf(stderr, "---------\n");
-    }
-
-    Tokenization tokenization = {0};
-    tokenize(source_code, &tokenization);
-
-    if (tokenization.err) {
-        ErrorMsg *err = err_msg_create_with_line(full_path, tokenization.err_line, tokenization.err_column,
-                source_code, tokenization.line_offsets, tokenization.err);
-
-        print_err_msg(err, g->err_color);
-        exit(1);
-    }
-
-    if (g->verbose) {
-        print_tokens(source_code, tokenization.tokens);
-
-        fprintf(stderr, "\nAST:\n");
-        fprintf(stderr, "------\n");
-    }
-
-    ImportTableEntry *import_entry = allocate<ImportTableEntry>(1);
-    import_entry->source_code = source_code;
-    import_entry->line_offsets = tokenization.line_offsets;
-    import_entry->path = full_path;
-    import_entry->fn_table.init(32);
-    import_entry->type_table.init(8);
-    import_entry->error_table.init(8);
-
-    import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color,
-            &g->next_node_index);
-    assert(import_entry->root);
-    if (g->verbose) {
-        ast_print(stderr, import_entry->root, 0);
-    }
-
-    import_entry->di_file = LLVMZigCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname));
-    g->import_table.put(abs_full_path, import_entry);
-
-    import_entry->block_context = new_block_context(import_entry->root, nullptr);
-    import_entry->block_context->di_scope = LLVMZigFileToScope(import_entry->di_file);
-
-
-    assert(import_entry->root->type == NodeTypeRoot);
-    for (int decl_i = 0; decl_i < import_entry->root->data.root.top_level_decls.length; decl_i += 1) {
-        AstNode *top_level_decl = import_entry->root->data.root.top_level_decls.at(decl_i);
-
-        if (top_level_decl->type == NodeTypeRootExportDecl) {
-            if (g->root_import) {
-                add_node_error(g, top_level_decl,
-                        buf_sprintf("root export declaration only valid in root source file"));
-            } else {
-                ZigList<AstNode *> *directives = top_level_decl->data.root_export_decl.directives;
-                if (directives) {
-                    for (int i = 0; i < directives->length; i += 1) {
-                        AstNode *directive_node = directives->at(i);
-                        Buf *name = &directive_node->data.directive.name;
-                        AstNode *param_node = directive_node->data.directive.expr;
-                        assert(param_node->type == NodeTypeStringLiteral);
-                        Buf *param = &param_node->data.string_literal.buf;
-
-                        if (param) {
-                            if (buf_eql_str(name, "version")) {
-                                set_root_export_version(g, param, directive_node);
-                            } else if (buf_eql_str(name, "link")) {
-                                if (buf_eql_str(param, "c")) {
-                                    g->link_libc = true;
-                                } else {
-                                    g->link_libs.append(param);
-                                }
-                            } else {
-                                add_node_error(g, directive_node,
-                                        buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
-                            }
-                        }
-                    }
-                }
-
-                if (g->root_export_decl) {
-                    add_node_error(g, top_level_decl,
-                            buf_sprintf("only one root export declaration allowed"));
-                } else {
-                    g->root_export_decl = top_level_decl;
-
-                    if (!g->root_out_name)
-                        g->root_out_name = &top_level_decl->data.root_export_decl.name;
-
-                    Buf *out_type = &top_level_decl->data.root_export_decl.type;
-                    OutType export_out_type;
-                    if (buf_eql_str(out_type, "executable")) {
-                        export_out_type = OutTypeExe;
-                    } else if (buf_eql_str(out_type, "library")) {
-                        export_out_type = OutTypeLib;
-                    } else if (buf_eql_str(out_type, "object")) {
-                        export_out_type = OutTypeObj;
-                    } else {
-                        add_node_error(g, top_level_decl,
-                                buf_sprintf("invalid export type: '%s'", buf_ptr(out_type)));
-                    }
-                    if (g->out_type == OutTypeUnknown) {
-                        g->out_type = export_out_type;
-                    }
-                }
-            }
-        } else if (top_level_decl->type == NodeTypeImport) {
-            Buf *import_target_path = &top_level_decl->data.import.path;
-            Buf full_path = BUF_INIT;
-            Buf *import_code = buf_alloc();
-            bool found_it = false;
-
-            for (int path_i = 0; path_i < g->lib_search_paths.length; path_i += 1) {
-                Buf *search_path = g->lib_search_paths.at(path_i);
-                os_path_join(search_path, import_target_path, &full_path);
-
-                Buf *abs_full_path = buf_alloc();
-                if ((err = os_path_real(&full_path, abs_full_path))) {
-                    if (err == ErrorFileNotFound) {
-                        continue;
-                    } else {
-                        g->error_during_imports = true;
-                        add_node_error(g, top_level_decl,
-                                buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
-                        goto done_looking_at_imports;
-                    }
-                }
-
-                auto entry = g->import_table.maybe_get(abs_full_path);
-                if (entry) {
-                    found_it = true;
-                    top_level_decl->data.import.import = entry->value;
-                } else {
-                    if ((err = os_fetch_file_path(abs_full_path, import_code))) {
-                        if (err == ErrorFileNotFound) {
-                            continue;
-                        } else {
-                            g->error_during_imports = true;
-                            add_node_error(g, top_level_decl,
-                                    buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err)));
-                            goto done_looking_at_imports;
-                        }
-                    }
-                    top_level_decl->data.import.import = codegen_add_code(g,
-                            abs_full_path, search_path, &top_level_decl->data.import.path, import_code);
-                    found_it = true;
-                }
-                break;
-            }
-            if (!found_it) {
-                g->error_during_imports = true;
-                add_node_error(g, top_level_decl,
-                        buf_sprintf("unable to find '%s'", buf_ptr(import_target_path)));
-            }
-        } else if (top_level_decl->type == NodeTypeFnDef) {
-            AstNode *proto_node = top_level_decl->data.fn_def.fn_proto;
-            assert(proto_node->type == NodeTypeFnProto);
-            Buf *proto_name = &proto_node->data.fn_proto.name;
-
-            bool is_private = (proto_node->data.fn_proto.visib_mod == VisibModPrivate);
-
-            if (buf_eql_str(proto_name, "main") && !is_private) {
-                g->have_exported_main = true;
-            }
-        }
-    }
-
-done_looking_at_imports:
-
-    return import_entry;
-}
-
-static ImportTableEntry *add_special_code(CodeGen *g, const char *basename) {
+static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package, const char *basename) {
     Buf *std_dir = buf_create_from_str(ZIG_STD_DIR);
     Buf *code_basename = buf_create_from_str(basename);
     Buf path_to_code_src = BUF_INIT;
@@ -4013,12 +3851,22 @@ static ImportTableEntry *add_special_code(CodeGen *g, const char *basename) {
         zig_panic("unable to open '%s': %s", buf_ptr(&path_to_code_src), err_str(err));
     }
 
-    return codegen_add_code(g, abs_full_path, std_dir, code_basename, import_code);
+    return add_source_file(g, package, abs_full_path, std_dir, code_basename, import_code);
+}
+
+static PackageTableEntry *create_bootstrap_pkg(CodeGen *g) {
+    PackageTableEntry *package = new_package(ZIG_STD_DIR, "");
+    package->package_table.put(buf_create_from_str("std"), g->std_package);
+    package->package_table.put(buf_create_from_str("@root"), g->root_package);
+    return package;
 }
 
 void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *source_code) {
     Buf source_path = BUF_INIT;
     os_path_join(src_dir, src_basename, &source_path);
+
+    buf_init_from_buf(&g->root_package->root_src_path, src_basename);
+
     init(g, &source_path);
 
     Buf *abs_full_path = buf_alloc();
@@ -4027,19 +3875,14 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou
         zig_panic("unable to open '%s': %s", buf_ptr(&source_path), err_str(err));
     }
 
-    g->root_import = codegen_add_code(g, abs_full_path, src_dir, src_basename, source_code);
+    g->root_import = add_source_file(g, g->root_package, abs_full_path, src_dir, src_basename, source_code);
 
-    if (!g->root_out_name) {
-        add_node_error(g, g->root_import->root,
-                buf_sprintf("missing export declaration and output name not provided"));
-    } else if (g->out_type == OutTypeUnknown) {
-        add_node_error(g, g->root_import->root,
-                buf_sprintf("missing export declaration and export type not provided"));
-    }
+    assert(g->root_out_name);
+    assert(g->out_type != OutTypeUnknown);
 
     if (!g->link_libc && !g->is_test_build) {
         if (g->have_exported_main && (g->out_type == OutTypeObj || g->out_type == OutTypeExe)) {
-            g->bootstrap_import = add_special_code(g, "bootstrap.zig");
+            g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g), "bootstrap.zig");
         }
     }
 
@@ -4120,7 +3963,7 @@ void codegen_generate_h_file(CodeGen *g) {
         assert(proto_node->type == NodeTypeFnProto);
         AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
 
-        if (fn_proto->visib_mod != VisibModExport)
+        if (fn_proto->top_level_decl.visib_mod != VisibModExport)
             continue;
 
         Buf return_type_c = BUF_INIT;
src/codegen.hpp
@@ -19,6 +19,7 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target);
 void codegen_set_clang_argv(CodeGen *codegen, const char **args, int len);
 void codegen_set_is_release(CodeGen *codegen, bool is_release);
 void codegen_set_is_test(CodeGen *codegen, bool is_test);
+void codegen_set_check_unused(CodeGen *codegen, bool check_unused);
 
 void codegen_set_is_static(CodeGen *codegen, bool is_static);
 void codegen_set_strip(CodeGen *codegen, bool strip);
@@ -34,6 +35,7 @@ void codegen_set_linker_path(CodeGen *g, Buf *linker_path);
 void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole);
 void codegen_set_windows_unicode(CodeGen *g, bool municode);
 void codegen_add_lib_dir(CodeGen *codegen, const char *dir);
+void codegen_add_link_lib(CodeGen *codegen, const char *lib);
 void codegen_set_mlinker_version(CodeGen *g, Buf *darwin_linker_version);
 void codegen_set_rdynamic(CodeGen *g, bool rdynamic);
 void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min);
src/errmsg.cpp
@@ -4,36 +4,57 @@
 #include <stdio.h>
 
 #define RED "\x1b[31;1m"
-#define WHITE "\x1b[37;1m"
 #define GREEN "\x1b[32;1m"
+#define CYAN "\x1b[36;1m"
+#define WHITE "\x1b[37;1m"
 #define RESET "\x1b[0m"
 
-void print_err_msg(ErrorMsg *err, ErrColor color) {
+enum ErrType {
+    ErrTypeError,
+    ErrTypeNote,
+};
+
+static void print_err_msg_type(ErrorMsg *err, ErrColor color, ErrType err_type) {
+    const char *path = buf_ptr(err->path);
+    int line = err->line_start + 1;
+    int col = err->column_start + 1;
+    const char *text = buf_ptr(err->msg);
+
+
     if (color == ErrColorOn || (color == ErrColorAuto && os_stderr_tty())) {
-        fprintf(stderr, WHITE "%s:%d:%d: " RED "error:" WHITE " %s" RESET "\n",
-                buf_ptr(err->path),
-                err->line_start + 1, err->column_start + 1,
-                buf_ptr(err->msg));
+        if (err_type == ErrTypeError) {
+            fprintf(stderr, WHITE "%s:%d:%d: " RED "error:" WHITE " %s" RESET "\n", path, line, col, text);
+        } else if (err_type == ErrTypeNote) {
+            fprintf(stderr, WHITE "%s:%d:%d: " CYAN "note:" WHITE " %s" RESET "\n", path, line, col, text);
+        } else {
+            zig_unreachable();
+        }
 
         fprintf(stderr, "%s\n", buf_ptr(&err->line_buf));
         for (int i = 0; i < err->column_start; i += 1) {
             fprintf(stderr, " ");
         }
         fprintf(stderr, GREEN "^" RESET "\n");
-
     } else {
-        fprintf(stderr, "%s:%d:%d: error: %s\n",
-                buf_ptr(err->path),
-                err->line_start + 1, err->column_start + 1,
-                buf_ptr(err->msg));
+        if (err_type == ErrTypeError) {
+            fprintf(stderr, "%s:%d:%d: error: %s\n", path, line, col, text);
+        } else if (err_type == ErrTypeNote) {
+            fprintf(stderr, " %s:%d:%d: note: %s\n", path, line, col, text);
+        } else {
+            zig_unreachable();
+        }
     }
 
     for (int i = 0; i < err->notes.length; i += 1) {
         ErrorMsg *note = err->notes.at(i);
-        print_err_msg(note, color);
+        print_err_msg_type(note, color, ErrTypeNote);
     }
 }
 
+void print_err_msg(ErrorMsg *err, ErrColor color) {
+    print_err_msg_type(err, color, ErrTypeError);
+}
+
 void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note) {
     parent->notes.append(note);
 }
src/main.cpp
@@ -18,8 +18,8 @@
 static int usage(const char *arg0) {
     fprintf(stderr, "Usage: %s [command] [options]\n"
         "Commands:\n"
-        "  build [source]               create executable, object, or library from source\n"
-        "  test [source]                create and run a test build\n"
+        "  build [sources]              create executable, object, or library from source\n"
+        "  test [sources]               create and run a test build\n"
         "  parseh [source]              convert a c header file to zig extern declarations\n"
         "  version                      print version number and exit\n"
         "  targets                      list available compilation targets\n"
@@ -40,6 +40,7 @@ static int usage(const char *arg0) {
         "  -isystem [dir]               add additional search path for other .h files\n"
         "  -dirafter [dir]              same as -isystem but do it last\n"
         "  --library-path [dir]         add a directory to the library search path\n"
+        "  --library [lib]              link against lib\n"
         "  --target-arch [name]         specify target architecture\n"
         "  --target-os [name]           specify target operating system\n"
         "  --target-environ [name]      specify target environment\n"
@@ -50,6 +51,7 @@ static int usage(const char *arg0) {
         "  -rdynamic                    add all symbols to the dynamic symbol table\n"
         "  -mmacosx-version-min [ver]   (darwin only) set Mac OS X deployment target\n"
         "  -mios-version-min [ver]      (darwin only) set iOS deployment target\n"
+        "  --check-unused               perform semantic analysis on unused declarations\n"
     , arg0);
     return EXIT_FAILURE;
 }
@@ -118,6 +120,7 @@ int main(int argc, char **argv) {
     const char *linker_path = nullptr;
     ZigList<const char *> clang_argv = {0};
     ZigList<const char *> lib_dirs = {0};
+    ZigList<const char *> link_libs = {0};
     int err;
     const char *target_arch = nullptr;
     const char *target_os = nullptr;
@@ -129,6 +132,7 @@ int main(int argc, char **argv) {
     bool rdynamic = false;
     const char *mmacosx_version_min = nullptr;
     const char *mios_version_min = nullptr;
+    bool check_unused = false;
 
     for (int i = 1; i < argc; i += 1) {
         char *arg = argv[i];
@@ -150,6 +154,8 @@ int main(int argc, char **argv) {
                 municode = true;
             } else if (strcmp(arg, "-rdynamic") == 0) {
                 rdynamic = true;
+            } else if (strcmp(arg, "--check-unused") == 0) {
+                check_unused = true;
             } else if (i + 1 >= argc) {
                 return usage(arg0);
             } else {
@@ -198,6 +204,8 @@ int main(int argc, char **argv) {
                     clang_argv.append(argv[i]);
                 } else if (strcmp(arg, "--library-path") == 0) {
                     lib_dirs.append(argv[i]);
+                } else if (strcmp(arg, "--library") == 0) {
+                    link_libs.append(argv[i]);
                 } else if (strcmp(arg, "--target-arch") == 0) {
                     target_arch = argv[i];
                 } else if (strcmp(arg, "--target-os") == 0) {
@@ -258,6 +266,16 @@ int main(int argc, char **argv) {
             if (!in_file)
                 return usage(arg0);
 
+            if (cmd == CmdBuild && !out_name) {
+                fprintf(stderr, "--name [name] not provided\n\n");
+                return usage(arg0);
+            }
+
+            if (cmd == CmdBuild && out_type == OutTypeUnknown) {
+                fprintf(stderr, "--export [exe|lib|obj] not provided\n\n");
+                return usage(arg0);
+            }
+
             init_all_targets();
 
             ZigTarget alloc_target;
@@ -313,6 +331,8 @@ int main(int argc, char **argv) {
             codegen_set_is_release(g, is_release_build);
             codegen_set_is_test(g, cmd == CmdTest);
 
+            codegen_set_check_unused(g, check_unused);
+
             codegen_set_clang_argv(g, clang_argv.items, clang_argv.length);
             codegen_set_strip(g, strip);
             codegen_set_is_static(g, is_static);
@@ -342,6 +362,9 @@ int main(int argc, char **argv) {
             for (int i = 0; i < lib_dirs.length; i += 1) {
                 codegen_add_lib_dir(g, lib_dirs.at(i));
             }
+            for (int i = 0; i < link_libs.length; i += 1) {
+                codegen_add_link_lib(g, link_libs.at(i));
+            }
 
             codegen_set_windows_subsystem(g, mwindows, mconsole);
             codegen_set_windows_unicode(g, municode);
src/parseh.cpp
@@ -121,9 +121,9 @@ static AstNode *create_typed_var_decl_node(Context *c, bool is_const, const char
     AstNode *node = create_node(c, NodeTypeVariableDeclaration);
     buf_init_from_str(&node->data.variable_declaration.symbol, var_name);
     node->data.variable_declaration.is_const = is_const;
-    node->data.variable_declaration.visib_mod = c->visib_mod;
+    node->data.variable_declaration.top_level_decl.visib_mod = c->visib_mod;
     node->data.variable_declaration.expr = init_node;
-    node->data.variable_declaration.directives = nullptr;
+    node->data.variable_declaration.top_level_decl.directives = nullptr;
     node->data.variable_declaration.type = type_node;
     normalize_parent_ptrs(node);
     return node;
@@ -146,7 +146,7 @@ static AstNode *create_struct_field_node(Context *c, const char *name, AstNode *
     assert(type_node);
     AstNode *node = create_node(c, NodeTypeStructField);
     buf_init_from_str(&node->data.struct_field.name, name);
-    node->data.struct_field.visib_mod = VisibModPub;
+    node->data.struct_field.top_level_decl.visib_mod = VisibModPub;
     node->data.struct_field.type = type_node;
 
     normalize_parent_ptrs(node);
@@ -202,7 +202,7 @@ static AstNode *create_num_lit_signed(Context *c, int64_t x) {
 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.top_level_decl.visib_mod = c->visib_mod;
     node->data.type_decl.child_type = child_type_node;
 
     normalize_parent_ptrs(node);
@@ -219,7 +219,7 @@ static AstNode *create_fn_proto_node(Context *c, Buf *name, TypeTableEntry *fn_t
     assert(fn_type->id == TypeTableEntryIdFn);
     AstNode *node = create_node(c, NodeTypeFnProto);
     node->data.fn_proto.is_inline = true;
-    node->data.fn_proto.visib_mod = c->visib_mod;
+    node->data.fn_proto.top_level_decl.visib_mod = c->visib_mod;
     buf_init_from_buf(&node->data.fn_proto.name, name);
     node->data.fn_proto.return_type = make_type_node(c, fn_type->data.fn.fn_type_id.return_type);
 
@@ -677,7 +677,7 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
     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.top_level_decl.visib_mod = c->visib_mod;
     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);
 
@@ -861,7 +861,7 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {
     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.top_level_decl.visib_mod = VisibModExport;
     enum_node->data.struct_decl.type_entry = enum_type;
 
     for (uint32_t i = 0; i < field_count; i += 1) {
@@ -1043,7 +1043,7 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) {
         AstNode *struct_node = create_node(c, NodeTypeStructDecl);
         buf_init_from_buf(&struct_node->data.struct_decl.name, &struct_type->name);
         struct_node->data.struct_decl.kind = ContainerKindStruct;
-        struct_node->data.struct_decl.visib_mod = VisibModExport;
+        struct_node->data.struct_decl.top_level_decl.visib_mod = VisibModExport;
         struct_node->data.struct_decl.type_entry = struct_type;
 
         for (uint32_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) {
src/parser.cpp
@@ -20,7 +20,6 @@ struct ParseContext {
     ZigList<Token> *tokens;
     ImportTableEntry *owner;
     ErrColor err_color;
-    bool parsed_root_export;
     uint32_t *next_node_index;
 };
 
@@ -1741,8 +1740,8 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token
     AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, first_token);
 
     node->data.variable_declaration.is_const = is_const;
-    node->data.variable_declaration.visib_mod = visib_mod;
-    node->data.variable_declaration.directives = directives;
+    node->data.variable_declaration.top_level_decl.visib_mod = visib_mod;
+    node->data.variable_declaration.top_level_decl.directives = directives;
 
     Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol);
     ast_buf_from_token(pc, name_token, &node->data.variable_declaration.symbol);
@@ -2251,8 +2250,8 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand
     *token_index += 1;
 
     AstNode *node = ast_create_node(pc, NodeTypeFnProto, first_token);
-    node->data.fn_proto.visib_mod = visib_mod;
-    node->data.fn_proto.directives = directives;
+    node->data.fn_proto.top_level_decl.visib_mod = visib_mod;
+    node->data.fn_proto.top_level_decl.directives = directives;
 
     Token *fn_name = &pc->tokens->at(*token_index);
     if (fn_name->id == TokenIdSymbol) {
@@ -2345,76 +2344,23 @@ static AstNode *ast_parse_extern_decl(ParseContext *pc, int *token_index, bool m
 }
 
 /*
-RootExportDecl : "export" "Symbol" "String" ";"
+UseDecl = "use" Expression ";"
 */
-static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index,
-        ZigList<AstNode*> *directives)
-{
-    Token *export_type = &pc->tokens->at(*token_index);
-    if (export_type->id != TokenIdSymbol)
-        return nullptr;
-
-    *token_index += 1;
-
-    AstNode *node = ast_create_node(pc, NodeTypeRootExportDecl, export_type);
-    node->data.root_export_decl.directives = directives;
-
-    ast_buf_from_token(pc, export_type, &node->data.root_export_decl.type);
-
-    Token *export_name = &pc->tokens->at(*token_index);
-    *token_index += 1;
-    ast_expect_token(pc, export_name, TokenIdStringLiteral);
-
-    parse_string_literal(pc, export_name, &node->data.root_export_decl.name, nullptr, nullptr);
-
-    Token *semicolon = &pc->tokens->at(*token_index);
-    *token_index += 1;
-    ast_expect_token(pc, semicolon, TokenIdSemicolon);
-
-    normalize_parent_ptrs(node);
-    return node;
-}
-
-/*
-Import : "import" "String" ";"
-*/
-static AstNode *ast_parse_import(ParseContext *pc, int *token_index,
+static AstNode *ast_parse_use(ParseContext *pc, int *token_index,
         ZigList<AstNode*> *directives, VisibMod visib_mod)
 {
-    Token *import_kw = &pc->tokens->at(*token_index);
-    if (import_kw->id != TokenIdKeywordImport)
+    Token *use_kw = &pc->tokens->at(*token_index);
+    if (use_kw->id != TokenIdKeywordUse)
         return nullptr;
     *token_index += 1;
 
-    Token *import_name = ast_eat_token(pc, token_index, TokenIdStringLiteral);
+    AstNode *node = ast_create_node(pc, NodeTypeUse, use_kw);
+    node->data.use.top_level_decl.visib_mod = visib_mod;
+    node->data.use.top_level_decl.directives = directives;
+    node->data.use.expr = ast_parse_expression(pc, token_index, true);
 
     ast_eat_token(pc, token_index, TokenIdSemicolon);
 
-    AstNode *node = ast_create_node(pc, NodeTypeImport, import_kw);
-    node->data.import.visib_mod = visib_mod;
-    node->data.import.directives = directives;
-
-    parse_string_literal(pc, import_name, &node->data.import.path, nullptr, nullptr);
-    normalize_parent_ptrs(node);
-    return node;
-}
-
-/*
-CImportDecl : "c_import" Block
-*/
-static AstNode *ast_parse_c_import(ParseContext *pc, int *token_index,
-        ZigList<AstNode*> *directives, VisibMod visib_mod)
-{
-    Token *c_import_kw = &pc->tokens->at(*token_index);
-    if (c_import_kw->id != TokenIdKeywordCImport)
-        return nullptr;
-    *token_index += 1;
-
-    AstNode *node = ast_create_node(pc, NodeTypeCImport, c_import_kw);
-    node->data.c_import.visib_mod = visib_mod;
-    node->data.c_import.directives = directives;
-    node->data.c_import.block = ast_parse_block(pc, token_index, true);
-
     normalize_parent_ptrs(node);
     return node;
 }
@@ -2445,8 +2391,8 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index,
     AstNode *node = ast_create_node(pc, NodeTypeStructDecl, first_token);
     node->data.struct_decl.kind = kind;
     ast_buf_from_token(pc, struct_name, &node->data.struct_decl.name);
-    node->data.struct_decl.visib_mod = visib_mod;
-    node->data.struct_decl.directives = directives;
+    node->data.struct_decl.top_level_decl.visib_mod = visib_mod;
+    node->data.struct_decl.top_level_decl.directives = directives;
 
     ast_eat_token(pc, token_index, TokenIdLBrace);
 
@@ -2486,8 +2432,8 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index,
             AstNode *field_node = ast_create_node(pc, NodeTypeStructField, token);
             *token_index += 1;
 
-            field_node->data.struct_field.visib_mod = visib_mod;
-            field_node->data.struct_field.directives = directive_list;
+            field_node->data.struct_field.top_level_decl.visib_mod = visib_mod;
+            field_node->data.struct_field.top_level_decl.directives = directive_list;
 
             ast_buf_from_token(pc, token, &field_node->data.struct_field.name);
 
@@ -2529,8 +2475,8 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index,
     ast_eat_token(pc, token_index, TokenIdSemicolon);
 
     AstNode *node = ast_create_node(pc, NodeTypeErrorValueDecl, first_token);
-    node->data.error_value_decl.visib_mod = visib_mod;
-    node->data.error_value_decl.directives = directives;
+    node->data.error_value_decl.top_level_decl.visib_mod = visib_mod;
+    node->data.error_value_decl.top_level_decl.directives = directives;
     ast_buf_from_token(pc, name_tok, &node->data.error_value_decl.name);
 
     normalize_parent_ptrs(node);
@@ -2559,15 +2505,15 @@ static AstNode *ast_parse_type_decl(ParseContext *pc, int *token_index,
 
     ast_eat_token(pc, token_index, TokenIdSemicolon);
 
-    node->data.type_decl.visib_mod = visib_mod;
-    node->data.type_decl.directives = directives;
+    node->data.type_decl.top_level_decl.visib_mod = visib_mod;
+    node->data.type_decl.top_level_decl.directives = directives;
 
     normalize_parent_ptrs(node);
     return node;
 }
 
 /*
-TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl | TypeDecl)
+TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl | TypeDecl)
 */
 static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) {
     for (;;) {
@@ -2587,17 +2533,6 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis
             visib_mod = VisibModPrivate;
         }
 
-        bool try_to_parse_root_export = (visib_mod == VisibModExport && !pc->parsed_root_export);
-        pc->parsed_root_export = true;
-
-        if (try_to_parse_root_export) {
-            AstNode *root_export_decl_node = ast_parse_root_export_decl(pc, token_index, directives);
-            if (root_export_decl_node) {
-                top_level_decls->append(root_export_decl_node);
-                continue;
-            }
-        }
-
         AstNode *fn_def_node = ast_parse_fn_def(pc, token_index, false, directives, visib_mod);
         if (fn_def_node) {
             top_level_decls->append(fn_def_node);
@@ -2610,15 +2545,9 @@ static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigLis
             continue;
         }
 
-        AstNode *import_node = ast_parse_import(pc, token_index, directives, visib_mod);
-        if (import_node) {
-            top_level_decls->append(import_node);
-            continue;
-        }
-
-        AstNode *c_import_node = ast_parse_c_import(pc, token_index, directives, visib_mod);
-        if (c_import_node) {
-            top_level_decls->append(c_import_node);
+        AstNode *use_node = ast_parse_use(pc, token_index, directives, visib_mod);
+        if (use_node) {
+            top_level_decls->append(use_node);
             continue;
         }
 
@@ -2706,12 +2635,9 @@ void normalize_parent_ptrs(AstNode *node) {
         case NodeTypeRoot:
             set_list_fields(&node->data.root.top_level_decls);
             break;
-        case NodeTypeRootExportDecl:
-            set_list_fields(node->data.root_export_decl.directives);
-            break;
         case NodeTypeFnProto:
             set_field(&node->data.fn_proto.return_type);
-            set_list_fields(node->data.fn_proto.directives);
+            set_list_fields(node->data.fn_proto.top_level_decl.directives);
             set_list_fields(&node->data.fn_proto.params);
             break;
         case NodeTypeFnDef:
@@ -2737,12 +2663,12 @@ void normalize_parent_ptrs(AstNode *node) {
             set_field(&node->data.defer.expr);
             break;
         case NodeTypeVariableDeclaration:
-            set_list_fields(node->data.variable_declaration.directives);
+            set_list_fields(node->data.variable_declaration.top_level_decl.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_list_fields(node->data.type_decl.top_level_decl.directives);
             set_field(&node->data.type_decl.child_type);
             break;
         case NodeTypeErrorValueDecl:
@@ -2788,12 +2714,9 @@ void normalize_parent_ptrs(AstNode *node) {
         case NodeTypeFieldAccessExpr:
             set_field(&node->data.field_access_expr.struct_expr);
             break;
-        case NodeTypeImport:
-            set_list_fields(node->data.import.directives);
-            break;
-        case NodeTypeCImport:
-            set_list_fields(node->data.c_import.directives);
-            set_field(&node->data.c_import.block);
+        case NodeTypeUse:
+            set_field(&node->data.use.expr);
+            set_list_fields(node->data.use.top_level_decl.directives);
             break;
         case NodeTypeBoolLiteral:
             // none
@@ -2863,11 +2786,11 @@ void normalize_parent_ptrs(AstNode *node) {
         case NodeTypeStructDecl:
             set_list_fields(&node->data.struct_decl.fields);
             set_list_fields(&node->data.struct_decl.fns);
-            set_list_fields(node->data.struct_decl.directives);
+            set_list_fields(node->data.struct_decl.top_level_decl.directives);
             break;
         case NodeTypeStructField:
             set_field(&node->data.struct_field.type);
-            set_list_fields(node->data.struct_field.directives);
+            set_list_fields(node->data.struct_field.top_level_decl.directives);
             break;
         case NodeTypeContainerInitExpr:
             set_field(&node->data.container_init_expr.type);
src/tokenizer.cpp
@@ -99,7 +99,7 @@
 
 const char * zig_keywords[] = {
     "true", "false", "null", "fn", "return", "var", "const", "extern",
-    "pub", "export", "import", "c_import", "if", "else", "goto", "asm",
+    "pub", "export", "use", "if", "else", "goto", "asm",
     "volatile", "struct", "enum", "while", "for", "continue", "break",
     "null", "noalias", "switch", "undefined", "error", "type", "inline",
     "defer",
@@ -232,10 +232,8 @@ static void end_token(Tokenize *t) {
         t->cur_tok->id = TokenIdKeywordPub;
     } else if (mem_eql_str(token_mem, token_len, "export")) {
         t->cur_tok->id = TokenIdKeywordExport;
-    } else if (mem_eql_str(token_mem, token_len, "c_import")) {
-        t->cur_tok->id = TokenIdKeywordCImport;
-    } else if (mem_eql_str(token_mem, token_len, "import")) {
-        t->cur_tok->id = TokenIdKeywordImport;
+    } else if (mem_eql_str(token_mem, token_len, "use")) {
+        t->cur_tok->id = TokenIdKeywordUse;
     } else if (mem_eql_str(token_mem, token_len, "true")) {
         t->cur_tok->id = TokenIdKeywordTrue;
     } else if (mem_eql_str(token_mem, token_len, "false")) {
@@ -1071,8 +1069,7 @@ const char * token_name(TokenId id) {
         case TokenIdKeywordExtern: return "extern";
         case TokenIdKeywordPub: return "pub";
         case TokenIdKeywordExport: return "export";
-        case TokenIdKeywordImport: return "import";
-        case TokenIdKeywordCImport: return "c_import";
+        case TokenIdKeywordUse: return "use";
         case TokenIdKeywordTrue: return "true";
         case TokenIdKeywordFalse: return "false";
         case TokenIdKeywordIf: return "if";
src/tokenizer.hpp
@@ -19,9 +19,8 @@ enum TokenId {
     TokenIdKeywordConst,
     TokenIdKeywordExtern,
     TokenIdKeywordPub,
+    TokenIdKeywordUse,
     TokenIdKeywordExport,
-    TokenIdKeywordImport,
-    TokenIdKeywordCImport,
     TokenIdKeywordTrue,
     TokenIdKeywordFalse,
     TokenIdKeywordIf,
std/bootstrap.zig
@@ -1,7 +1,7 @@
-import "syscall.zig";
+// This file is in a package which has the root source file exposed as "@root".
 
-// The compiler treats this file special by implicitly importing the function `main`
-// from the root source file as the symbol `zig_user_main`.
+const root = @import("@root");
+const syscall = @import("syscall.zig");
 
 const want_start_symbol = switch(@compile_var("os")) {
     linux => true,
@@ -26,7 +26,7 @@ export fn _start() -> unreachable {
         },
         else => unreachable{},
     }
-    call_main()
+    call_main_and_exit()
 }
 
 fn strlen(ptr: &const u8) -> isize {
@@ -37,23 +37,24 @@ fn strlen(ptr: &const u8) -> isize {
     return count;
 }
 
-fn call_main() -> unreachable {
+fn call_main() -> %void {
     var args: [argc][]u8 = undefined;
     for (args) |arg, i| {
         const ptr = argv[i];
         args[i] = ptr[0...strlen(ptr)];
     }
-    zig_user_main(args) %% exit(1);
-    exit(0);
+    return root.main(args);
+}
+
+fn call_main_and_exit() -> unreachable {
+    call_main() %% syscall.exit(1);
+    syscall.exit(0);
 }
 
 #condition(want_main_symbol)
-export fn main(argc: i32, argv: &&u8) -> i32 {
-    var args: [argc][]u8 = undefined;
-    for (args) |arg, i| {
-        const ptr = argv[i];
-        args[i] = ptr[0...strlen(ptr)];
-    }
-    zig_user_main(args) %% return 1;
+export fn main(c_argc: i32, c_argv: &&u8) -> i32 {
+    argc = c_argc;
+    argv = c_argv;
+    call_main() %% return 1;
     return 0;
 }
std/index.zig
@@ -0,0 +1,4 @@
+pub const Rand = @import("rand.zig").Rand;
+pub const io = @import("io.zig");
+pub const os = @import("os.zig");
+pub const math = @import("math.zig");
std/std.zig โ†’ std/io.zig
@@ -1,6 +1,6 @@
-import "syscall.zig";
-import "errno.zig";
-import "math.zig";
+const syscall = @import("syscall.zig");
+const errno = @import("errno.zig");
+const math = @import("math.zig");
 
 pub const stdin_fileno = 0;
 pub const stdout_fileno = 1;
@@ -55,7 +55,7 @@ pub struct OutStream {
         const dest_space_left = os.buffer.len - os.index;
 
         while (src_bytes_left > 0) {
-            const copy_amt = min_isize(dest_space_left, src_bytes_left);
+            const copy_amt = math.min_isize(dest_space_left, src_bytes_left);
             @memcpy(&os.buffer[os.index], &str[src_index], copy_amt);
             os.index += copy_amt;
             if (os.index == os.buffer.len) {
@@ -105,19 +105,19 @@ pub struct OutStream {
     }
 
     pub fn flush(os: &OutStream) -> %void {
-        const amt_written = write(os.fd, &os.buffer[0], os.index);
+        const amt_written = syscall.write(os.fd, &os.buffer[0], os.index);
         os.index = 0;
         if (amt_written < 0) {
             return switch (-amt_written) {
-                EINVAL => unreachable{},
-                EDQUOT => error.DiskQuota,
-                EFBIG  => error.FileTooBig,
-                EINTR  => error.SigInterrupt,
-                EIO    => error.Io,
-                ENOSPC => error.NoSpaceLeft,
-                EPERM  => error.BadPerm,
-                EPIPE  => error.PipeFail,
-                else   => error.Unexpected,
+                errno.EINVAL => unreachable{},
+                errno.EDQUOT => error.DiskQuota,
+                errno.EFBIG  => error.FileTooBig,
+                errno.EINTR  => error.SigInterrupt,
+                errno.EIO    => error.Io,
+                errno.ENOSPC => error.NoSpaceLeft,
+                errno.EPERM  => error.BadPerm,
+                errno.EPIPE  => error.PipeFail,
+                else         => error.Unexpected,
             }
         }
     }
@@ -139,15 +139,15 @@ pub struct InStream {
     fd: isize,
 
     pub fn read(is: &InStream, buf: []u8) -> %isize {
-        const amt_read = read(is.fd, &buf[0], buf.len);
+        const amt_read = syscall.read(is.fd, &buf[0], buf.len);
         if (amt_read < 0) {
             return switch (-amt_read) {
-                EINVAL => unreachable{},
-                EFAULT => unreachable{},
-                EBADF  => error.BadFd,
-                EINTR  => error.SigInterrupt,
-                EIO    => error.Io,
-                else   => error.Unexpected,
+                errno.EINVAL => unreachable{},
+                errno.EFAULT => unreachable{},
+                errno.EBADF  => error.BadFd,
+                errno.EINTR  => error.SigInterrupt,
+                errno.EIO    => error.Io,
+                else         => error.Unexpected,
             }
         }
         return amt_read;
@@ -168,8 +168,8 @@ pub struct InStream {
 
 #attribute("cold")
 pub fn abort() -> unreachable {
-    raise(SIGABRT);
-    raise(SIGKILL);
+    syscall.raise(syscall.SIGABRT);
+    syscall.raise(syscall.SIGKILL);
     while (true) {}
 }
 
@@ -253,15 +253,15 @@ pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize {
         decs = max_u64_base10_digits - 1;
     }
 
-    if (x == f64_get_pos_inf()) {
+    if (x == math.f64_get_pos_inf()) {
         const buf2 = "+Inf";
         @memcpy(&out_buf[0], &buf2[0], buf2.len);
         return 4;
-    } else if (x == f64_get_neg_inf()) {
+    } else if (x == math.f64_get_neg_inf()) {
         const buf2 = "-Inf";
         @memcpy(&out_buf[0], &buf2[0], buf2.len);
         return 4;
-    } else if (f64_is_nan(x)) {
+    } else if (math.f64_is_nan(x)) {
         const buf2 = "NaN";
         @memcpy(&out_buf[0], &buf2[0], buf2.len);
         return 3;
@@ -275,7 +275,7 @@ pub fn buf_print_f64(out_buf: []u8, x: f64, decimals: isize) -> isize {
     // 11 exponent bits
     // 52 significand bits (+ 1 implicit always non-zero bit)
 
-    const bits = f64_to_bits(x);
+    const bits = math.f64_to_bits(x);
     if (bits & (1 << 63) != 0) {
         buf[0] = '-';
         len += 1;
std/os.zig
@@ -1,19 +1,19 @@
-import "syscall.zig";
-import "errno.zig";
+const syscall = @import("syscall.zig");
+const errno = @import("errno.zig");
 
 pub error SigInterrupt;
 pub error Unexpected;
 
-pub fn os_get_random_bytes(buf: []u8) -> %void {
+pub fn get_random_bytes(buf: []u8) -> %void {
     switch (@compile_var("os")) {
         linux => {
-            const amt_got = getrandom(buf.ptr, buf.len, 0);
+            const amt_got = syscall.getrandom(buf.ptr, buf.len, 0);
             if (amt_got < 0) {
                 return switch (-amt_got) {
-                    EINVAL => unreachable{},
-                    EFAULT => unreachable{},
-                    EINTR  => error.SigInterrupt,
-                    else   => error.Unexpected,
+                    errno.EINVAL => unreachable{},
+                    errno.EFAULT => unreachable{},
+                    errno.EINTR  => error.SigInterrupt,
+                    else         => error.Unexpected,
                 }
             }
         },
std/rand.zig
@@ -84,26 +84,26 @@ pub struct Rand {
         }
         return bytes_left;
     }
-}
 
-/// Initialize random state with the given seed.
-pub fn rand_new(seed: u32) -> Rand {
-    var r: Rand = undefined;
-    r.index = 0;
-    r.array[0] = seed;
-    var i : isize = 1;
-    var prev_value: u64 = seed;
-    while (i < ARRAY_SIZE) {
-        r.array[i] = u32((prev_value ^ (prev_value << 30)) * 0x6c078965 + u32(i));
-        prev_value = r.array[i];
-        i += 1;
+    /// Initialize random state with the given seed.
+    pub fn init(seed: u32) -> Rand {
+        var r: Rand = undefined;
+        r.index = 0;
+        r.array[0] = seed;
+        var i : isize = 1;
+        var prev_value: u64 = seed;
+        while (i < ARRAY_SIZE) {
+            r.array[i] = u32((prev_value ^ (prev_value << 30)) * 0x6c078965 + u32(i));
+            prev_value = r.array[i];
+            i += 1;
+        }
+        return r;
     }
-    return r;
 }
 
 #attribute("test")
 fn test_float32() {
-    var r = rand_new(42);
+    var r = Rand.init(42);
 
     // TODO for loop with range
     var i: i32 = 0;
std/test_runner.zig
@@ -1,4 +1,4 @@
-import "std.zig";
+const io = @import("std").io;
 
 struct TestFn {
     name: []u8,
@@ -9,19 +9,19 @@ extern var zig_test_fn_list: []TestFn;
 
 pub fn run_tests() -> %void {
     for (zig_test_fn_list) |test_fn, i| {
-        %%stderr.print_str("Test ");
-        %%stderr.print_i64(i + 1);
-        %%stderr.print_str("/");
-        %%stderr.print_i64(zig_test_fn_list.len);
-        %%stderr.print_str(" ");
-        %%stderr.print_str(test_fn.name);
-        %%stderr.print_str("...");
-        %%stderr.flush();
+        %%io.stderr.print_str("Test ");
+        %%io.stderr.print_i64(i + 1);
+        %%io.stderr.print_str("/");
+        %%io.stderr.print_i64(zig_test_fn_list.len);
+        %%io.stderr.print_str(" ");
+        %%io.stderr.print_str(test_fn.name);
+        %%io.stderr.print_str("...");
+        %%io.stderr.flush();
 
         test_fn.func();
 
 
-        %%stderr.print_str("OK\n");
-        %%stderr.flush();
+        %%io.stderr.print_str("OK\n");
+        %%io.stderr.flush();
     }
 }
std/test_runner_libc.zig
@@ -1,6 +1,6 @@
-import "test_runner.zig";
+const test_runner = @import("test_runner.zig");
 
 export fn main(argc: c_int, argv: &&u8) -> c_int {
-    run_tests() %% return -1;
+    test_runner.run_tests() %% return -1;
     return 0;
 }
std/test_runner_nolibc.zig
@@ -1,5 +1,5 @@
-import "test_runner.zig";
+const test_runner = @import("test_runner.zig");
 
 pub fn main(args: [][]u8) -> %void {
-    return run_tests();
+    return test_runner.run_tests();
 }
test/run_tests.cpp
@@ -70,12 +70,20 @@ static TestCase *add_simple_case(const char *case_name, const char *source, cons
     test_case->compiler_args.append("--strip");
     test_case->compiler_args.append("--color");
     test_case->compiler_args.append("on");
+    test_case->compiler_args.append("--check-unused");
 
     test_cases.append(test_case);
 
     return test_case;
 }
 
+static TestCase *add_simple_case_libc(const char *case_name, const char *source, const char *output) {
+    TestCase *tc = add_simple_case(case_name, source, output);
+    tc->compiler_args.append("--library");
+    tc->compiler_args.append("c");
+    return tc;
+}
+
 static TestCase *add_compile_fail_case(const char *case_name, const char *source, int count, ...) {
     va_list ap;
     va_start(ap, count);
@@ -93,11 +101,19 @@ static TestCase *add_compile_fail_case(const char *case_name, const char *source
 
     test_case->compiler_args.append("build");
     test_case->compiler_args.append(tmp_source_path);
+
+    test_case->compiler_args.append("--name");
+    test_case->compiler_args.append("test");
+
+    test_case->compiler_args.append("--export");
+    test_case->compiler_args.append("obj");
+
     test_case->compiler_args.append("--output");
     test_case->compiler_args.append(tmp_exe_path);
+
     test_case->compiler_args.append("--release");
     test_case->compiler_args.append("--strip");
-    //test_case->compiler_args.append("--verbose");
+    test_case->compiler_args.append("--check-unused");
 
     test_cases.append(test_case);
 
@@ -134,43 +150,18 @@ static TestCase *add_parseh_case(const char *case_name, const char *source, int
 }
 
 static void add_compiling_test_cases(void) {
-    add_simple_case("hello world with libc", R"SOURCE(
-#link("c")
-export executable "test";
-
-c_import {
-    @c_include("stdio.h");
-}
-
+    add_simple_case_libc("hello world with libc", R"SOURCE(
+const c = @c_import(@c_include("stdio.h"));
 export fn main(argc: c_int, argv: &&u8) -> c_int {
-    puts(c"Hello, world!");
+    c.puts(c"Hello, world!");
     return 0;
 }
     )SOURCE", "Hello, world!" NL);
 
-    add_simple_case("function call", R"SOURCE(
-import "std.zig";
-import "syscall.zig";
-
-fn empty_function_1() {}
-fn empty_function_2() { return; }
-
-pub fn main(args: [][]u8) -> %void {
-    empty_function_1();
-    empty_function_2();
-    this_is_a_function();
-}
-
-fn this_is_a_function() -> unreachable {
-    %%stdout.printf("OK\n");
-    exit(0);
-}
-    )SOURCE", "OK\n");
-
     {
         TestCase *tc = add_simple_case("multiple files with private function", R"SOURCE(
-import "std.zig";
-import "foo.zig";
+use @import("std").io;
+use @import("foo.zig");
 
 pub fn main(args: [][]u8) -> %void {
     private_function();
@@ -183,7 +174,7 @@ fn private_function() {
         )SOURCE", "OK 1\nOK 2\n");
 
         add_source_file(tc, "foo.zig", R"SOURCE(
-import "std.zig";
+use @import("std").io;
 
 // purposefully conflicting function with main.zig
 // but it's private so it should be OK
@@ -199,8 +190,8 @@ pub fn print_text() {
 
     {
         TestCase *tc = add_simple_case("import segregation", R"SOURCE(
-import "foo.zig";
-import "bar.zig";
+use @import("foo.zig");
+use @import("bar.zig");
 
 pub fn main(args: [][]u8) -> %void {
     foo_function();
@@ -209,15 +200,15 @@ pub fn main(args: [][]u8) -> %void {
         )SOURCE", "OK\nOK\n");
 
         add_source_file(tc, "foo.zig", R"SOURCE(
-import "std.zig";
+use @import("std").io;
 pub fn foo_function() {
     %%stdout.printf("OK\n");
 }
         )SOURCE");
 
         add_source_file(tc, "bar.zig", R"SOURCE(
-import "other.zig";
-import "std.zig";
+use @import("other.zig");
+use @import("std").io;
 
 pub fn bar_function() {
     if (foo_function()) {
@@ -234,8 +225,35 @@ pub fn foo_function() -> bool {
         )SOURCE");
     }
 
+    {
+        TestCase *tc = add_simple_case("two files use import each other", R"SOURCE(
+use @import("a.zig");
+
+pub fn main(args: [][]u8) -> %void {
+    ok();
+}
+        )SOURCE", "OK\n");
+
+        add_source_file(tc, "a.zig", R"SOURCE(
+use @import("b.zig");
+const io = @import("std").io;
+
+pub const a_text = "OK\n";
+
+pub fn ok() {
+    %%io.stdout.printf(b_text);
+}
+        )SOURCE");
+
+        add_source_file(tc, "b.zig", R"SOURCE(
+use @import("a.zig");
+
+pub const b_text = a_text;
+        )SOURCE");
+    }
+
     add_simple_case("params", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 fn add(a: i32, b: i32) -> i32 {
     a + b
@@ -243,13 +261,13 @@ fn add(a: i32, b: i32) -> i32 {
 
 pub fn main(args: [][]u8) -> %void {
     if (add(22, 11) == 33) {
-        %%stdout.printf("pass\n");
+        %%io.stdout.printf("pass\n");
     }
 }
     )SOURCE", "pass\n");
 
     add_simple_case("void parameters", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
     void_fun(1, void{}, 2);
@@ -258,28 +276,28 @@ pub fn main(args: [][]u8) -> %void {
 fn void_fun(a : i32, b : void, c : i32) {
     const v = b;
     const vv : void = if (a == 1) {v} else {};
-    if (a + c == 3) { %%stdout.printf("OK\n"); }
+    if (a + c == 3) { %%io.stdout.printf("OK\n"); }
     return vv;
 }
     )SOURCE", "OK\n");
 
     add_simple_case("mutable local variables", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
     var zero : i32 = 0;
-    if (zero == 0) { %%stdout.printf("zero\n"); }
+    if (zero == 0) { %%io.stdout.printf("zero\n"); }
 
     var i = i32(0);
     while (i != 3) {
-        %%stdout.printf("loop\n");
+        %%io.stdout.printf("loop\n");
         i += 1;
     }
 }
     )SOURCE", "zero\nloop\nloop\nloop\n");
 
     add_simple_case("arrays", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
     var array : [5]i32 = undefined;
@@ -299,11 +317,11 @@ pub fn main(args: [][]u8) -> %void {
     }
 
     if (accumulator == 15) {
-        %%stdout.printf("OK\n");
+        %%io.stdout.printf("OK\n");
     }
 
     if (get_array_len(array) != 5) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
 }
 fn get_array_len(a: []i32) -> isize {
@@ -313,144 +331,139 @@ fn get_array_len(a: []i32) -> isize {
 
 
     add_simple_case("hello world without libc", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
-    %%stdout.printf("Hello, world!\n");
+    %%io.stdout.printf("Hello, world!\n");
 }
     )SOURCE", "Hello, world!\n");
 
 
     add_simple_case("short circuit", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
-    if (true || { %%stdout.printf("BAD 1\n"); false }) {
-      %%stdout.printf("OK 1\n");
+    if (true || { %%io.stdout.printf("BAD 1\n"); false }) {
+      %%io.stdout.printf("OK 1\n");
     }
-    if (false || { %%stdout.printf("OK 2\n"); false }) {
-      %%stdout.printf("BAD 2\n");
+    if (false || { %%io.stdout.printf("OK 2\n"); false }) {
+      %%io.stdout.printf("BAD 2\n");
     }
 
-    if (true && { %%stdout.printf("OK 3\n"); false }) {
-      %%stdout.printf("BAD 3\n");
+    if (true && { %%io.stdout.printf("OK 3\n"); false }) {
+      %%io.stdout.printf("BAD 3\n");
     }
-    if (false && { %%stdout.printf("BAD 4\n"); false }) {
+    if (false && { %%io.stdout.printf("BAD 4\n"); false }) {
     } else {
-      %%stdout.printf("OK 4\n");
+      %%io.stdout.printf("OK 4\n");
     }
 }
     )SOURCE", "OK 1\nOK 2\nOK 3\nOK 4\n");
 
     add_simple_case("modify operators", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
     var i : i32 = 0;
-    i += 5;  if (i != 5)  { %%stdout.printf("BAD +=\n"); }
-    i -= 2;  if (i != 3)  { %%stdout.printf("BAD -=\n"); }
-    i *= 20; if (i != 60) { %%stdout.printf("BAD *=\n"); }
-    i /= 3;  if (i != 20) { %%stdout.printf("BAD /=\n"); }
-    i %= 11; if (i != 9)  { %%stdout.printf("BAD %=\n"); }
-    i <<= 1; if (i != 18) { %%stdout.printf("BAD <<=\n"); }
-    i >>= 2; if (i != 4)  { %%stdout.printf("BAD >>=\n"); }
+    i += 5;  if (i != 5)  { %%io.stdout.printf("BAD +=\n"); }
+    i -= 2;  if (i != 3)  { %%io.stdout.printf("BAD -=\n"); }
+    i *= 20; if (i != 60) { %%io.stdout.printf("BAD *=\n"); }
+    i /= 3;  if (i != 20) { %%io.stdout.printf("BAD /=\n"); }
+    i %= 11; if (i != 9)  { %%io.stdout.printf("BAD %=\n"); }
+    i <<= 1; if (i != 18) { %%io.stdout.printf("BAD <<=\n"); }
+    i >>= 2; if (i != 4)  { %%io.stdout.printf("BAD >>=\n"); }
     i = 6;
-    i &= 5;  if (i != 4)  { %%stdout.printf("BAD &=\n"); }
-    i ^= 6;  if (i != 2)  { %%stdout.printf("BAD ^=\n"); }
+    i &= 5;  if (i != 4)  { %%io.stdout.printf("BAD &=\n"); }
+    i ^= 6;  if (i != 2)  { %%io.stdout.printf("BAD ^=\n"); }
     i = 6;
-    i |= 3;  if (i != 7)  { %%stdout.printf("BAD |=\n"); }
+    i |= 3;  if (i != 7)  { %%io.stdout.printf("BAD |=\n"); }
 
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
-    add_simple_case("number literals", R"SOURCE(
-#link("c")
-export executable "test";
-
-c_import {
-    @c_include("stdio.h");
-}
+    add_simple_case_libc("number literals", R"SOURCE(
+const c = @c_import(@c_include("stdio.h"));
 
 export fn main(argc: c_int, argv: &&u8) -> c_int {
-    printf(c"\n");
+    c.printf(c"\n");
 
-    printf(c"0: %llu\n",
+    c.printf(c"0: %llu\n",
              u64(0));
-    printf(c"320402575052271: %llu\n",
+    c.printf(c"320402575052271: %llu\n",
          u64(320402575052271));
-    printf(c"0x01236789abcdef: %llu\n",
+    c.printf(c"0x01236789abcdef: %llu\n",
          u64(0x01236789abcdef));
-    printf(c"0xffffffffffffffff: %llu\n",
+    c.printf(c"0xffffffffffffffff: %llu\n",
          u64(0xffffffffffffffff));
-    printf(c"0x000000ffffffffffffffff: %llu\n",
+    c.printf(c"0x000000ffffffffffffffff: %llu\n",
          u64(0x000000ffffffffffffffff));
-    printf(c"0o1777777777777777777777: %llu\n",
+    c.printf(c"0o1777777777777777777777: %llu\n",
          u64(0o1777777777777777777777));
-    printf(c"0o0000001777777777777777777777: %llu\n",
+    c.printf(c"0o0000001777777777777777777777: %llu\n",
          u64(0o0000001777777777777777777777));
-    printf(c"0b1111111111111111111111111111111111111111111111111111111111111111: %llu\n",
+    c.printf(c"0b1111111111111111111111111111111111111111111111111111111111111111: %llu\n",
          u64(0b1111111111111111111111111111111111111111111111111111111111111111));
-    printf(c"0b0000001111111111111111111111111111111111111111111111111111111111111111: %llu\n",
+    c.printf(c"0b0000001111111111111111111111111111111111111111111111111111111111111111: %llu\n",
          u64(0b0000001111111111111111111111111111111111111111111111111111111111111111));
 
-    printf(c"\n");
+    c.printf(c"\n");
 
-    printf(c"0.0: %a\n",
+    c.printf(c"0.0: %a\n",
          f64(0.0));
-    printf(c"0e0: %a\n",
+    c.printf(c"0e0: %a\n",
          f64(0e0));
-    printf(c"0.0e0: %a\n",
+    c.printf(c"0.0e0: %a\n",
          f64(0.0e0));
-    printf(c"000000000000000000000000000000000000000000000000000000000.0e0: %a\n",
+    c.printf(c"000000000000000000000000000000000000000000000000000000000.0e0: %a\n",
          f64(000000000000000000000000000000000000000000000000000000000.0e0));
-    printf(c"0.000000000000000000000000000000000000000000000000000000000e0: %a\n",
+    c.printf(c"0.000000000000000000000000000000000000000000000000000000000e0: %a\n",
          f64(0.000000000000000000000000000000000000000000000000000000000e0));
-    printf(c"0.0e000000000000000000000000000000000000000000000000000000000: %a\n",
+    c.printf(c"0.0e000000000000000000000000000000000000000000000000000000000: %a\n",
          f64(0.0e000000000000000000000000000000000000000000000000000000000));
-    printf(c"1.0: %a\n",
+    c.printf(c"1.0: %a\n",
          f64(1.0));
-    printf(c"10.0: %a\n",
+    c.printf(c"10.0: %a\n",
          f64(10.0));
-    printf(c"10.5: %a\n",
+    c.printf(c"10.5: %a\n",
          f64(10.5));
-    printf(c"10.5e5: %a\n",
+    c.printf(c"10.5e5: %a\n",
          f64(10.5e5));
-    printf(c"10.5e+5: %a\n",
+    c.printf(c"10.5e+5: %a\n",
          f64(10.5e+5));
-    printf(c"50.0e-2: %a\n",
+    c.printf(c"50.0e-2: %a\n",
          f64(50.0e-2));
-    printf(c"50e-2: %a\n",
+    c.printf(c"50e-2: %a\n",
          f64(50e-2));
 
-    printf(c"\n");
+    c.printf(c"\n");
 
-    printf(c"0x1.0: %a\n",
+    c.printf(c"0x1.0: %a\n",
          f64(0x1.0));
-    printf(c"0x10.0: %a\n",
+    c.printf(c"0x10.0: %a\n",
          f64(0x10.0));
-    printf(c"0x100.0: %a\n",
+    c.printf(c"0x100.0: %a\n",
          f64(0x100.0));
-    printf(c"0x103.0: %a\n",
+    c.printf(c"0x103.0: %a\n",
          f64(0x103.0));
-    printf(c"0x103.7: %a\n",
+    c.printf(c"0x103.7: %a\n",
          f64(0x103.7));
-    printf(c"0x103.70: %a\n",
+    c.printf(c"0x103.70: %a\n",
          f64(0x103.70));
-    printf(c"0x103.70p4: %a\n",
+    c.printf(c"0x103.70p4: %a\n",
          f64(0x103.70p4));
-    printf(c"0x103.70p5: %a\n",
+    c.printf(c"0x103.70p5: %a\n",
          f64(0x103.70p5));
-    printf(c"0x103.70p+5: %a\n",
+    c.printf(c"0x103.70p+5: %a\n",
          f64(0x103.70p+5));
-    printf(c"0x103.70p-5: %a\n",
+    c.printf(c"0x103.70p-5: %a\n",
          f64(0x103.70p-5));
 
-    printf(c"\n");
+    c.printf(c"\n");
 
-    printf(c"0b10100.00010e0: %a\n",
+    c.printf(c"0b10100.00010e0: %a\n",
          f64(0b10100.00010e0));
-    printf(c"0o10700.00010e0: %a\n",
+    c.printf(c"0o10700.00010e0: %a\n",
          f64(0o10700.00010e0));
 
     return 0;
@@ -496,7 +509,7 @@ export fn main(argc: c_int, argv: &&u8) -> c_int {
 )OUTPUT");
 
     add_simple_case("structs", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
     var foo : Foo = undefined;
@@ -506,12 +519,12 @@ pub fn main(args: [][]u8) -> %void {
     test_foo(foo);
     test_mutation(&foo);
     if (foo.c != 100) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
     test_point_to_self();
     test_byval_assign();
     test_initializer();
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
 struct Foo {
     a : i32,
@@ -520,7 +533,7 @@ struct Foo {
 }
 fn test_foo(foo : Foo) {
     if (!foo.b) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
 }
 fn test_mutation(foo : &Foo) {
@@ -545,7 +558,7 @@ fn test_point_to_self() {
     root.next = &node;
 
     if (node.next.next.next.val.x != 1) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
 }
 fn test_byval_assign() {
@@ -554,38 +567,38 @@ fn test_byval_assign() {
 
     foo1.a = 1234;
 
-    if (foo2.a != 0) { %%stdout.printf("BAD\n"); }
+    if (foo2.a != 0) { %%io.stdout.printf("BAD\n"); }
 
     foo2 = foo1;
 
-    if (foo2.a != 1234) { %%stdout.printf("BAD - byval assignment failed\n"); }
+    if (foo2.a != 1234) { %%io.stdout.printf("BAD - byval assignment failed\n"); }
 }
 fn test_initializer() {
     const val = Val { .x = 42 };
-    if (val.x != 42) { %%stdout.printf("BAD\n"); }
+    if (val.x != 42) { %%io.stdout.printf("BAD\n"); }
 }
     )SOURCE", "OK\n");
 
     add_simple_case("global variables", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 const g1 : i32 = 1233 + 1;
 var g2 : i32 = 0;
 
 pub fn main(args: [][]u8) -> %void {
-    if (g2 != 0) { %%stdout.printf("BAD\n"); }
+    if (g2 != 0) { %%io.stdout.printf("BAD\n"); }
     g2 = g1;
-    if (g2 != 1234) { %%stdout.printf("BAD\n"); }
-    %%stdout.printf("OK\n");
+    if (g2 != 1234) { %%io.stdout.printf("BAD\n"); }
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("while loop", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
     var i : i32 = 0;
     while (i < 4) {
-        %%stdout.printf("loop\n");
+        %%io.stdout.printf("loop\n");
         i += 1;
     }
     g();
@@ -601,11 +614,11 @@ fn f() -> i32 {
     )SOURCE", "loop\nloop\nloop\nloop\n");
 
     add_simple_case("continue and break", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
     var i : i32 = 0;
     while (true) {
-        %%stdout.printf("loop\n");
+        %%io.stdout.printf("loop\n");
         i += 1;
         if (i < 4) {
             continue;
@@ -616,11 +629,11 @@ pub fn main(args: [][]u8) -> %void {
     )SOURCE", "loop\nloop\nloop\nloop\n");
 
     add_simple_case("implicit cast after unreachable", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
     const x = outer();
     if (x == 1234) {
-        %%stdout.printf("OK\n");
+        %%io.stdout.printf("OK\n");
     }
 }
 fn inner() -> i32 { 1234 }
@@ -630,18 +643,18 @@ fn outer() -> isize {
     )SOURCE", "OK\n");
 
     add_simple_case("@sizeof() and @typeof()", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 const x: u16 = 13;
 const z: @typeof(x) = 19;
 pub fn main(args: [][]u8) -> %void {
     const y: @typeof(x) = 120;
-    %%stdout.print_u64(@sizeof(@typeof(y)));
-    %%stdout.printf("\n");
+    %%io.stdout.print_u64(@sizeof(@typeof(y)));
+    %%io.stdout.printf("\n");
 }
     )SOURCE", "2\n");
 
     add_simple_case("member functions", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 struct Rand {
     seed: u32,
     pub fn get_seed(r: Rand) -> u32 {
@@ -651,14 +664,14 @@ struct Rand {
 pub fn main(args: [][]u8) -> %void {
     const r = Rand {.seed = 1234};
     if (r.get_seed() != 1234) {
-        %%stdout.printf("BAD seed\n");
+        %%io.stdout.printf("BAD seed\n");
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("pointer dereferencing", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
     var x = i32(3);
@@ -667,93 +680,93 @@ pub fn main(args: [][]u8) -> %void {
     *y += 1;
 
     if (x != 4) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
     if (*y != 4) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("constant expressions", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 const ARRAY_SIZE : i8 = 20;
 
 pub fn main(args: [][]u8) -> %void {
     var array : [ARRAY_SIZE]u8 = undefined;
-    %%stdout.print_u64(@sizeof(@typeof(array)));
-    %%stdout.printf("\n");
+    %%io.stdout.print_u64(@sizeof(@typeof(array)));
+    %%io.stdout.printf("\n");
 }
     )SOURCE", "20\n");
 
     add_simple_case("@min_value() and @max_value()", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
-    %%stdout.printf("max u8: ");
-    %%stdout.print_u64(@max_value(u8));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("max u8: ");
+    %%io.stdout.print_u64(@max_value(u8));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("max u16: ");
-    %%stdout.print_u64(@max_value(u16));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("max u16: ");
+    %%io.stdout.print_u64(@max_value(u16));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("max u32: ");
-    %%stdout.print_u64(@max_value(u32));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("max u32: ");
+    %%io.stdout.print_u64(@max_value(u32));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("max u64: ");
-    %%stdout.print_u64(@max_value(u64));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("max u64: ");
+    %%io.stdout.print_u64(@max_value(u64));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("max i8: ");
-    %%stdout.print_i64(@max_value(i8));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("max i8: ");
+    %%io.stdout.print_i64(@max_value(i8));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("max i16: ");
-    %%stdout.print_i64(@max_value(i16));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("max i16: ");
+    %%io.stdout.print_i64(@max_value(i16));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("max i32: ");
-    %%stdout.print_i64(@max_value(i32));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("max i32: ");
+    %%io.stdout.print_i64(@max_value(i32));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("max i64: ");
-    %%stdout.print_i64(@max_value(i64));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("max i64: ");
+    %%io.stdout.print_i64(@max_value(i64));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("min u8: ");
-    %%stdout.print_u64(@min_value(u8));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("min u8: ");
+    %%io.stdout.print_u64(@min_value(u8));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("min u16: ");
-    %%stdout.print_u64(@min_value(u16));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("min u16: ");
+    %%io.stdout.print_u64(@min_value(u16));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("min u32: ");
-    %%stdout.print_u64(@min_value(u32));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("min u32: ");
+    %%io.stdout.print_u64(@min_value(u32));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("min u64: ");
-    %%stdout.print_u64(@min_value(u64));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("min u64: ");
+    %%io.stdout.print_u64(@min_value(u64));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("min i8: ");
-    %%stdout.print_i64(@min_value(i8));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("min i8: ");
+    %%io.stdout.print_i64(@min_value(i8));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("min i16: ");
-    %%stdout.print_i64(@min_value(i16));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("min i16: ");
+    %%io.stdout.print_i64(@min_value(i16));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("min i32: ");
-    %%stdout.print_i64(@min_value(i32));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("min i32: ");
+    %%io.stdout.print_i64(@min_value(i32));
+    %%io.stdout.printf("\n");
 
-    %%stdout.printf("min i64: ");
-    %%stdout.print_i64(@min_value(i64));
-    %%stdout.printf("\n");
+    %%io.stdout.printf("min i64: ");
+    %%io.stdout.print_i64(@min_value(i64));
+    %%io.stdout.printf("\n");
 }
     )SOURCE",
         "max u8: 255\n"
@@ -775,10 +788,10 @@ pub fn main(args: [][]u8) -> %void {
 
 
     add_simple_case("else if expression", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
     if (f(1) == 1) {
-        %%stdout.printf("OK\n");
+        %%io.stdout.printf("OK\n");
     }
 }
 fn f(c: u8) -> u8 {
@@ -793,82 +806,82 @@ fn f(c: u8) -> u8 {
     )SOURCE", "OK\n");
 
     add_simple_case("overflow intrinsics", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
     var result: u8 = undefined;
     if (!@add_with_overflow(u8, 250, 100, &result)) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
     if (@add_with_overflow(u8, 100, 150, &result)) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
     if (result != 250) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("order-independent declarations", R"SOURCE(
-import "std.zig";
-const z = stdin_fileno;
+const io = @import("std").io;
+const z = io.stdin_fileno;
 const x : @typeof(y) = 1234;
 const y : u16 = 5678;
 pub fn main(args: [][]u8) -> %void {
-    var x : i32 = print_ok(x);
+    var x_local : i32 = print_ok(x);
 }
 fn print_ok(val: @typeof(x)) -> @typeof(foo) {
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
     return 0;
 }
 const foo : i32 = 0;
     )SOURCE", "OK\n");
 
     add_simple_case("nested arrays", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
     const array_of_strings = [][]u8 {"hello", "this", "is", "my", "thing"};
     for (array_of_strings) |str| {
-        %%stdout.printf(str);
-        %%stdout.printf("\n");
+        %%io.stdout.printf(str);
+        %%io.stdout.printf("\n");
     }
 }
     )SOURCE", "hello\nthis\nis\nmy\nthing\n");
 
     add_simple_case("for loops", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
     const array = []u8 {9, 8, 7, 6};
     for (array) |item| {
-        %%stdout.print_u64(item);
-        %%stdout.printf("\n");
+        %%io.stdout.print_u64(item);
+        %%io.stdout.printf("\n");
     }
     for (array) |item, index| {
-        %%stdout.print_i64(index);
-        %%stdout.printf("\n");
+        %%io.stdout.print_i64(index);
+        %%io.stdout.printf("\n");
     }
     const unknown_size: []u8 = array;
     for (unknown_size) |item| {
-        %%stdout.print_u64(item);
-        %%stdout.printf("\n");
+        %%io.stdout.print_u64(item);
+        %%io.stdout.printf("\n");
     }
     for (unknown_size) |item, index| {
-        %%stdout.print_i64(index);
-        %%stdout.printf("\n");
+        %%io.stdout.print_i64(index);
+        %%io.stdout.printf("\n");
     }
 }
     )SOURCE", "9\n8\n7\n6\n0\n1\n2\n3\n9\n8\n7\n6\n0\n1\n2\n3\n");
 
     add_simple_case("function pointers", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 pub fn main(args: [][]u8) -> %void {
     const fns = []@typeof(fn1) { fn1, fn2, fn3, fn4, };
     for (fns) |f| {
-        %%stdout.print_u64(f());
-        %%stdout.printf("\n");
+        %%io.stdout.print_u64(f());
+        %%io.stdout.printf("\n");
     }
 }
 
@@ -879,7 +892,7 @@ fn fn4() -> u32 {8}
     )SOURCE", "5\n6\n7\n8\n");
 
     add_simple_case("statically initialized struct", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 struct Foo {
     x: i32,
     y: bool,
@@ -888,38 +901,38 @@ var foo = Foo { .x = 13, .y = true, };
 pub fn main(args: [][]u8) -> %void {
     foo.x += 1;
     if (foo.x != 14) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
 
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("statically initialized array literal", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 const x = []u8{1,2,3,4};
 pub fn main(args: [][]u8) -> %void {
     const y : [4]u8 = x;
     if (y[3] != 4) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
 
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("return with implicit cast from while loop", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
     while (true) {
-        %%stdout.printf("OK\n");
+        %%io.stdout.printf("OK\n");
         return;
     }
 }
     )SOURCE", "OK\n");
 
     add_simple_case("return struct byval from function", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 struct Foo {
     x: i32,
     y: i32,
@@ -933,14 +946,14 @@ fn make_foo(x: i32, y: i32) -> Foo {
 pub fn main(args: [][]u8) -> %void {
     const foo = make_foo(1234, 5678);
     if (foo.y != 5678) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("%% binary operator", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 error ItBroke;
 fn g(x: bool) -> %isize {
     if (x) {
@@ -953,24 +966,24 @@ pub fn main(args: [][]u8) -> %void {
     const a = g(true) %% 3;
     const b = g(false) %% 3;
     if (a != 3) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
     if (b != 10) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("string concatenation", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
-    %%stdout.printf("OK" ++ " IT " ++ "WORKED\n");
+    %%io.stdout.printf("OK" ++ " IT " ++ "WORKED\n");
 }
     )SOURCE", "OK IT WORKED\n");
 
     add_simple_case("constant struct with negation", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 struct Vertex {
     x: f32,
     y: f32,
@@ -985,30 +998,30 @@ const vertices = []Vertex {
 };
 pub fn main(args: [][]u8) -> %void {
     if (vertices[0].x != -0.6) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("int to ptr cast", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
     const x = isize(13);
     const y = (&u8)(x);
     const z = usize(y);
     if (z != 13) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("pointer to void return type", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 const x = void{};
 fn f() -> &void {
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
     return &x;
 }
 pub fn main(args: [][]u8) -> %void {
@@ -1018,7 +1031,7 @@ pub fn main(args: [][]u8) -> %void {
     )SOURCE", "OK\n");
 
     add_simple_case("unwrap simple value from error", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 fn do() -> %isize {
     13
 }
@@ -1026,14 +1039,14 @@ fn do() -> %isize {
 pub fn main(args: [][]u8) -> %void {
     const i = %%do();
     if (i != 13) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("store member function in variable", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 struct Foo {
     x: i32,
     fn member(foo: Foo) -> i32 { foo.x }
@@ -1043,14 +1056,14 @@ pub fn main(args: [][]u8) -> %void {
     const member_fn = Foo.member;
     const result = member_fn(instance);
     if (result != 1234) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("call member function directly", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 struct Foo {
     x: i32,
     fn member(foo: Foo) -> i32 { foo.x }
@@ -1059,18 +1072,18 @@ pub fn main(args: [][]u8) -> %void {
     const instance = Foo { .x = 1234, };
     const result = Foo.member(instance);
     if (result != 1234) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
     add_simple_case("call result of if else expression", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 fn a() -> []u8 { "a\n" }
 fn b() -> []u8 { "b\n" }
 fn f(x: bool) {
-    %%stdout.printf((if (x) a else b)());
+    %%io.stdout.printf((if (x) a else b)());
 }
 pub fn main(args: [][]u8) -> %void {
     f(true);
@@ -1079,15 +1092,10 @@ pub fn main(args: [][]u8) -> %void {
     )SOURCE", "a\nb\n");
 
 
-    add_simple_case("expose function pointer to C land", R"SOURCE(
-#link("c")
-export executable "test";
-
-c_import {
-    @c_include("stdlib.h");
-}
+    add_simple_case_libc("expose function pointer to C land", R"SOURCE(
+const c = @c_import(@c_include("stdlib.h"));
 
-export fn compare_fn(a: ?&const c_void, b: ?&const c_void) -> c_int {
+export fn compare_fn(a: ?&const c.c_void, b: ?&const c.c_void) -> c_int {
     const a_int = (&i32)(a ?? unreachable{});
     const b_int = (&i32)(b ?? unreachable{});
     if (*a_int < *b_int) {
@@ -1102,11 +1110,11 @@ export fn compare_fn(a: ?&const c_void, b: ?&const c_void) -> c_int {
 export fn main(args: c_int, argv: &&u8) -> c_int {
     var array = []i32 { 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 };
 
-    qsort((&c_void)(&array[0]), c_ulong(array.len), @sizeof(i32), compare_fn);
+    c.qsort((&c.c_void)(&array[0]), c_ulong(array.len), @sizeof(i32), compare_fn);
 
     for (array) |item, i| {
         if (item != i) {
-            abort();
+            c.abort();
         }
     }
 
@@ -1116,37 +1124,33 @@ export fn main(args: c_int, argv: &&u8) -> c_int {
 
 
 
-    add_simple_case("casting between float and integer types", R"SOURCE(
-#link("c")
-export executable "test";
-c_import {
-    @c_include("stdio.h");
-}
+    add_simple_case_libc("casting between float and integer types", R"SOURCE(
+const c = @c_import(@c_include("stdio.h"));
 export fn main(argc: c_int, argv: &&u8) -> c_int {
     const small: f32 = 3.25;
     const x: f64 = small;
     const y = i32(x);
     const z = f64(y);
-    printf(c"%.2f\n%d\n%.2f\n%.2f\n", x, y, z, f64(-0.4));
+    c.printf(c"%.2f\n%d\n%.2f\n%.2f\n", x, y, z, f64(-0.4));
     return 0;
 }
     )SOURCE", "3.25\n3\n3.00\n-0.40\n");
 
 
     add_simple_case("const expression eval handling of variables", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
     var x = true;
     while (x) {
         x = false;
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
 
     add_simple_case("incomplete struct parameter top level decl", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 struct A {
     b: B,
 }
@@ -1159,7 +1163,7 @@ struct C {
     x: i32,
 
     fn d(c: C) {
-        %%stdout.printf("OK\n");
+        %%io.stdout.printf("OK\n");
     }
 }
 
@@ -1182,7 +1186,7 @@ pub fn main(args: [][]u8) -> %void {
 
 
     add_simple_case("same named methods in incomplete struct", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 
 struct Foo {
     field1: Bar,
@@ -1200,53 +1204,53 @@ pub fn main(args: [][]u8) -> %void {
     const bar = Bar {.field2 = 13,};
     const foo = Foo {.field1 = bar,};
     if (!foo.method()) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
     if (!bar.method()) {
-        %%stdout.printf("BAD\n");
+        %%io.stdout.printf("BAD\n");
     }
-    %%stdout.printf("OK\n");
+    %%io.stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
 
     add_simple_case("defer with only fallthrough", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
-    %%stdout.printf("before\n");
-    defer %%stdout.printf("defer1\n");
-    defer %%stdout.printf("defer2\n");
-    defer %%stdout.printf("defer3\n");
-    %%stdout.printf("after\n");
+    %%io.stdout.printf("before\n");
+    defer %%io.stdout.printf("defer1\n");
+    defer %%io.stdout.printf("defer2\n");
+    defer %%io.stdout.printf("defer3\n");
+    %%io.stdout.printf("after\n");
 }
     )SOURCE", "before\nafter\ndefer3\ndefer2\ndefer1\n");
 
 
     add_simple_case("defer with return", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
-    %%stdout.printf("before\n");
-    defer %%stdout.printf("defer1\n");
-    defer %%stdout.printf("defer2\n");
+    %%io.stdout.printf("before\n");
+    defer %%io.stdout.printf("defer1\n");
+    defer %%io.stdout.printf("defer2\n");
     if (args.len == 1) return;
-    defer %%stdout.printf("defer3\n");
-    %%stdout.printf("after\n");
+    defer %%io.stdout.printf("defer3\n");
+    %%io.stdout.printf("after\n");
 }
     )SOURCE", "before\ndefer2\ndefer1\n");
 
 
     add_simple_case("%defer and it fails", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
     do_test() %% return;
 }
 fn do_test() -> %void {
-    %%stdout.printf("before\n");
-    defer %%stdout.printf("defer1\n");
-    %defer %%stdout.printf("deferErr\n");
+    %%io.stdout.printf("before\n");
+    defer %%io.stdout.printf("defer1\n");
+    %defer %%io.stdout.printf("deferErr\n");
     %return its_gonna_fail();
-    defer %%stdout.printf("defer3\n");
-    %%stdout.printf("after\n");
+    defer %%io.stdout.printf("defer3\n");
+    %%io.stdout.printf("after\n");
 }
 error IToldYouItWouldFail;
 fn its_gonna_fail() -> %void {
@@ -1256,17 +1260,17 @@ fn its_gonna_fail() -> %void {
 
 
     add_simple_case("%defer and it passes", R"SOURCE(
-import "std.zig";
+const io = @import("std").io;
 pub fn main(args: [][]u8) -> %void {
     do_test() %% return;
 }
 fn do_test() -> %void {
-    %%stdout.printf("before\n");
-    defer %%stdout.printf("defer1\n");
-    %defer %%stdout.printf("deferErr\n");
+    %%io.stdout.printf("before\n");
+    defer %%io.stdout.printf("defer1\n");
+    %defer %%io.stdout.printf("deferErr\n");
     %return its_gonna_pass();
-    defer %%stdout.printf("defer3\n");
-    %%stdout.printf("after\n");
+    defer %%io.stdout.printf("defer3\n");
+    %%io.stdout.printf("after\n");
 }
 fn its_gonna_pass() -> %void { }
     )SOURCE", "before\nafter\ndefer3\ndefer1\n");
@@ -1327,14 +1331,9 @@ fn a() {
 fn b() {}
     )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code");
 
-    add_compile_fail_case("bad version string", R"SOURCE(
-#version("aoeu")
-export executable "test";
-    )SOURCE", 1, ".tmp_source.zig:2:1: error: invalid version string");
-
     add_compile_fail_case("bad import", R"SOURCE(
-import "bogus-does-not-exist.zig";
-    )SOURCE", 1, ".tmp_source.zig:2:1: error: unable to find 'bogus-does-not-exist.zig'");
+const bogus = @import("bogus-does-not-exist.zig");
+    )SOURCE", 1, ".tmp_source.zig:2:15: error: unable to find 'bogus-does-not-exist.zig'");
 
     add_compile_fail_case("undeclared identifier", R"SOURCE(
 fn a() {
@@ -1450,13 +1449,13 @@ fn f() {
 
     add_compile_fail_case("direct struct loop", R"SOURCE(
 struct A { a : A, }
-    )SOURCE", 1, ".tmp_source.zig:2:1: error: struct has infinite size");
+    )SOURCE", 1, ".tmp_source.zig:2:1: error: 'A' depends on itself");
 
     add_compile_fail_case("indirect struct loop", R"SOURCE(
 struct A { b : B, }
 struct B { c : C, }
 struct C { a : A, }
-    )SOURCE", 1, ".tmp_source.zig:4:1: error: struct has infinite size");
+    )SOURCE", 1, ".tmp_source.zig:2:1: error: 'A' depends on itself");
 
     add_compile_fail_case("invalid struct field", R"SOURCE(
 struct A { x : i32, }
@@ -1568,7 +1567,7 @@ fn f() -> @bogus(foo) {
     add_compile_fail_case("top level decl dependency loop", R"SOURCE(
 const a : @typeof(b) = 0;
 const b : @typeof(a) = 0;
-    )SOURCE", 1, ".tmp_source.zig:3:19: error: use of undeclared identifier 'a'");
+    )SOURCE", 1, ".tmp_source.zig:2:1: error: 'a' depends on itself");
 
     add_compile_fail_case("noalias on non pointer param", R"SOURCE(
 fn f(noalias x: i32) {}
@@ -1589,8 +1588,11 @@ struct Bar {}
 fn f(Foo: i32) {
     var Bar : i32 = undefined;
 }
-    )SOURCE", 2, ".tmp_source.zig:5:6: error: variable shadows type 'Foo'",
-                 ".tmp_source.zig:6:5: error: variable shadows type 'Bar'");
+    )SOURCE", 4,
+            ".tmp_source.zig:5:6: error: redefinition of 'Foo'",
+            ".tmp_source.zig:2:1: note: previous definition is here",
+            ".tmp_source.zig:6:5: error: redefinition of 'Bar'",
+            ".tmp_source.zig:3:1: note: previous definition is here");
 
     add_compile_fail_case("multiple else prongs in a switch", R"SOURCE(
 fn f(x: u32) {
@@ -1614,14 +1616,9 @@ fn f(s: []u8) -> []u8 {
     )SOURCE", 1, ".tmp_source.zig:3:5: error: string concatenation requires constant expression");
 
     add_compile_fail_case("c_import with bogus include", R"SOURCE(
-c_import {
-    @c_include("bogus.h");
-}
-    )SOURCE", 2, ".tmp_source.zig:2:1: error: C import failed",
-                 ".h:1:10: error: 'bogus.h' file not found");
-
-    add_compile_fail_case("empty file", "",
-            1, ".tmp_source.zig:1:1: error: missing export declaration and output name not provided");
+const c = @c_import(@c_include("bogus.h"));
+    )SOURCE", 2, ".tmp_source.zig:2:11: error: C import failed",
+                 ".h:1:10: note: 'bogus.h' file not found");
 
     add_compile_fail_case("address of number literal", R"SOURCE(
 const x = 3;
test/self_hosted.zig
@@ -1,4 +1,5 @@
-import "test_std.zig";
+// test std library
+const std = @import("std");
 
 #attribute("test")
 fn empty_function() {}
test/test_std.zig
@@ -1,1 +0,0 @@
-import "std.zig";
CMakeLists.txt
@@ -137,12 +137,13 @@ set(ZIG_STD_SRC
     "${CMAKE_SOURCE_DIR}/std/test_runner.zig"
     "${CMAKE_SOURCE_DIR}/std/test_runner_libc.zig"
     "${CMAKE_SOURCE_DIR}/std/test_runner_nolibc.zig"
-    "${CMAKE_SOURCE_DIR}/std/std.zig"
+    "${CMAKE_SOURCE_DIR}/std/io.zig"
     "${CMAKE_SOURCE_DIR}/std/os.zig"
     "${CMAKE_SOURCE_DIR}/std/syscall.zig"
     "${CMAKE_SOURCE_DIR}/std/errno.zig"
     "${CMAKE_SOURCE_DIR}/std/rand.zig"
     "${CMAKE_SOURCE_DIR}/std/math.zig"
+    "${CMAKE_SOURCE_DIR}/std/index.zig"
 )