Commit b28b7f63d1

Andrew Kelley <superjoe30@gmail.com>
2016-01-14 02:15:51
all types are now expressions
See #22
1 parent cb46d0b
doc/vim/syntax/zig.vim
@@ -15,8 +15,8 @@ syn keyword zigConditional if else switch
 syn keyword zigRepeat while for
 
 syn keyword zigConstant null
-syn keyword zigKeyword fn unreachable use void
-syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string
+syn keyword zigKeyword fn use
+syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string void unreachable
 
 syn keyword zigBoolean true false
 
doc/langref.md
@@ -34,13 +34,13 @@ Root : many(TopLevelDecl) token(EOF)
 
 TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Use | ContainerDecl | VariableDeclaration
 
-VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression))
+VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) UnwrapMaybeExpression option(token(Eq) Expression))
 
 ContainerDecl : many(Directive) option(FnVisibleMod) (token(Struct) | token(Enum)) token(Symbol) token(LBrace) many(StructMember) token(RBrace)
 
 StructMember: StructField | FnDecl
 
-StructField : token(Symbol) option(token(Colon) Type token(Comma))
+StructField : token(Symbol) option(token(Colon) Expression) token(Comma))
 
 Use : many(Directive) token(Use) token(String) token(Semicolon)
 
@@ -48,7 +48,7 @@ RootExportDecl : many(Directive) token(Export) token(Symbol) token(String) token
 
 ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnDecl) token(RBrace)
 
-FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type)
+FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(Expression)
 
 Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen)
 
@@ -56,23 +56,11 @@ FnVisibleMod : token(Pub) | token(Export)
 
 FnDecl : FnProto token(Semicolon)
 
-FnDef : FnProto Block
+FnDef : FnProto token(FatArrow) Block
 
 ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen)
 
-ParamDecl : token(Symbol) token(Colon) Type | token(Ellipsis)
-
-Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType | MaybeType | CompilerFnExpr
-
-CompilerFnExpr : token(NumberSign) token(Symbol) token(LParen) Expression token(RParen)
-
-CompilerFnType : token(NumberSign) token(Symbol) token(LParen) Type token(RParen)
-
-PointerType : token(Ampersand) option(token(Const)) option(token(NoAlias)) Type
-
-MaybeType : token(Question) Type
-
-ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) option(token(NoAlias)) Type
+ParamDecl : option(token(NoAlias)) token(Symbol) token(Colon) UnwrapMaybeExpression | token(Ellipsis)
 
 Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
 
@@ -80,11 +68,9 @@ Statement : Label | VariableDeclaration token(Semicolon) | NonBlockExpression to
 
 Label: token(Symbol) token(Colon)
 
-VariableDeclaration : (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression))
-
 Expression : BlockExpression | NonBlockExpression
 
-NonBlockExpression : ReturnExpression | AssignmentExpression | AsmExpression
+NonBlockExpression : ReturnExpression | AssignmentExpression
 
 AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen)
 
@@ -92,13 +78,13 @@ AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput)
 
 AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers)
 
-AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Arrow) Type) token(RParen)
+AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Arrow) Expression) token(RParen)
 
 AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen)
 
 AsmClobbers: token(Colon) list(token(String), token(Comma))
 
-UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression
+UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestionMark) BoolOrExpression | BoolOrExpression
 
 AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpression | UnwrapMaybeExpression
 
@@ -116,7 +102,7 @@ IfExpression : IfVarExpression | IfBoolExpression
 
 IfBoolExpression : token(If) token(LParen) Expression token(RParen) Expression option(Else)
 
-IfVarExpression : token(If) token(LParen) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(MaybeAssign) Expression token(RParen) Expression Option(Else)
+IfVarExpression : token(If) token(LParen) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Expression) Token(MaybeAssign) Expression token(RParen) Expression Option(Else)
 
 Else : token(Else) Expression
 
@@ -144,11 +130,11 @@ MultiplyExpression : CastExpression MultiplyOperator MultiplyExpression | CastEx
 
 MultiplyOperator : token(Star) | token(Slash) | token(Percent)
 
-CastExpression : CastExpression token(as) Type | PrefixOpExpression
+CastExpression : CastExpression token(as) PrimaryExpression | PrefixOpExpression
 
 PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression
 
-SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
+SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ContainerInitExpression)
 
 FieldAccessExpression : token(Dot) token(Symbol)
 
@@ -158,26 +144,30 @@ ArrayAccessExpression : token(LBracket) Expression token(RBracket)
 
 SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) token(RBracket) option(token(Const))
 
-PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const)))
+ContainerInitExpression : token(LBrace) ContainerInitBody token(RBrace)
+
+ContainerInitBody : list(StructLiteralField, token(Comma)) | list(Expression, token(Comma))
+
+StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression
 
-PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | (token(AtSign) token(Symbol) FnCallExpression)
+PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const))) | token(QuestionMark)
 
-StructValueExpression : token(Type) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace)
+PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | token(Symbol) | (token(AtSign) token(Symbol) FnCallExpression) | ArrayType | AsmExpression
 
-StructValueExpressionField : token(Dot) token(Symbol) token(Eq) Expression
+ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) Expression
 
-Goto: token(Goto) token(Symbol)
+GotoExpression: token(Goto) token(Symbol)
 
 GroupedExpression : token(LParen) Expression token(RParen)
 
-KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null)
+KeywordLiteral : token(True) | token(False) | token(Null) | token(Break) | token(Continue)
 ```
 
 ## Operator Precedence
 
 ```
-x() x[] x.y
-!x -x ~x *x &x &const x
+x() x[] x{} x.y
+!x -x ~x *x &x
 as
 * / %
 + -
example/arrays/arrays.zig
@@ -1,36 +0,0 @@
-export executable "arrays";
-
-use "std.zig";
-
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
-    var array : [5]u32;
-
-    var i : u32 = 0;
-    while (i < 5) {
-        array[i] = i + 1;
-        i = array[i];
-    }
-
-    i = 0;
-    var accumulator : u32 = 0;
-    while (i < 5) {
-        accumulator += array[i];
-
-        i += 1;
-    }
-
-    if (accumulator != 15) {
-        print_str("BAD\n");
-    }
-
-    if (get_array_len(array) != 5) {
-        print_str("BAD\n");
-    }
-
-    print_str("OK\n");
-    return 0;
-}
-
-fn get_array_len(a: []u32) -> usize {
-    a.len
-}
example/cat/main.zig
@@ -0,0 +1,7 @@
+export executable "cat";
+
+pub main(argv: [][]u8) -> i32 {
+
+
+    return 0;
+}
example/expressions/expressions.zig
@@ -1,52 +0,0 @@
-export executable "expressions";
-
-use "std.zig";
-
-fn other_exit() -> unreachable {
-    if (true) { exit(0); }
-    // the unreachable statement is the programmer assuring the compiler that this code is impossible to execute.
-    unreachable;
-}
-
-export fn main(argc: isize, argv: &&u8, env: &&u8) -> unreachable {
-    const a : i32 = 1;
-    const b = 2 as i32;
-    // const c : i32; // not yet support for const variables
-    // const d; // parse error
-    if (a + b == 3) {
-        const no_conflict : i32 = 5;
-        if (no_conflict == 5) { print_str("OK 1\n" as string); }
-    }
-
-    const c = {
-        const no_conflict : i32 = 10;
-        no_conflict
-    };
-    if (c == 10) { print_str("OK 2\n" as string); }
-
-    void_fun(1, void, 2);
-
-    test_mutable_vars();
-
-    other_exit();
-}
-
-fn void_fun(a : i32, b : void, c : i32) -> void {
-    const x = a + 1;    // i32
-    const y = c + 1;    // i32
-    const z = b;        // void
-    const w : void = z; // void
-    if (x + y == 4) { return w; }
-}
-
-fn test_mutable_vars() {
-    var i : i32 = 0;
-loop_start:
-    if i == 3 {
-        goto done;
-    }
-    print_str("loop\n" as string);
-    i = i + 1;
-    goto loop_start;
-done:
-}
example/guess_number/main.zig
@@ -3,12 +3,12 @@ export executable "guess_number";
 use "std.zig";
 use "rand.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     print_str("Welcome to the Guess Number Game in Zig.\n");
 
     var seed : u32;
-    const err = os_get_random_bytes(&seed as &u8, #sizeof(u32));
-    if (err != #sizeof(u32)) {
+    const err = os_get_random_bytes(&seed as (&u8), @sizeof(u32));
+    if (err != @sizeof(u32)) {
         // TODO full error message
         fprint_str(stderr_fileno, "unable to get random bytes\n");
         return 1;
example/hello_world/hello.zig
@@ -2,7 +2,7 @@ export executable "hello";
 
 use "std.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     print_str("Hello, world!\n");
     return 0;
 }
example/hello_world/hello_libc.zig
@@ -2,11 +2,10 @@ export executable "hello";
 
 #link("c")
 extern {
-    fn printf(__format: &const u8, ...) -> i32;
-    fn exit(__status: i32) -> unreachable;
+    fn printf(__format: &const u8, ...) i32;
 }
 
-export fn main(argc: i32, argv: &&u8, env: &&u8) -> i32 {
+export fn main(argc: i32, argv: &&u8, env: &&u8) i32 => {
     printf(c"Hello, world!\n");
     return 0;
 }
example/list/list.zig
@@ -1,16 +1,16 @@
 pub struct List#(T: type) {
     items: ?&T,
-    length: usize,
-    capacity: usize,
+    length: isize,
+    capacity: isize,
 
-    pub fn (l: &List) deinit() {
+    pub fn deinit(l: &List) {
         free(l.items);
-        l.items = None;
+        l.items = null;
     }
 
     pub fn append(l: &List, item: T) -> error {
         const err = l.ensure_capacity(l.length + 1);
-        if err != Error.None {
+        if err != error.None {
             return err;
         }
         const raw_items = l.items ?? unreachable;
@@ -47,11 +47,11 @@ pub struct List#(T: type) {
             better_capacity *= 2;
         }
         if better_capacity != l.capacity {
-            const new_items = realloc(l.items, better_capacity) ?? { return Error.NoMem };
+            const new_items = realloc(l.items, better_capacity) ?? { return error.NoMem };
             l.items = new_items;
             l.capacity = better_capacity;
         }
-        Error.None
+        error.None
     }
 }
 
@@ -64,3 +64,41 @@ pub fn realloc#(T: type)(ptr: ?&T, new_count: usize) -> ?&T {
 pub fn free#(T: type)(ptr: ?&T) {
 
 }
+
+
+////////////////// alternate
+
+// previously proposed, but with : instead of ->
+// `:` means "parser should expect a type now"
+fn max#(T :type)(a :T, b :T) :T {
+    if (a > b) a else b
+}
+
+// andy's new idea
+// parameters can talk about @typeof() for previous parameters.
+// using :T here is equivalent to @child_type(@typeof(T))
+fn max(T :type, a :T, b :T) :T {
+    if (a > b) a else b
+}
+
+fn f() {
+    const x :i32 = 1234;
+    const y :i32 = 5678;
+    const z = max(@typeof(x), x, y);
+}
+
+// So, type-generic functions don't need any fancy syntax. type-generic
+// containers still do, though:
+
+pub struct List(T :type) {
+    items :?&T,
+    length :isize,
+    capacity :isize,
+}
+
+// Types are always marked with ':' so we don't need '#' to indicate type generic parameters.
+
+fn f() {
+    var list :List(:u8);
+}
+
example/maybe_type/main.zig
@@ -2,7 +2,7 @@ export executable "maybe_type";
 
 use "std.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     const x : ?bool = true;
 
     if (const y ?= x) {
@@ -25,7 +25,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
 
     const final_x : ?i32 = 13;
 
-    const num = final_x ?? unreachable;
+    const num = final_x ?? unreachable{};
 
     if (num != 13) {
         print_str("BAD\n");
example/multiple_files/foo.zig
@@ -2,10 +2,10 @@ use "std.zig";
 
 // purposefully conflicting function with main.zig
 // but it's private so it should be OK
-fn private_function() {
+fn private_function() => {
     print_str("OK 1\n");
 }
 
-pub fn print_text() {
+pub fn print_text() => {
     private_function();
 }
example/multiple_files/main.zig
@@ -3,12 +3,12 @@ export executable "test-multiple-files";
 use "std.zig";
 use "foo.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     private_function();
     print_str("OK 2\n");
     return 0;
 }
 
-fn private_function() {
+fn private_function() => {
     print_text();
 }
example/structs/structs.zig
@@ -1,87 +0,0 @@
-export executable "structs";
-
-use "std.zig";
-
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
-    var foo : Foo;
-
-    foo.a = foo.a + 1;
-
-    foo.b = foo.a == 1;
-
-    test_foo(foo);
-
-    modify_foo(&foo);
-
-    if foo.c != 100 {
-        print_str("BAD\n");
-    }
-
-    test_point_to_self();
-
-    test_byval_assign();
-
-    test_initializer();
-
-    print_str("OK\n");
-    return 0;
-}
-
-struct Foo {
-    a : i32,
-    b : bool,
-    c : f32,
-}
-
-struct Node {
-    val: Val,
-    next: &Node,
-}
-
-struct Val {
-    x: i32,
-}
-
-fn test_foo(foo : Foo) {
-    if !foo.b {
-        print_str("BAD\n");
-    }
-}
-
-fn modify_foo(foo : &Foo) {
-    foo.c = 100;
-}
-
-fn test_point_to_self() {
-    var root : Node;
-    root.val.x = 1;
-
-    var node : Node;
-    node.next = &root;
-    node.val.x = 2;
-
-    root.next = &node;
-
-    if node.next.next.next.val.x != 1 {
-        print_str("BAD\n");
-    }
-}
-
-fn test_byval_assign() {
-    var foo1 : Foo;
-    var foo2 : Foo;
-
-    foo1.a = 1234;
-
-    if foo2.a != 0 { print_str("BAD\n"); }
-
-    foo2 = foo1;
-
-    if foo2.a != 1234 { print_str("BAD - byval assignment failed\n"); }
-
-}
-
-fn test_initializer() {
-    const val = Val { .x = 42 };
-    if val.x != 42 { print_str("BAD\n"); }
-}
src/all_types.hpp
@@ -100,7 +100,6 @@ enum NodeType {
     NodeTypeFnDef,
     NodeTypeFnDecl,
     NodeTypeParamDecl,
-    NodeTypeType,
     NodeTypeBlock,
     NodeTypeExternBlock,
     NodeTypeDirective,
@@ -111,7 +110,6 @@ enum NodeType {
     NodeTypeNumberLiteral,
     NodeTypeStringLiteral,
     NodeTypeCharLiteral,
-    NodeTypeUnreachable,
     NodeTypeSymbol,
     NodeTypePrefixOpExpr,
     NodeTypeFnCallExpr,
@@ -119,7 +117,6 @@ enum NodeType {
     NodeTypeSliceExpr,
     NodeTypeFieldAccessExpr,
     NodeTypeUse,
-    NodeTypeVoid,
     NodeTypeBoolLiteral,
     NodeTypeNullLiteral,
     NodeTypeIfBoolExpr,
@@ -132,10 +129,9 @@ enum NodeType {
     NodeTypeAsmExpr,
     NodeTypeStructDecl,
     NodeTypeStructField,
-    NodeTypeStructValueExpr,
+    NodeTypeContainerInitExpr,
     NodeTypeStructValueField,
-    NodeTypeCompilerFnExpr,
-    NodeTypeCompilerFnType,
+    NodeTypeArrayType,
 };
 
 struct AstNodeRoot {
@@ -185,30 +181,10 @@ struct AstNodeFnDecl {
 struct AstNodeParamDecl {
     Buf name;
     AstNode *type;
-
-    // populated by semantic analyzer
-    VariableTableEntry *variable;
-};
-
-enum AstNodeTypeType {
-    AstNodeTypeTypePrimitive,
-    AstNodeTypeTypePointer,
-    AstNodeTypeTypeArray,
-    AstNodeTypeTypeMaybe,
-    AstNodeTypeTypeCompilerExpr,
-};
-
-struct AstNodeType {
-    AstNodeTypeType type;
-    Buf primitive_name;
-    AstNode *child_type;
-    AstNode *array_size; // can be null
-    bool is_const;
     bool is_noalias;
-    AstNode *compiler_expr;
 
     // populated by semantic analyzer
-    TypeTableEntry *entry;
+    VariableTableEntry *variable;
 };
 
 struct AstNodeBlock {
@@ -295,6 +271,7 @@ struct AstNodeFnCallExpr {
     // populated by semantic analyzer:
     BuiltinFnEntry *builtin_fn;
     Expr resolved_expr;
+    NumLitCodeGen resolved_num_lit;
 };
 
 struct AstNodeArrayAccessExpr {
@@ -360,6 +337,7 @@ enum PrefixOp {
     PrefixOpAddressOf,
     PrefixOpConstAddressOf,
     PrefixOpDereference,
+    PrefixOpMaybe,
 };
 
 struct AstNodePrefixOpExpr {
@@ -537,30 +515,19 @@ struct AstNodeStructValueField {
     TypeStructField *type_struct_field;
 };
 
-struct AstNodeStructValueExpr {
-    AstNode *type;
-    ZigList<AstNode *> fields;
-
-    // populated by semantic analyzer
-    StructValExprCodeGen codegen;
-    Expr resolved_expr;
-};
-
-struct AstNodeCompilerFnExpr {
-    Buf name;
-    AstNode *expr;
-
-    // populated by semantic analyzer
-    Expr resolved_expr;
+enum ContainerInitKind {
+    ContainerInitKindStruct,
+    ContainerInitKindArray,
 };
 
-struct AstNodeCompilerFnType {
-    Buf name;
+struct AstNodeContainerInitExpr {
     AstNode *type;
+    ZigList<AstNode *> entries;
+    ContainerInitKind kind;
 
     // populated by semantic analyzer
+    StructValExprCodeGen resolved_struct_val_expr;
     Expr resolved_expr;
-    NumLitCodeGen resolved_num_lit;
 };
 
 struct AstNodeNullLiteral {
@@ -569,16 +536,6 @@ struct AstNodeNullLiteral {
     Expr resolved_expr;
 };
 
-struct AstNodeVoidExpr {
-    // populated by semantic analyzer
-    Expr resolved_expr;
-};
-
-struct AstNodeUnreachableExpr {
-    // populated by semantic analyzer
-    Expr resolved_expr;
-};
-
 struct AstNodeSymbolExpr {
     Buf symbol;
 
@@ -603,6 +560,15 @@ struct AstNodeContinueExpr {
     Expr resolved_expr;
 };
 
+struct AstNodeArrayType {
+    AstNode *size;
+    AstNode *child_type;
+    bool is_const;
+
+    // populated by semantic analyzer
+    Expr resolved_expr;
+};
+
 struct AstNode {
     enum NodeType type;
     int line;
@@ -615,7 +581,6 @@ struct AstNode {
         AstNodeFnDef fn_def;
         AstNodeFnDecl fn_decl;
         AstNodeFnProto fn_proto;
-        AstNodeType type;
         AstNodeParamDecl param_decl;
         AstNodeBlock block;
         AstNodeReturnExpr return_expr;
@@ -641,17 +606,14 @@ struct AstNode {
         AstNodeStringLiteral string_literal;
         AstNodeCharLiteral char_literal;
         AstNodeNumberLiteral number_literal;
-        AstNodeStructValueExpr struct_val_expr;
+        AstNodeContainerInitExpr container_init_expr;
         AstNodeStructValueField struct_val_field;
-        AstNodeCompilerFnExpr compiler_fn_expr;
-        AstNodeCompilerFnType compiler_fn_type;
         AstNodeNullLiteral null_literal;
-        AstNodeVoidExpr void_expr;
-        AstNodeUnreachableExpr unreachable_expr;
         AstNodeSymbolExpr symbol_expr;
         AstNodeBoolLiteral bool_literal;
         AstNodeBreakExpr break_expr;
         AstNodeContinueExpr continue_expr;
+        AstNodeArrayType array_type;
     } data;
 };
 
@@ -670,7 +632,6 @@ struct AsmToken {
 struct TypeTableEntryPointer {
     TypeTableEntry *child_type;
     bool is_const;
-    bool is_noalias;
 };
 
 struct TypeTableEntryInt {
@@ -770,8 +731,8 @@ struct TypeTableEntry {
     } data;
 
     // use these fields to make sure we don't duplicate type table entries for the same type
-    TypeTableEntry *pointer_parent[2][2]; // 0 - const. 1 - noalias
-    TypeTableEntry *unknown_size_array_parent[2][2]; // 0 - const. 1 - noalias
+    TypeTableEntry *pointer_parent[2];
+    TypeTableEntry *unknown_size_array_parent[2];
     HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size;
     TypeTableEntry *maybe_parent;
     TypeTableEntry *meta_parent;
@@ -830,6 +791,11 @@ enum BuiltinFnId {
     BuiltinFnIdArithmeticWithOverflow,
     BuiltinFnIdMemcpy,
     BuiltinFnIdMemset,
+    BuiltinFnIdSizeof,
+    BuiltinFnIdMaxValue,
+    BuiltinFnIdMinValue,
+    BuiltinFnIdValueCount,
+    BuiltinFnIdTypeof,
 };
 
 struct BuiltinFnEntry {
src/analyze.cpp
@@ -15,10 +15,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
         TypeTableEntry *expected_type, AstNode *node);
 static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
         AstNode *node, AstNodeNumberLiteral *out_number_literal);
-static void collect_type_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *type_node, TopLevelDecl *decl_node);
 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);
+static TypeTableEntry *unwrapped_node_type(AstNode *node);
 
 static AstNode *first_executing_node(AstNode *node) {
     switch (node->type) {
@@ -40,7 +40,6 @@ static AstNode *first_executing_node(AstNode *node) {
         case NodeTypeFnDef:
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
-        case NodeTypeType:
         case NodeTypeBlock:
         case NodeTypeExternBlock:
         case NodeTypeDirective:
@@ -49,11 +48,9 @@ static AstNode *first_executing_node(AstNode *node) {
         case NodeTypeNumberLiteral:
         case NodeTypeStringLiteral:
         case NodeTypeCharLiteral:
-        case NodeTypeUnreachable:
         case NodeTypeSymbol:
         case NodeTypePrefixOpExpr:
         case NodeTypeUse:
-        case NodeTypeVoid:
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeIfBoolExpr:
@@ -65,11 +62,10 @@ static AstNode *first_executing_node(AstNode *node) {
         case NodeTypeAsmExpr:
         case NodeTypeStructDecl:
         case NodeTypeStructField:
-        case NodeTypeStructValueExpr:
         case NodeTypeStructValueField:
         case NodeTypeWhileExpr:
-        case NodeTypeCompilerFnExpr:
-        case NodeTypeCompilerFnType:
+        case NodeTypeContainerInitExpr:
+        case NodeTypeArrayType:
             return node;
     }
     zig_panic("unreachable");
@@ -188,8 +184,9 @@ static TypeTableEntry *get_meta_type(CodeGen *g, TypeTableEntry *child_type) {
     }
 }
 
-TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const, bool is_noalias) {
-    TypeTableEntry **parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)][(is_noalias ? 1 : 0)];
+TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) {
+    assert(child_type->id != TypeTableEntryIdInvalid);
+    TypeTableEntry **parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)];
     if (*parent_pointer) {
         return *parent_pointer;
     } else {
@@ -197,9 +194,8 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
         entry->type_ref = LLVMPointerType(child_type->type_ref, 0);
 
         const char *const_str = is_const ? "const " : "";
-        const char *noalias_str = is_noalias ? "noalias " : "";
         buf_resize(&entry->name, 0);
-        buf_appendf(&entry->name, "&%s%s%s", const_str, noalias_str, buf_ptr(&child_type->name));
+        buf_appendf(&entry->name, "&%s%s", const_str, buf_ptr(&child_type->name));
 
         entry->size_in_bits = g->pointer_size_bytes * 8;
         entry->align_in_bits = g->pointer_size_bytes * 8;
@@ -208,14 +204,13 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
                 entry->size_in_bits, entry->align_in_bits, buf_ptr(&entry->name));
         entry->data.pointer.child_type = child_type;
         entry->data.pointer.is_const = is_const;
-        entry->data.pointer.is_noalias = is_noalias;
 
         *parent_pointer = entry;
         return entry;
     }
 }
 
-static TypeTableEntry *get_maybe_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *child_type) {
+static TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
     if (child_type->maybe_parent) {
         TypeTableEntry *entry = child_type->maybe_parent;
         return entry;
@@ -293,9 +288,10 @@ static TypeTableEntry *get_array_type(CodeGen *g, ImportTableEntry *import,
 }
 
 static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry *import,
-        TypeTableEntry *child_type, bool is_const, bool is_noalias)
+        TypeTableEntry *child_type, bool is_const)
 {
-    TypeTableEntry **parent_pointer = &child_type->unknown_size_array_parent[(is_const ? 1 : 0)][(is_noalias ? 1 : 0)];
+    assert(child_type->id != TypeTableEntryIdInvalid);
+    TypeTableEntry **parent_pointer = &child_type->unknown_size_array_parent[(is_const ? 1 : 0)];
     if (*parent_pointer) {
         return *parent_pointer;
     } else {
@@ -305,7 +301,7 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry
         buf_appendf(&entry->name, "[]%s", buf_ptr(&child_type->name));
         entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&entry->name));
 
-        TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const, is_noalias);
+        TypeTableEntry *pointer_type = get_pointer_to_type(g, child_type, is_const);
 
         unsigned element_count = 2;
         LLVMTypeRef element_types[] = {
@@ -343,6 +339,25 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry
     }
 }
 
+// like analyze expression, but expects a type. creates an error if resulting type is
+// not a meta type. unwraps it if it is.
+static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        AstNode *node)
+{
+    TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, node);
+    if (type_entry->id == TypeTableEntryIdInvalid) {
+        return type_entry;
+    } else if (type_entry->id == TypeTableEntryIdMetaType) {
+        return type_entry->data.meta_type.child_type;
+    } else {
+        add_node_error(g, first_executing_node(node),
+            buf_sprintf("expected type, found expression"));
+        get_resolved_expr(node)->type_entry = g->builtin_types.entry_invalid;
+        return g->builtin_types.entry_invalid;
+    }
+}
+
+
 static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
         AstNode *node, AstNodeNumberLiteral *out_number_literal)
 {
@@ -436,6 +451,40 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
     zig_unreachable();
 }
 
+static TypeTableEntry *eval_const_expr_fn_call(CodeGen *g, BlockContext *context,
+        AstNode *node, AstNodeNumberLiteral *out_number_literal)
+{
+    if (!node->data.fn_call_expr.is_builtin) {
+        return g->builtin_types.entry_invalid;
+    }
+
+    switch (node->data.fn_call_expr.builtin_fn->id) {
+        case BuiltinFnIdInvalid:
+            zig_unreachable();
+        case BuiltinFnIdArithmeticWithOverflow:
+        case BuiltinFnIdMemcpy:
+        case BuiltinFnIdMemset:
+            return g->builtin_types.entry_invalid;
+        case BuiltinFnIdSizeof:
+            {
+                AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                TypeTableEntry *target_type = unwrapped_node_type(type_node);
+                out_number_literal->overflow = false;
+                out_number_literal->data.x_uint = target_type->size_in_bits / 8;
+                out_number_literal->kind = get_number_literal_kind_unsigned(out_number_literal->data.x_uint);
+                return get_resolved_expr(node)->type_entry;
+            }
+        case BuiltinFnIdMaxValue:
+        case BuiltinFnIdMinValue:
+            zig_panic("TODO eval_const_expr_fn_call max/min value");
+        case BuiltinFnIdValueCount:
+            zig_panic("TODO eval_const_expr_fn_call value_count");
+        case BuiltinFnIdTypeof:
+            return get_resolved_expr(node)->type_entry;
+    }
+    zig_unreachable();
+}
+
 static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
         AstNode *node, AstNodeNumberLiteral *out_number_literal)
 {
@@ -450,175 +499,25 @@ static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
             return get_resolved_expr(node)->type_entry;
         case NodeTypeBinOpExpr:
             return eval_const_expr_bin_op(g, context, node, out_number_literal);
-        case NodeTypeCompilerFnType:
-            {
-                Buf *name = &node->data.compiler_fn_type.name;
-                TypeTableEntry *expr_type = get_resolved_expr(node)->type_entry;
-                if (buf_eql_str(name, "sizeof")) {
-                    TypeTableEntry *target_type = node->data.compiler_fn_type.type->data.type.entry;
-                    out_number_literal->overflow = false;
-                    out_number_literal->data.x_uint = target_type->size_in_bits / 8;
-                    out_number_literal->kind = get_number_literal_kind_unsigned(out_number_literal->data.x_uint);
-
-                    return expr_type;
-                } else if (buf_eql_str(name, "max_value")) {
-                    zig_panic("TODO eval_const_expr max_value");
-                } else if (buf_eql_str(name, "min_value")) {
-                    zig_panic("TODO eval_const_expr min_value");
-                } else if (buf_eql_str(name, "value_count")) {
-                    zig_panic("TODO eval_const_expr value_count");
-                } else {
-                    return g->builtin_types.entry_invalid;
-                }
-                break;
-            }
         case NodeTypeSymbol:
             {
                 VariableTableEntry *var = find_variable(context, &node->data.symbol_expr.symbol);
                 assert(var);
                 AstNode *decl_node = var->decl_node;
                 AstNode *expr_node = decl_node->data.variable_declaration.expr;
-                BlockContext *next_context = get_resolved_expr(expr_node)->block_context;
-                return eval_const_expr(g, next_context, expr_node, out_number_literal);
-            }
-        default:
-            return g->builtin_types.entry_invalid;
-    }
-}
-
-static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry *import,
-        BlockContext *context, bool noalias_allowed)
-{
-    assert(node->type == NodeTypeType);
-    switch (node->data.type.type) {
-        case AstNodeTypeTypePrimitive:
-            {
-                Buf *name = &node->data.type.primitive_name;
-                auto table_entry = import->block_context->type_table.maybe_get(name);
-                if (!table_entry) {
-                    table_entry = g->primitive_type_table.maybe_get(name);
-                }
-                if (table_entry) {
-                    node->data.type.entry = table_entry->value;
-                } else {
-                    add_node_error(g, node,
-                            buf_sprintf("invalid type name: '%s'", buf_ptr(name)));
-                    node->data.type.entry = g->builtin_types.entry_invalid;
-                }
-                return node->data.type.entry;
-            }
-        case AstNodeTypeTypePointer:
-            {
-                bool use_noalias = false;
-                if (node->data.type.is_noalias) {
-                    if (!noalias_allowed) {
-                        add_node_error(g, node,
-                                buf_create_from_str("invalid noalias qualifier"));
-                    } else {
-                        use_noalias = true;
-                    }
-                }
-
-                resolve_type(g, node->data.type.child_type, import, context, false);
-                TypeTableEntry *child_type = node->data.type.child_type->data.type.entry;
-                assert(child_type);
-                if (child_type->id == TypeTableEntryIdUnreachable) {
-                    add_node_error(g, node,
-                            buf_create_from_str("pointer to unreachable not allowed"));
-                    node->data.type.entry = g->builtin_types.entry_invalid;
-                    return node->data.type.entry;
-                } else if (child_type->id == TypeTableEntryIdInvalid) {
-                    node->data.type.entry = child_type;
-                    return child_type;
-                } else {
-                    node->data.type.entry = get_pointer_to_type(g, child_type, node->data.type.is_const, use_noalias);
-                    return node->data.type.entry;
-                }
-            }
-        case AstNodeTypeTypeArray:
-            {
-                AstNode *size_node = node->data.type.array_size;
-
-                bool use_noalias = false;
-                if (node->data.type.is_noalias) {
-                    if (!noalias_allowed || size_node) {
-                        add_node_error(g, node,
-                                buf_create_from_str("invalid noalias qualifier"));
-                    } else {
-                        use_noalias = true;
-                    }
-                }
-
-                TypeTableEntry *child_type = resolve_type(g, node->data.type.child_type, import, context, false);
-                if (child_type->id == TypeTableEntryIdUnreachable) {
-                    add_node_error(g, node,
-                            buf_create_from_str("array of unreachable not allowed"));
-                    node->data.type.entry = g->builtin_types.entry_invalid;
-                    return node->data.type.entry;
-                }
-
-                if (size_node) {
-                    TypeTableEntry *size_type = analyze_expression(g, import, context,
-                            g->builtin_types.entry_usize, size_node);
-                    if (size_type->id == TypeTableEntryIdInvalid) {
-                        node->data.type.entry = g->builtin_types.entry_invalid;
-                        return node->data.type.entry;
-                    }
-
-                    AstNodeNumberLiteral number_literal;
-                    TypeTableEntry *resolved_type = eval_const_expr(g, context, size_node, &number_literal);
-
-                    if (resolved_type->id == TypeTableEntryIdInt) {
-                        if (resolved_type->data.integral.is_signed) {
-                            add_node_error(g, size_node,
-                                buf_create_from_str("array size must be unsigned integer"));
-                            node->data.type.entry = g->builtin_types.entry_invalid;
-                        } else {
-                            node->data.type.entry = get_array_type(g, import, child_type, number_literal.data.x_uint);
-                        }
-                    } else {
-                        add_node_error(g, size_node,
-                            buf_create_from_str("unable to resolve constant expression"));
-                        node->data.type.entry = g->builtin_types.entry_invalid;
-                    }
-                    return node->data.type.entry;
-                } else {
-                    node->data.type.entry = get_unknown_size_array_type(g, import, child_type,
-                            node->data.type.is_const, use_noalias);
-                    return node->data.type.entry;
-                }
-
-            }
-        case AstNodeTypeTypeMaybe:
-            {
-                resolve_type(g, node->data.type.child_type, import, context, false);
-                TypeTableEntry *child_type = node->data.type.child_type->data.type.entry;
-                assert(child_type);
-                if (child_type->id == TypeTableEntryIdUnreachable) {
-                    add_node_error(g, node,
-                            buf_create_from_str("maybe unreachable type not allowed"));
-                } else if (child_type->id == TypeTableEntryIdInvalid) {
-                    return child_type;
-                }
-                node->data.type.entry = get_maybe_type(g, import, child_type);
-                return node->data.type.entry;
-            }
-        case AstNodeTypeTypeCompilerExpr:
-            {
-                AstNode *compiler_expr_node = node->data.type.compiler_expr;
-                Buf *fn_name = &compiler_expr_node->data.compiler_fn_expr.name;
-                if (buf_eql_str(fn_name, "typeof")) {
-                    node->data.type.entry = analyze_expression(g, import, context, nullptr,
-                            compiler_expr_node->data.compiler_fn_expr.expr);
+                if (expr_node) {
+                    BlockContext *next_context = get_resolved_expr(expr_node)->block_context;
+                    return eval_const_expr(g, next_context, expr_node, out_number_literal);
                 } else {
-                    add_node_error(g, node,
-                            buf_sprintf("invalid compiler function: '%s'", buf_ptr(fn_name)));
-                    node->data.type.entry = g->builtin_types.entry_invalid;
+                    // can't eval it
+                    return g->builtin_types.entry_invalid;
                 }
-                return node->data.type.entry;
             }
+        case NodeTypeFnCallExpr:
+            return eval_const_expr_fn_call(g, context, node, out_number_literal);
+        default:
+            return g->builtin_types.entry_invalid;
     }
-    zig_unreachable();
 }
 
 static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry,
@@ -654,8 +553,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
     for (int i = 0; i < node->data.fn_proto.params.length; i += 1) {
         AstNode *child = node->data.fn_proto.params.at(i);
         assert(child->type == NodeTypeParamDecl);
-        TypeTableEntry *type_entry = resolve_type(g, child->data.param_decl.type,
-                import, import->block_context, true);
+        TypeTableEntry *type_entry = analyze_type_expr(g, import, import->block_context,
+                child->data.param_decl.type);
+
         if (type_entry->id == TypeTableEntryIdUnreachable) {
             add_node_error(g, child->data.param_decl.type,
                 buf_sprintf("parameter of type 'unreachable' not allowed"));
@@ -667,7 +567,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
         }
     }
 
-    resolve_type(g, node->data.fn_proto.return_type, import, import->block_context, true);
+    analyze_type_expr(g, import, import->block_context, node->data.fn_proto.return_type);
 }
 
 static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry) {
@@ -729,8 +629,8 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt
         AstNode *field_node = decl_node->data.struct_decl.fields.at(i);
         TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i];
         type_enum_field->name = &field_node->data.struct_field.name;
-        type_enum_field->type_entry = resolve_type(g, field_node->data.struct_field.type,
-                import, import->block_context, false);
+        type_enum_field->type_entry = analyze_type_expr(g, import, import->block_context,
+                field_node->data.struct_field.type);
         type_enum_field->value = i;
 
         di_enumerators[i] = LLVMZigCreateDebugEnumerator(g->dbuilder, buf_ptr(type_enum_field->name), i);
@@ -815,7 +715,7 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt
                     biggest_align_in_bits,
                     tag_type_entry->size_in_bits, 0, union_di_type);
 
-            // create debug type for root struct 
+            // create debug type for root struct
             LLVMZigDIType *di_root_members[] = {
                 tag_member_di_type,
                 union_member_di_type,
@@ -893,8 +793,8 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE
         AstNode *field_node = decl_node->data.struct_decl.fields.at(i);
         TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
         type_struct_field->name = &field_node->data.struct_field.name;
-        type_struct_field->type_entry = resolve_type(g, field_node->data.struct_field.type,
-                import, import->block_context, false);
+        type_struct_field->type_entry = analyze_type_expr(g, import, import->block_context,
+                field_node->data.struct_field.type);
         type_struct_field->src_index = i;
         type_struct_field->gen_index = -1;
 
@@ -1144,7 +1044,6 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeFnDef:
         case NodeTypeDirective:
         case NodeTypeParamDecl:
-        case NodeTypeType:
         case NodeTypeFnDecl:
         case NodeTypeReturnExpr:
         case NodeTypeRoot:
@@ -1156,8 +1055,6 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeNumberLiteral:
         case NodeTypeStringLiteral:
         case NodeTypeCharLiteral:
-        case NodeTypeUnreachable:
-        case NodeTypeVoid:
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeSymbol:
@@ -1173,10 +1070,9 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeAsmExpr:
         case NodeTypeFieldAccessExpr:
         case NodeTypeStructField:
-        case NodeTypeStructValueExpr:
         case NodeTypeStructValueField:
-        case NodeTypeCompilerFnExpr:
-        case NodeTypeCompilerFnType:
+        case NodeTypeContainerInitExpr:
+        case NodeTypeArrayType:
             zig_unreachable();
     }
 }
@@ -1186,13 +1082,22 @@ static FnTableEntry *get_context_fn_entry(BlockContext *context) {
     return context->fn_entry;
 }
 
+static TypeTableEntry *unwrapped_node_type(AstNode *node) {
+    TypeTableEntry *meta_type_entry = get_resolved_expr(node)->type_entry;
+    if (meta_type_entry->id == TypeTableEntryIdInvalid) {
+        return meta_type_entry;
+    } else {
+        assert(meta_type_entry->id == TypeTableEntryIdMetaType);
+        return meta_type_entry->data.meta_type.child_type;
+    }
+}
+
 static TypeTableEntry *get_return_type(BlockContext *context) {
     FnTableEntry *fn_entry = get_context_fn_entry(context);
     AstNode *fn_proto_node = fn_entry->proto_node;
     assert(fn_proto_node->type == NodeTypeFnProto);
     AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type;
-    assert(return_type_node->type == NodeTypeType);
-    return return_type_node->data.type.entry;
+    return unwrapped_node_type(return_type_node);
 }
 
 static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) {
@@ -1553,6 +1458,114 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp
     return enum_type;
 }
 
+static TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name, int *index) {
+    assert(type_entry->id == TypeTableEntryIdStruct);
+    for (int i = 0; i < type_entry->data.structure.field_count; i += 1) {
+        TypeStructField *field = &type_entry->data.structure.fields[i];
+        if (buf_eql_buf(field->name, name)) {
+            *index = i;
+            return field;
+        }
+    }
+    return nullptr;
+}
+
+static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        AstNode *node)
+{
+    assert(node->type == NodeTypeContainerInitExpr);
+
+    AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
+
+    ContainerInitKind kind = container_init_expr->kind;
+
+    TypeTableEntry *container_type = analyze_type_expr(g, import, context, container_init_expr->type);
+
+    if (container_type->id == TypeTableEntryIdInvalid) {
+        return container_type;
+    } else if (container_type->id == TypeTableEntryIdStruct) {
+        switch (kind) {
+            case ContainerInitKindStruct:
+                {
+                    StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr;
+                    codegen->type_entry = container_type;
+                    codegen->source_node = node;
+                    context->struct_val_expr_alloca_list.append(codegen);
+
+
+                    int expr_field_count = container_init_expr->entries.length;
+                    int actual_field_count = container_type->data.structure.field_count;
+
+                    int *field_use_counts = allocate<int>(actual_field_count);
+                    for (int i = 0; i < expr_field_count; i += 1) {
+                        AstNode *val_field_node = container_init_expr->entries.at(i);
+                        assert(val_field_node->type == NodeTypeStructValueField);
+
+                        int field_index;
+                        TypeStructField *type_field = find_struct_type_field(container_type,
+                                &val_field_node->data.struct_val_field.name, &field_index);
+
+                        if (!type_field) {
+                            add_node_error(g, val_field_node,
+                                buf_sprintf("no member named '%s' in '%s'",
+                                    buf_ptr(&val_field_node->data.struct_val_field.name), buf_ptr(&container_type->name)));
+                            continue;
+                        }
+
+                        field_use_counts[field_index] += 1;
+                        if (field_use_counts[field_index] > 1) {
+                            add_node_error(g, val_field_node, buf_sprintf("duplicate field"));
+                            continue;
+                        }
+
+                        val_field_node->data.struct_val_field.type_struct_field = type_field;
+
+                        analyze_expression(g, import, context, type_field->type_entry,
+                                val_field_node->data.struct_val_field.expr);
+                    }
+
+                    for (int i = 0; i < actual_field_count; i += 1) {
+                        if (field_use_counts[i] == 0) {
+                            add_node_error(g, node,
+                                buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name)));
+                        }
+                    }
+                    break;
+                }
+            case ContainerInitKindArray:
+                add_node_error(g, node,
+                    buf_sprintf("struct '%s' does not support array initialization syntax",
+                        buf_ptr(&container_type->name)));
+                break;
+        }
+        return container_type;
+    } else if (container_type->id == TypeTableEntryIdArray) {
+        zig_panic("TODO array container init");
+        return container_type;
+    } else if (container_type->id == TypeTableEntryIdEnum) {
+        zig_panic("TODO enum container init");
+        return container_type;
+    } else if (container_type->id == TypeTableEntryIdVoid) {
+        if (container_init_expr->entries.length != 0) {
+            add_node_error(g, node, buf_sprintf("void expression expects no arguments"));
+            return g->builtin_types.entry_invalid;
+        } else {
+            return container_type;
+        }
+    } else if (container_type->id == TypeTableEntryIdUnreachable) {
+        if (container_init_expr->entries.length != 0) {
+            add_node_error(g, node, buf_sprintf("unreachable expression expects no arguments"));
+            return g->builtin_types.entry_invalid;
+        } else {
+            return container_type;
+        }
+    } else {
+        add_node_error(g, node,
+            buf_sprintf("type '%s' does not support initialization syntax", buf_ptr(&container_type->name)));
+        return g->builtin_types.entry_invalid;
+    }
+}
+
 static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         AstNode *node)
 {
@@ -1585,7 +1598,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
             return_type = g->builtin_types.entry_usize;
         } else if (buf_eql_str(name, "ptr")) {
             // TODO determine whether the pointer should be const
-            return_type = get_pointer_to_type(g, struct_type->data.array.child_type, false, false);
+            return_type = get_pointer_to_type(g, struct_type->data.array.child_type, false);
         } else {
             add_node_error(g, node,
                 buf_sprintf("no member named '%s' in '%s'", buf_ptr(name),
@@ -1623,16 +1636,16 @@ static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import,
         return_type = g->builtin_types.entry_invalid;
     } else if (array_type->id == TypeTableEntryIdArray) {
         return_type = get_unknown_size_array_type(g, import, array_type->data.array.child_type,
-                node->data.slice_expr.is_const, false);
+                node->data.slice_expr.is_const);
     } else if (array_type->id == TypeTableEntryIdPointer) {
         return_type = get_unknown_size_array_type(g, import, array_type->data.pointer.child_type,
-                node->data.slice_expr.is_const, false);
+                node->data.slice_expr.is_const);
     } else if (array_type->id == TypeTableEntryIdStruct &&
                array_type->data.structure.is_unknown_size_array)
     {
         return_type = get_unknown_size_array_type(g, import,
                 array_type->data.structure.fields[0].type_entry->data.pointer.child_type,
-                node->data.slice_expr.is_const, false);
+                node->data.slice_expr.is_const);
     } else {
         add_node_error(g, node,
             buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));
@@ -1687,19 +1700,24 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import,
         TypeTableEntry *expected_type, AstNode *node)
 {
     Buf *variable_name = &node->data.symbol_expr.symbol;
+
+    auto primitive_table_entry = g->primitive_type_table.maybe_get(variable_name);
+    if (primitive_table_entry) {
+        return get_meta_type(g, primitive_table_entry->value);
+    }
+
     VariableTableEntry *var = find_variable(context, variable_name);
     if (var) {
         return var->type;
-    } else {
-        TypeTableEntry *container_type = find_container(context, variable_name);
-        if (container_type) {
-            return get_meta_type(g, container_type);
-        } else {
-            add_node_error(g, node,
-                    buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
-            return g->builtin_types.entry_invalid;
-        }
     }
+
+    TypeTableEntry *container_type = find_container(context, variable_name);
+    if (container_type) {
+        return get_meta_type(g, container_type);
+    }
+
+    add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
+    return g->builtin_types.entry_invalid;
 }
 
 static TypeTableEntry *analyze_variable_name(CodeGen *g, ImportTableEntry *import, BlockContext *context,
@@ -1768,7 +1786,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
 {
     assert(node->type == NodeTypeCastExpr);
 
-    TypeTableEntry *wanted_type = resolve_type(g, node->data.cast_expr.type, import, context, false);
+    TypeTableEntry *wanted_type = analyze_type_expr(g, import, context, node->data.cast_expr.type);
     TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr, node->data.cast_expr.expr);
 
     if (wanted_type->id == TypeTableEntryIdInvalid ||
@@ -1833,23 +1851,22 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc
     TypeTableEntry *expected_rhs_type = nullptr;
     if (lhs_node->type == NodeTypeSymbol) {
         Buf *name = &lhs_node->data.symbol_expr.symbol;
-        VariableTableEntry *var = find_variable(block_context, name);
-        if (var) {
-            if (purpose == LValPurposeAssign && var->is_const) {
-                add_node_error(g, lhs_node,
-                    buf_sprintf("cannot assign to constant"));
-                expected_rhs_type = g->builtin_types.entry_invalid;
-            } else if (purpose == LValPurposeAddressOf && var->is_const && !is_ptr_const) {
+        if (purpose == LValPurposeAddressOf) {
+            expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node);
+        } else {
+            VariableTableEntry *var = find_variable(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;
+                }
+            } else {
                 add_node_error(g, lhs_node,
-                    buf_sprintf("must use &const to get address of constant"));
+                        buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
                 expected_rhs_type = g->builtin_types.entry_invalid;
-            } else {
-                expected_rhs_type = var->type;
             }
-        } else {
-            add_node_error(g, lhs_node,
-                    buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
-            expected_rhs_type = g->builtin_types.entry_invalid;
         }
     } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
         expected_rhs_type = analyze_array_access_expr(g, import, block_context, lhs_node);
@@ -1873,13 +1890,19 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc
         }
     } else {
         if (purpose == LValPurposeAssign) {
-            add_node_error(g, lhs_node,
-                    buf_sprintf("invalid assignment target"));
+            add_node_error(g, lhs_node, buf_sprintf("invalid assignment target"));
+            expected_rhs_type = g->builtin_types.entry_invalid;
         } else if (purpose == LValPurposeAddressOf) {
-            add_node_error(g, lhs_node,
-                    buf_sprintf("invalid addressof target"));
+            TypeTableEntry *type_entry = analyze_expression(g, import, block_context, nullptr, lhs_node);
+            if (type_entry->id == TypeTableEntryIdInvalid) {
+                expected_rhs_type = g->builtin_types.entry_invalid;
+            } else if (type_entry->id == TypeTableEntryIdMetaType) {
+                expected_rhs_type = type_entry;
+            } else {
+                add_node_error(g, lhs_node, buf_sprintf("invalid addressof target"));
+                expected_rhs_type = g->builtin_types.entry_invalid;
+            }
         }
-        expected_rhs_type = g->builtin_types.entry_invalid;
     }
     assert(expected_rhs_type);
     return expected_rhs_type;
@@ -1988,7 +2011,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
 {
     TypeTableEntry *explicit_type = nullptr;
     if (variable_declaration->type != nullptr) {
-        explicit_type = resolve_type(g, variable_declaration->type, import, context, false);
+        explicit_type = analyze_type_expr(g, import, context, variable_declaration->type);
         if (explicit_type->id == TypeTableEntryIdUnreachable) {
             add_node_error(g, variable_declaration->type,
                 buf_sprintf("variable of type 'unreachable' not allowed"));
@@ -2109,78 +2132,46 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry
     }
 }
 
-static TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name, int *index) {
-    assert(type_entry->id == TypeTableEntryIdStruct);
-    for (int i = 0; i < type_entry->data.structure.field_count; i += 1) {
-        TypeStructField *field = &type_entry->data.structure.fields[i];
-        if (buf_eql_buf(field->name, name)) {
-            *index = i;
-            return field;
-        }
-    }
-    return nullptr;
-}
-
-static TypeTableEntry *analyze_struct_val_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
-    assert(node->type == NodeTypeStructValueExpr);
-
-    AstNodeStructValueExpr *struct_val_expr = &node->data.struct_val_expr;
+    AstNode *size_node = node->data.array_type.size;
 
-    TypeTableEntry *type_entry = resolve_type(g, struct_val_expr->type, import, context, false);
+    TypeTableEntry *child_type = analyze_type_expr(g, import, context, node->data.array_type.child_type);
 
-    if (type_entry->id == TypeTableEntryIdInvalid) {
+    if (child_type->id == TypeTableEntryIdUnreachable) {
+        add_node_error(g, node, buf_create_from_str("array of unreachable not allowed"));
         return g->builtin_types.entry_invalid;
-    } else if (type_entry->id != TypeTableEntryIdStruct) {
-        add_node_error(g, node,
-            buf_sprintf("type '%s' is not a struct", buf_ptr(&type_entry->name)));
+    } else if (child_type->id == TypeTableEntryIdInvalid) {
         return g->builtin_types.entry_invalid;
     }
 
-    node->data.struct_val_expr.codegen.type_entry = type_entry;
-    node->data.struct_val_expr.codegen.source_node = node;
-    context->struct_val_expr_alloca_list.append(&node->data.struct_val_expr.codegen);
-
-    int expr_field_count = struct_val_expr->fields.length;
-    int actual_field_count = type_entry->data.structure.field_count;
-
-    int *field_use_counts = allocate<int>(actual_field_count);
-    for (int i = 0; i < expr_field_count; i += 1) {
-        AstNode *val_field_node = struct_val_expr->fields.at(i);
-        assert(val_field_node->type == NodeTypeStructValueField);
-
-        int field_index;
-        TypeStructField *type_field = find_struct_type_field(type_entry,
-                &val_field_node->data.struct_val_field.name, &field_index);
-
-        if (!type_field) {
-            add_node_error(g, val_field_node,
-                buf_sprintf("no member named '%s' in '%s'",
-                    buf_ptr(&val_field_node->data.struct_val_field.name), buf_ptr(&type_entry->name)));
-            continue;
-        }
-
-        field_use_counts[field_index] += 1;
-        if (field_use_counts[field_index] > 1) {
-            add_node_error(g, val_field_node, buf_sprintf("duplicate field"));
-            continue;
+    if (size_node) {
+        TypeTableEntry *size_type = analyze_expression(g, import, context,
+                g->builtin_types.entry_usize, size_node);
+        if (size_type->id == TypeTableEntryIdInvalid) {
+            return g->builtin_types.entry_invalid;
         }
 
-        val_field_node->data.struct_val_field.type_struct_field = type_field;
-
-        analyze_expression(g, import, context, type_field->type_entry,
-                val_field_node->data.struct_val_field.expr);
-    }
+        AstNodeNumberLiteral number_literal;
+        TypeTableEntry *resolved_type = eval_const_expr(g, context, size_node, &number_literal);
 
-    for (int i = 0; i < actual_field_count; i += 1) {
-        if (field_use_counts[i] == 0) {
-            add_node_error(g, node,
-                buf_sprintf("missing field: '%s'", buf_ptr(type_entry->data.structure.fields[i].name)));
+        if (resolved_type->id == TypeTableEntryIdInt) {
+            if (resolved_type->data.integral.is_signed) {
+                add_node_error(g, size_node,
+                    buf_create_from_str("array size must be unsigned integer"));
+                return g->builtin_types.entry_invalid;
+            } else {
+                return get_meta_type(g, get_array_type(g, import, child_type, number_literal.data.x_uint));
+            }
+        } else {
+            add_node_error(g, size_node,
+                buf_create_from_str("unable to resolve constant expression"));
+            return g->builtin_types.entry_invalid;
         }
+    } else {
+        return get_meta_type(g, get_unknown_size_array_type(g, import, child_type, node->data.array_type.is_const));
     }
-
-    return type_entry;
 }
 
 static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
@@ -2293,9 +2284,14 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import,
             node->data.if_var_expr.then_block, node->data.if_var_expr.else_node, node);
 }
 
-static TypeTableEntry *analyze_min_max_value(CodeGen *g, AstNode *node, TypeTableEntry *type_entry,
-        const char *err_format)
+static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        AstNode *node, const char *err_format)
 {
+    assert(node->type == NodeTypeFnCallExpr);
+    assert(node->data.fn_call_expr.params.length == 1);
+
+    AstNode *type_node = node->data.fn_call_expr.params.at(0);
+    TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
     if (type_entry->id == TypeTableEntryIdInt ||
         type_entry->id == TypeTableEntryIdFloat ||
         type_entry->id == TypeTableEntryIdBool ||
@@ -2309,54 +2305,6 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, AstNode *node, TypeTabl
     }
 }
 
-static TypeTableEntry *analyze_compiler_fn_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        TypeTableEntry *expected_type, AstNode *node)
-{
-    assert(node->type == NodeTypeCompilerFnType);
-
-    Buf *name = &node->data.compiler_fn_type.name;
-    TypeTableEntry *type_entry = resolve_type(g, node->data.compiler_fn_type.type, import, context, false);
-
-    if (buf_eql_str(name, "sizeof")) {
-        if (type_entry->id == TypeTableEntryIdInvalid) {
-            return type_entry;
-        } else if (type_entry->id == TypeTableEntryIdUnreachable) {
-            add_node_error(g, node,
-                    buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
-            return g->builtin_types.entry_invalid;
-        } else {
-            uint64_t size_in_bytes = type_entry->size_in_bits / 8;
-
-            TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, size_in_bytes);
-            TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type);
-            return resolved_type ? resolved_type : num_lit_type;
-        }
-    } else if (buf_eql_str(name, "min_value")) {
-        return analyze_min_max_value(g, node, type_entry, "no min value available for type '%s'");
-    } else if (buf_eql_str(name, "max_value")) {
-        return analyze_min_max_value(g, node, type_entry, "no max value available for type '%s'");
-    } else if (buf_eql_str(name, "value_count")) {
-        if (type_entry->id == TypeTableEntryIdInvalid) {
-            return type_entry;
-        } else if (type_entry->id == TypeTableEntryIdEnum) {
-            uint64_t value_count = type_entry->data.enumeration.field_count;
-
-            TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, value_count);
-            TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type);
-            return resolved_type ? resolved_type : num_lit_type;
-
-        } else {
-            add_node_error(g, node,
-                    buf_sprintf("no value count available for type '%s'", buf_ptr(&type_entry->name)));
-            return g->builtin_types.entry_invalid;
-        }
-    } else {
-        add_node_error(g, node,
-                buf_sprintf("invalid compiler function: '%s'", buf_ptr(name)));
-        return g->builtin_types.entry_invalid;
-    }
-}
-
 static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
@@ -2445,6 +2393,62 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
 
                     return builtin_fn->return_type;
                 }
+            case BuiltinFnIdSizeof:
+                {
+                    AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                    TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
+                    if (type_entry->id == TypeTableEntryIdInvalid) {
+                        return g->builtin_types.entry_invalid;
+                    } else if (type_entry->id == TypeTableEntryIdUnreachable) {
+                        add_node_error(g, first_executing_node(type_node),
+                                buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
+                        return g->builtin_types.entry_invalid;
+                    } else {
+                        uint64_t size_in_bytes = type_entry->size_in_bits / 8;
+
+                        TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, size_in_bytes);
+                        TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type);
+                        return resolved_type ? resolved_type : num_lit_type;
+                    }
+                }
+            case BuiltinFnIdMaxValue:
+                return analyze_min_max_value(g, import, context, node, "no max value available for type '%s'");
+            case BuiltinFnIdMinValue:
+                return analyze_min_max_value(g, import, context, node, "no min value available for type '%s'");
+            case BuiltinFnIdValueCount:
+                {
+                    AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                    TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
+
+                    if (type_entry->id == TypeTableEntryIdInvalid) {
+                        return type_entry;
+                    } else if (type_entry->id == TypeTableEntryIdEnum) {
+                        uint64_t value_count = type_entry->data.enumeration.field_count;
+
+                        TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, value_count);
+                        TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type);
+                        return resolved_type ? resolved_type : num_lit_type;
+
+                    } else {
+                        add_node_error(g, node,
+                                buf_sprintf("no value count available for type '%s'", buf_ptr(&type_entry->name)));
+                        return g->builtin_types.entry_invalid;
+                    }
+                }
+            case BuiltinFnIdTypeof:
+                {
+                    AstNode *expr_node = node->data.fn_call_expr.params.at(0);
+                    TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
+
+                    if (type_entry->id == TypeTableEntryIdInvalid) {
+                        return g->builtin_types.entry_invalid;
+                    } else if (type_entry->id == TypeTableEntryIdMetaType) {
+                        add_node_error(g, node, buf_sprintf("expected expression, got type"));
+                    } else {
+                        return get_meta_type(g, type_entry);
+                    }
+                }
+
         }
         zig_unreachable();
     } else {
@@ -2560,15 +2564,127 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
                 AstNode *param_decl_node = fn_proto->params.at(fn_proto_i);
                 assert(param_decl_node->type == NodeTypeParamDecl);
                 AstNode *param_type_node = param_decl_node->data.param_decl.type;
-                assert(param_type_node->type == NodeTypeType);
-                if (param_type_node->data.type.entry) {
-                    expected_param_type = param_type_node->data.type.entry;
+                TypeTableEntry *param_type_entry = get_resolved_expr(param_type_node)->type_entry;
+                if (param_type_entry) {
+                    expected_param_type = unwrapped_node_type(param_type_node);
                 }
             }
             analyze_expression(g, import, context, expected_param_type, child);
         }
 
-        return fn_proto->return_type->data.type.entry;
+        return unwrapped_node_type(fn_proto->return_type);
+    }
+}
+
+static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node)
+{
+    switch (node->data.prefix_op_expr.prefix_op) {
+        case PrefixOpInvalid:
+            zig_unreachable();
+        case PrefixOpBoolNot:
+            analyze_expression(g, import, context, g->builtin_types.entry_bool,
+                    node->data.prefix_op_expr.primary_expr);
+            return g->builtin_types.entry_bool;
+        case PrefixOpBinNot:
+            {
+                AstNode *operand_node = node->data.prefix_op_expr.primary_expr;
+                TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type,
+                        operand_node);
+                if (expr_type->id == TypeTableEntryIdInvalid) {
+                    return expr_type;
+                } else if (expr_type->id == TypeTableEntryIdInt ||
+                        (expr_type->id == TypeTableEntryIdNumberLiteral &&
+                            !is_num_lit_float(expr_type->data.num_lit.kind)))
+                {
+                    return expr_type;
+                } else {
+                    add_node_error(g, operand_node, buf_sprintf("invalid binary not type: '%s'",
+                            buf_ptr(&expr_type->name)));
+                    return g->builtin_types.entry_invalid;
+                }
+            }
+        case PrefixOpNegation:
+            {
+                AstNode *operand_node = node->data.prefix_op_expr.primary_expr;
+                TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type,
+                        operand_node);
+                if (expr_type->id == TypeTableEntryIdInvalid) {
+                    return expr_type;
+                } else if (expr_type->id == TypeTableEntryIdInt &&
+                        expr_type->data.integral.is_signed)
+                {
+                    return expr_type;
+                } else if (expr_type->id == TypeTableEntryIdFloat) {
+                    return expr_type;
+                } else if (expr_type->id == TypeTableEntryIdNumberLiteral) {
+                    return expr_type;
+                } else {
+                    add_node_error(g, operand_node, buf_sprintf("invalid negation type: '%s'",
+                            buf_ptr(&expr_type->name)));
+                    return g->builtin_types.entry_invalid;
+                }
+            }
+        case PrefixOpAddressOf:
+        case PrefixOpConstAddressOf:
+            {
+                bool is_const = (node->data.prefix_op_expr.prefix_op == PrefixOpConstAddressOf);
+
+                TypeTableEntry *child_type = analyze_lvalue(g, import, context,
+                        node->data.prefix_op_expr.primary_expr, LValPurposeAddressOf, is_const);
+
+                if (child_type->id == TypeTableEntryIdInvalid) {
+                    return g->builtin_types.entry_invalid;
+                } else if (child_type->id == TypeTableEntryIdMetaType) {
+                    TypeTableEntry *meta_child_type = child_type->data.meta_type.child_type;
+                    if (meta_child_type->id == TypeTableEntryIdUnreachable) {
+                        add_node_error(g, node,
+                                buf_create_from_str("pointer to unreachable not allowed"));
+                    } else {
+                        return get_meta_type(g, get_pointer_to_type(g, meta_child_type, is_const));
+                    }
+                } else {
+                    return get_pointer_to_type(g, child_type, is_const);
+                }
+            }
+        case PrefixOpDereference:
+            {
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr,
+                        node->data.prefix_op_expr.primary_expr);
+                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, node->data.prefix_op_expr.primary_expr,
+                        buf_sprintf("indirection requires pointer operand ('%s' invalid)",
+                            buf_ptr(&type_entry->name)));
+                    return g->builtin_types.entry_invalid;
+                }
+            }
+        case PrefixOpMaybe:
+            {
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr,
+                        node->data.prefix_op_expr.primary_expr);
+
+                if (type_entry->id == TypeTableEntryIdInvalid) {
+                    return type_entry;
+                } else if (type_entry->id == TypeTableEntryIdMetaType) {
+                    TypeTableEntry *child_type = type_entry->data.meta_type.child_type;
+                    if (child_type->id == TypeTableEntryIdUnreachable) {
+                        add_node_error(g, node, buf_create_from_str("maybe unreachable type not allowed"));
+                        return g->builtin_types.entry_invalid;
+                    } else {
+                        return get_meta_type(g, get_maybe_type(g, child_type));
+                    }
+                } else if (type_entry->id == TypeTableEntryIdUnreachable) {
+                    add_node_error(g, node->data.prefix_op_expr.primary_expr,
+                        buf_sprintf("unable to wrap unreachable in maybe type"));
+                    return g->builtin_types.entry_invalid;
+                } else {
+                    return get_maybe_type(g, type_entry);
+                }
+            }
     }
 }
 
@@ -2593,9 +2709,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                         continue;
                     }
                     if (return_type->id == TypeTableEntryIdUnreachable) {
-                        if (child->type == NodeTypeVoid) {
+                        if (is_node_void_expr(child)) {
                             // {unreachable;void;void} is allowed.
                             // ignore void statements once we enter unreachable land.
+                            analyze_expression(g, import, context, g->builtin_types.entry_void, child);
                             continue;
                         }
                         add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code"));
@@ -2604,6 +2721,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                     bool is_last = (i == node->data.block.statements.length - 1);
                     TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr;
                     return_type = analyze_expression(g, import, child_context, passed_expected_type, child);
+                    if (!is_last && return_type->id == TypeTableEntryIdMetaType) {
+                        add_node_error(g, child, buf_sprintf("expected expression, found type"));
+                    }
                 }
                 break;
             }
@@ -2664,7 +2784,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                     AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
                     if (asm_output->return_type) {
                         node->data.asm_expr.return_count += 1;
-                        return_type = resolve_type(g, asm_output->return_type, import, context, false);
+                        return_type = analyze_type_expr(g, import, context, asm_output->return_type);
                         if (node->data.asm_expr.return_count > 1) {
                             add_node_error(g, node,
                                 buf_sprintf("inline assembly allows up to one output value"));
@@ -2699,6 +2819,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
         case NodeTypeFieldAccessExpr:
             return_type = analyze_field_access_expr(g, import, context, node);
             break;
+        case NodeTypeContainerInitExpr:
+            return_type = analyze_container_init_expr(g, import, context, node);
+            break;
         case NodeTypeNumberLiteral:
             return_type = analyze_number_literal_expr(g, import, context, expected_type, node);
             break;
@@ -2713,14 +2836,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
         case NodeTypeCharLiteral:
             return_type = g->builtin_types.entry_u8;
             break;
-        case NodeTypeUnreachable:
-            return_type = g->builtin_types.entry_unreachable;
-            break;
-
-        case NodeTypeVoid:
-            return_type = g->builtin_types.entry_void;
-            break;
-
         case NodeTypeBoolLiteral:
             return_type = g->builtin_types.entry_bool;
             break;
@@ -2730,96 +2845,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
             break;
 
         case NodeTypeSymbol:
-            {
-                return_type = analyze_symbol_expr(g, import, context, expected_type, node);
-                break;
-            }
+            return_type = analyze_symbol_expr(g, import, context, expected_type, node);
+            break;
         case NodeTypeCastExpr:
             return_type = analyze_cast_expr(g, import, context, expected_type, node);
             break;
         case NodeTypePrefixOpExpr:
-            switch (node->data.prefix_op_expr.prefix_op) {
-                case PrefixOpInvalid:
-                    zig_unreachable();
-                case PrefixOpBoolNot:
-                    analyze_expression(g, import, context, g->builtin_types.entry_bool,
-                            node->data.prefix_op_expr.primary_expr);
-                    return_type = g->builtin_types.entry_bool;
-                    break;
-                case PrefixOpBinNot:
-                    {
-                        AstNode *operand_node = node->data.prefix_op_expr.primary_expr;
-                        TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type,
-                                operand_node);
-                        if (expr_type->id == TypeTableEntryIdInvalid) {
-                            return_type = expr_type;
-                        } else if (expr_type->id == TypeTableEntryIdInt ||
-                                (expr_type->id == TypeTableEntryIdNumberLiteral &&
-                                 !is_num_lit_float(expr_type->data.num_lit.kind)))
-                        {
-                            return_type = expr_type;
-                        } else {
-                            add_node_error(g, operand_node, buf_sprintf("invalid binary not type: '%s'",
-                                    buf_ptr(&expr_type->name)));
-                            return_type = g->builtin_types.entry_invalid;
-                        }
-                        break;
-                    }
-                case PrefixOpNegation:
-                    {
-                        AstNode *operand_node = node->data.prefix_op_expr.primary_expr;
-                        TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type,
-                                operand_node);
-                        if (expr_type->id == TypeTableEntryIdInvalid) {
-                            return_type = expr_type;
-                        } else if (expr_type->id == TypeTableEntryIdInt &&
-                                expr_type->data.integral.is_signed)
-                        {
-                            return_type = expr_type;
-                        } else if (expr_type->id == TypeTableEntryIdFloat) {
-                            return_type = expr_type;
-                        } else if (expr_type->id == TypeTableEntryIdNumberLiteral) {
-                            return_type = expr_type;
-                        } else {
-                            add_node_error(g, operand_node, buf_sprintf("invalid negation type: '%s'",
-                                    buf_ptr(&expr_type->name)));
-                            return_type = g->builtin_types.entry_invalid;
-                        }
-                        break;
-                    }
-                case PrefixOpAddressOf:
-                case PrefixOpConstAddressOf:
-                    {
-                        bool is_const = (node->data.prefix_op_expr.prefix_op == PrefixOpConstAddressOf);
-
-                        TypeTableEntry *child_type = analyze_lvalue(g, import, context,
-                                node->data.prefix_op_expr.primary_expr, LValPurposeAddressOf, is_const);
-
-                        if (child_type->id == TypeTableEntryIdInvalid) {
-                            return_type = g->builtin_types.entry_invalid;
-                            break;
-                        }
-
-                        return_type = get_pointer_to_type(g, child_type, is_const, false);
-                        break;
-                    }
-                case PrefixOpDereference:
-                    {
-                        TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr,
-                                node->data.prefix_op_expr.primary_expr);
-                        if (type_entry->id == TypeTableEntryIdInvalid) {
-                            return_type = type_entry;
-                        } else if (type_entry->id == TypeTableEntryIdPointer) {
-                            return_type = type_entry->data.pointer.child_type;
-                        } else {
-                            add_node_error(g, node->data.prefix_op_expr.primary_expr,
-                                buf_sprintf("indirection requires pointer operand ('%s' invalid)",
-                                    buf_ptr(&type_entry->name)));
-                            return_type = g->builtin_types.entry_invalid;
-                        }
-                        break;
-                    }
-            }
+            return_type = analyze_prefix_op_expr(g, import, context, expected_type, node);
             break;
         case NodeTypeIfBoolExpr:
             return_type = analyze_if_bool_expr(g, import, context, expected_type, node);
@@ -2830,17 +2862,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
         case NodeTypeWhileExpr:
             return_type = analyze_while_expr(g, import, context, expected_type, node);
             break;
-        case NodeTypeStructValueExpr:
-            return_type = analyze_struct_val_expr(g, import, context, expected_type, node);
-            break;
-        case NodeTypeCompilerFnType:
-            return_type = analyze_compiler_fn_type(g, import, context, expected_type, node);
+        case NodeTypeArrayType:
+            return_type = analyze_array_type(g, import, context, expected_type, node);
             break;
         case NodeTypeDirective:
         case NodeTypeFnDecl:
         case NodeTypeFnProto:
         case NodeTypeParamDecl:
-        case NodeTypeType:
         case NodeTypeRoot:
         case NodeTypeRootExportDecl:
         case NodeTypeExternBlock:
@@ -2850,7 +2878,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
         case NodeTypeStructDecl:
         case NodeTypeStructField:
         case NodeTypeStructValueField:
-        case NodeTypeCompilerFnExpr:
             zig_unreachable();
     }
     assert(return_type);
@@ -2885,8 +2912,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
 
         // define local variables for parameters
         AstNodeParamDecl *param_decl = &param_decl_node->data.param_decl;
-        assert(param_decl->type->type == NodeTypeType);
-        TypeTableEntry *type = param_decl->type->data.type.entry;
+        TypeTableEntry *type = unwrapped_node_type(param_decl->type);
 
         if (is_exported && type->id == TypeTableEntryIdStruct) {
             add_node_error(g, param_decl_node,
@@ -2918,7 +2944,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
         }
     }
 
-    TypeTableEntry *expected_type = fn_proto->return_type->data.type.entry;
+    TypeTableEntry *expected_type = unwrapped_node_type(fn_proto->return_type);
     TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);
 
     node->data.fn_def.implicit_return_type = block_return_type;
@@ -2963,7 +2989,6 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeDirective:
         case NodeTypeParamDecl:
         case NodeTypeFnProto:
-        case NodeTypeType:
         case NodeTypeFnDecl:
         case NodeTypeReturnExpr:
         case NodeTypeRoot:
@@ -2975,8 +3000,6 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeNumberLiteral:
         case NodeTypeStringLiteral:
         case NodeTypeCharLiteral:
-        case NodeTypeUnreachable:
-        case NodeTypeVoid:
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeSymbol:
@@ -2992,177 +3015,150 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeAsmExpr:
         case NodeTypeFieldAccessExpr:
         case NodeTypeStructField:
-        case NodeTypeStructValueExpr:
         case NodeTypeStructValueField:
-        case NodeTypeCompilerFnExpr:
-        case NodeTypeCompilerFnType:
+        case NodeTypeContainerInitExpr:
+        case NodeTypeArrayType:
             zig_unreachable();
     }
 }
 
-static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *expr_node,
+static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node,
         TopLevelDecl *decl_node)
 {
-    switch (expr_node->type) {
+    switch (node->type) {
         case NodeTypeNumberLiteral:
         case NodeTypeStringLiteral:
         case NodeTypeCharLiteral:
-        case NodeTypeVoid:
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
-        case NodeTypeUnreachable:
         case NodeTypeGoto:
         case NodeTypeBreak:
         case NodeTypeContinue:
             // no dependencies on other top level declarations
             break;
         case NodeTypeSymbol:
-            decl_node->deps.put(&expr_node->data.symbol_expr.symbol, expr_node);
-            break;
+            {
+                Buf *name = &node->data.symbol_expr.symbol;
+                auto table_entry = g->primitive_type_table.maybe_get(name);
+                if (!table_entry) {
+                    table_entry = import->block_context->type_table.maybe_get(name);
+                }
+                if (!table_entry) {
+                    decl_node->deps.put(name, node);
+                }
+                break;
+            }
         case NodeTypeBinOpExpr:
-            collect_expr_decl_deps(g, import, expr_node->data.bin_op_expr.op1, decl_node);
-            collect_expr_decl_deps(g, import, expr_node->data.bin_op_expr.op2, decl_node);
+            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 NodeTypeReturnExpr:
-            collect_expr_decl_deps(g, import, expr_node->data.return_expr.expr, decl_node);
+            collect_expr_decl_deps(g, import, node->data.return_expr.expr, decl_node);
             break;
         case NodeTypeCastExpr:
-            collect_expr_decl_deps(g, import, expr_node->data.cast_expr.expr, decl_node);
-            collect_type_decl_deps(g, import, expr_node->data.cast_expr.type, decl_node);
+            collect_expr_decl_deps(g, import, node->data.cast_expr.expr, decl_node);
+            collect_expr_decl_deps(g, import, node->data.cast_expr.type, decl_node);
             break;
         case NodeTypePrefixOpExpr:
-            collect_expr_decl_deps(g, import, expr_node->data.prefix_op_expr.primary_expr, decl_node);
+            collect_expr_decl_deps(g, import, node->data.prefix_op_expr.primary_expr, decl_node);
             break;
         case NodeTypeFnCallExpr:
-            collect_expr_decl_deps(g, import, expr_node->data.fn_call_expr.fn_ref_expr, decl_node);
-            for (int i = 0; i < expr_node->data.fn_call_expr.params.length; i += 1) {
-                AstNode *arg_node = expr_node->data.fn_call_expr.params.at(i);
+            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, expr_node->data.array_access_expr.array_ref_expr, decl_node);
-            collect_expr_decl_deps(g, import, expr_node->data.array_access_expr.subscript, decl_node);
+            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, expr_node->data.slice_expr.array_ref_expr, decl_node);
-            collect_expr_decl_deps(g, import, expr_node->data.slice_expr.start, decl_node);
-            if (expr_node->data.slice_expr.end) {
-                collect_expr_decl_deps(g, import, expr_node->data.slice_expr.end, decl_node);
+            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, expr_node->data.field_access_expr.struct_expr, decl_node);
+            collect_expr_decl_deps(g, import, node->data.field_access_expr.struct_expr, decl_node);
             break;
         case NodeTypeIfBoolExpr:
-            collect_expr_decl_deps(g, import, expr_node->data.if_bool_expr.condition, decl_node);
-            collect_expr_decl_deps(g, import, expr_node->data.if_bool_expr.then_block, decl_node);
-            if (expr_node->data.if_bool_expr.else_node) {
-                collect_expr_decl_deps(g, import, expr_node->data.if_bool_expr.else_node, decl_node);
+            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 (expr_node->data.if_var_expr.var_decl.type) {
-                collect_type_decl_deps(g, import, expr_node->data.if_var_expr.var_decl.type, decl_node);
+            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 (expr_node->data.if_var_expr.var_decl.expr) {
-                collect_expr_decl_deps(g, import, expr_node->data.if_var_expr.var_decl.expr, 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, expr_node->data.if_var_expr.then_block, decl_node);
-            if (expr_node->data.if_bool_expr.else_node) {
-                collect_expr_decl_deps(g, import, expr_node->data.if_var_expr.else_node, 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, expr_node->data.while_expr.condition, decl_node);
-            collect_expr_decl_deps(g, import, expr_node->data.while_expr.body, decl_node);
+            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 NodeTypeBlock:
-            for (int i = 0; i < expr_node->data.block.statements.length; i += 1) {
-                AstNode *stmt = expr_node->data.block.statements.at(i);
+            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 < expr_node->data.asm_expr.output_list.length; i += 1) {
-                AsmOutput *asm_output = expr_node->data.asm_expr.output_list.at(i);
+            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_type_decl_deps(g, import, asm_output->return_type, decl_node);
+                    collect_expr_decl_deps(g, import, asm_output->return_type, decl_node);
                 } else {
-                    decl_node->deps.put(&asm_output->variable_name, expr_node);
+                    decl_node->deps.put(&asm_output->variable_name, node);
                 }
             }
-            for (int i = 0; i < expr_node->data.asm_expr.input_list.length; i += 1) {
-                AsmInput *asm_input = expr_node->data.asm_expr.input_list.at(i);
+            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 NodeTypeStructValueExpr:
-            collect_type_decl_deps(g, import, expr_node->data.struct_val_expr.type, decl_node);
-            for (int i = 0; i < expr_node->data.struct_val_expr.fields.length; i += 1) {
-                AstNode *field_node = expr_node->data.struct_val_expr.fields.at(i);
-                assert(field_node->type == NodeTypeStructValueField);
-                collect_expr_decl_deps(g, import, field_node->data.struct_val_field.expr, decl_node);
+        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 NodeTypeCompilerFnExpr:
-            collect_expr_decl_deps(g, import, expr_node->data.compiler_fn_expr.expr, decl_node);
+        case NodeTypeStructValueField:
+            collect_expr_decl_deps(g, import, node->data.struct_val_field.expr, decl_node);
             break;
-        case NodeTypeCompilerFnType:
-            collect_type_decl_deps(g, import, expr_node->data.compiler_fn_type.type, decl_node);
+        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 NodeTypeRoot:
-        case NodeTypeRootExportDecl:
+        case NodeTypeVariableDeclaration:
         case NodeTypeFnProto:
+        case NodeTypeExternBlock:
+        case NodeTypeRootExportDecl:
         case NodeTypeFnDef:
+        case NodeTypeRoot:
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
-        case NodeTypeType:
-        case NodeTypeExternBlock:
         case NodeTypeDirective:
-        case NodeTypeVariableDeclaration:
         case NodeTypeUse:
         case NodeTypeLabel:
         case NodeTypeStructDecl:
         case NodeTypeStructField:
-        case NodeTypeStructValueField:
             zig_unreachable();
     }
 }
 
-static void collect_type_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *type_node, TopLevelDecl *decl_node) {
-    assert(type_node->type == NodeTypeType);
-    switch (type_node->data.type.type) {
-        case AstNodeTypeTypePrimitive:
-            {
-                Buf *name = &type_node->data.type.primitive_name;
-                auto table_entry = g->primitive_type_table.maybe_get(name);
-                if (!table_entry) {
-                    table_entry = import->block_context->type_table.maybe_get(name);
-                }
-                if (!table_entry) {
-                    decl_node->deps.put(name, type_node);
-                }
-                break;
-            }
-        case AstNodeTypeTypePointer:
-            collect_type_decl_deps(g, import, type_node->data.type.child_type, decl_node);
-            break;
-        case AstNodeTypeTypeArray:
-            collect_type_decl_deps(g, import, type_node->data.type.child_type, decl_node);
-            if (type_node->data.type.array_size) {
-                collect_expr_decl_deps(g, import, type_node->data.type.array_size, decl_node);
-            }
-            break;
-        case AstNodeTypeTypeMaybe:
-            collect_type_decl_deps(g, import, type_node->data.type.child_type, decl_node);
-            break;
-        case AstNodeTypeTypeCompilerExpr:
-            collect_expr_decl_deps(g, import, type_node->data.type.compiler_expr, decl_node);
-            break;
-    }
-}
-
 static TypeTableEntryId container_to_type(ContainerKind kind) {
     switch (kind) {
         case ContainerKindStruct:
@@ -3231,7 +3227,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
                 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_type_decl_deps(g, import, type_node, decl_node);
+                    collect_expr_decl_deps(g, import, type_node, decl_node);
                 }
                 decl_node->name = name;
                 decl_node->import = import;
@@ -3271,7 +3267,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
                 TopLevelDecl *decl_node = &node->data.variable_declaration.top_level_decl;
                 decl_node->deps.init(1);
                 if (node->data.variable_declaration.type) {
-                    collect_type_decl_deps(g, import, node->data.variable_declaration.type, decl_node);
+                    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);
@@ -3294,8 +3290,9 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
                 for (int i = 0; i < node->data.fn_proto.params.length; i += 1) {
                     AstNode *param_node = node->data.fn_proto.params.at(i);
                     assert(param_node->type == NodeTypeParamDecl);
-                    collect_type_decl_deps(g, import, param_node->data.param_decl.type, decl_node);
+                    collect_expr_decl_deps(g, import, param_node->data.param_decl.type, decl_node);
                 }
+                collect_expr_decl_deps(g, import, node->data.fn_proto.return_type, decl_node);
 
                 Buf *name = &node->data.fn_proto.name;
                 decl_node->name = name;
@@ -3315,7 +3312,6 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
             break;
         case NodeTypeDirective:
         case NodeTypeParamDecl:
-        case NodeTypeType:
         case NodeTypeFnDecl:
         case NodeTypeReturnExpr:
         case NodeTypeRoot:
@@ -3327,8 +3323,6 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
         case NodeTypeNumberLiteral:
         case NodeTypeStringLiteral:
         case NodeTypeCharLiteral:
-        case NodeTypeUnreachable:
-        case NodeTypeVoid:
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeSymbol:
@@ -3344,10 +3338,9 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
         case NodeTypeAsmExpr:
         case NodeTypeFieldAccessExpr:
         case NodeTypeStructField:
-        case NodeTypeStructValueExpr:
+        case NodeTypeContainerInitExpr:
         case NodeTypeStructValueField:
-        case NodeTypeCompilerFnExpr:
-        case NodeTypeCompilerFnType:
+        case NodeTypeArrayType:
             zig_unreachable();
     }
 }
@@ -3526,18 +3519,14 @@ Expr *get_resolved_expr(AstNode *node) {
             return &node->data.while_expr.resolved_expr;
         case NodeTypeAsmExpr:
             return &node->data.asm_expr.resolved_expr;
-        case NodeTypeStructValueExpr:
-            return &node->data.struct_val_expr.resolved_expr;
+        case NodeTypeContainerInitExpr:
+            return &node->data.container_init_expr.resolved_expr;
         case NodeTypeNumberLiteral:
             return &node->data.number_literal.resolved_expr;
         case NodeTypeStringLiteral:
             return &node->data.string_literal.resolved_expr;
         case NodeTypeBlock:
             return &node->data.block.resolved_expr;
-        case NodeTypeVoid:
-            return &node->data.void_expr.resolved_expr;
-        case NodeTypeUnreachable:
-            return &node->data.unreachable_expr.resolved_expr;
         case NodeTypeSymbol:
             return &node->data.symbol_expr.resolved_expr;
         case NodeTypeVariableDeclaration:
@@ -3554,19 +3543,16 @@ Expr *get_resolved_expr(AstNode *node) {
             return &node->data.break_expr.resolved_expr;
         case NodeTypeContinue:
             return &node->data.continue_expr.resolved_expr;
-        case NodeTypeCompilerFnExpr:
-            return &node->data.compiler_fn_expr.resolved_expr;
-        case NodeTypeCompilerFnType:
-            return &node->data.compiler_fn_type.resolved_expr;
         case NodeTypeLabel:
             return &node->data.label.resolved_expr;
+        case NodeTypeArrayType:
+            return &node->data.array_type.resolved_expr;
         case NodeTypeRoot:
         case NodeTypeRootExportDecl:
         case NodeTypeFnProto:
         case NodeTypeFnDef:
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
-        case NodeTypeType:
         case NodeTypeExternBlock:
         case NodeTypeDirective:
         case NodeTypeUse:
@@ -3581,13 +3567,12 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) {
     switch (node->type) {
         case NodeTypeNumberLiteral:
             return &node->data.number_literal.codegen;
-        case NodeTypeCompilerFnType:
-            return &node->data.compiler_fn_type.resolved_num_lit;
+        case NodeTypeFnCallExpr:
+            return &node->data.fn_call_expr.resolved_num_lit;
         case NodeTypeReturnExpr:
         case NodeTypeBinOpExpr:
         case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
-        case NodeTypeFnCallExpr:
         case NodeTypeArrayAccessExpr:
         case NodeTypeSliceExpr:
         case NodeTypeFieldAccessExpr:
@@ -3595,24 +3580,21 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) {
         case NodeTypeIfVarExpr:
         case NodeTypeWhileExpr:
         case NodeTypeAsmExpr:
-        case NodeTypeStructValueExpr:
+        case NodeTypeContainerInitExpr:
         case NodeTypeRoot:
         case NodeTypeRootExportDecl:
         case NodeTypeFnProto:
         case NodeTypeFnDef:
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
-        case NodeTypeType:
         case NodeTypeBlock:
         case NodeTypeExternBlock:
         case NodeTypeDirective:
         case NodeTypeVariableDeclaration:
         case NodeTypeStringLiteral:
         case NodeTypeCharLiteral:
-        case NodeTypeUnreachable:
         case NodeTypeSymbol:
         case NodeTypeUse:
-        case NodeTypeVoid:
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeLabel:
@@ -3622,7 +3604,7 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) {
         case NodeTypeStructDecl:
         case NodeTypeStructField:
         case NodeTypeStructValueField:
-        case NodeTypeCompilerFnExpr:
+        case NodeTypeArrayType:
             zig_unreachable();
     }
 }
@@ -3648,22 +3630,19 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
         case NodeTypeIfVarExpr:
         case NodeTypeWhileExpr:
         case NodeTypeAsmExpr:
-        case NodeTypeStructValueExpr:
+        case NodeTypeContainerInitExpr:
         case NodeTypeRoot:
         case NodeTypeRootExportDecl:
         case NodeTypeFnDef:
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
-        case NodeTypeType:
         case NodeTypeBlock:
         case NodeTypeExternBlock:
         case NodeTypeDirective:
         case NodeTypeStringLiteral:
         case NodeTypeCharLiteral:
-        case NodeTypeUnreachable:
         case NodeTypeSymbol:
         case NodeTypeUse:
-        case NodeTypeVoid:
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeLabel:
@@ -3672,8 +3651,23 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
         case NodeTypeContinue:
         case NodeTypeStructField:
         case NodeTypeStructValueField:
-        case NodeTypeCompilerFnExpr:
-        case NodeTypeCompilerFnType:
+        case NodeTypeArrayType:
             zig_unreachable();
     }
 }
+
+bool is_node_void_expr(AstNode *node) {
+    if (node->type == NodeTypeContainerInitExpr &&
+        node->data.container_init_expr.kind == ContainerInitKindArray)
+    {
+        AstNode *type_node = node->data.container_init_expr.type;
+        if (type_node->type == NodeTypeSymbol &&
+            buf_eql_str(&type_node->data.symbol_expr.symbol, "void"))
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
src/analyze.hpp
@@ -13,12 +13,13 @@
 void semantic_analyze(CodeGen *g);
 void add_node_error(CodeGen *g, AstNode *node, Buf *msg);
 TypeTableEntry *new_type_table_entry(TypeTableEntryId id);
-TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const, bool is_noalias);
+TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const);
 VariableTableEntry *find_variable(BlockContext *context, Buf *name);
 TypeTableEntry *find_container(BlockContext *context, Buf *name);
 BlockContext *new_block_context(AstNode *node, BlockContext *parent);
 Expr *get_resolved_expr(AstNode *node);
 NumLitCodeGen *get_resolved_num_lit(AstNode *node);
 TopLevelDecl *get_resolved_top_level_decl(AstNode *node);
+bool is_node_void_expr(AstNode *node);
 
 #endif
src/codegen.cpp
@@ -72,35 +72,35 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b
         LLVMValueRef target_ref, LLVMValueRef value,
         TypeTableEntry *op1_type, TypeTableEntry *op2_type);
     
-
-static TypeTableEntry *get_type_for_type_node(CodeGen *g, AstNode *type_node) {
-    assert(type_node->type == NodeTypeType);
-    return type_node->data.type.entry;
+static TypeTableEntry *get_type_for_type_node(AstNode *node) {
+    TypeTableEntry *meta_type_entry = get_resolved_expr(node)->type_entry;
+    assert(meta_type_entry->id == TypeTableEntryIdMetaType);
+    return meta_type_entry->data.meta_type.child_type;
 }
 
 static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_node) {
-    TypeTableEntry *type_entry = get_type_for_type_node(g, type_node);
+    TypeTableEntry *type_entry = get_type_for_type_node(type_node);
 
     if (type_entry->id == TypeTableEntryIdStruct || type_entry->id == TypeTableEntryIdArray) {
-        return get_pointer_to_type(g, type_entry, true, true);
+        return get_pointer_to_type(g, type_entry, true);
     } else {
         return type_entry;
     }
 }
 
 static LLVMZigDIType *to_llvm_debug_type(CodeGen *g, AstNode *type_node) {
-    TypeTableEntry *type_entry = get_type_for_type_node(g, type_node);
+    TypeTableEntry *type_entry = get_type_for_type_node(type_node);
     return type_entry->di_type;
 }
 
 
 static bool type_is_unreachable(CodeGen *g, AstNode *type_node) {
-    return get_type_for_type_node(g, type_node)->id == TypeTableEntryIdUnreachable;
+    return get_type_for_type_node(type_node)->id == TypeTableEntryIdUnreachable;
 }
 
 static bool is_param_decl_type_void(CodeGen *g, AstNode *param_decl_node) {
     assert(param_decl_node->type == NodeTypeParamDecl);
-    return get_type_for_type_node(g, param_decl_node->data.param_decl.type)->id == TypeTableEntryIdVoid;
+    return get_type_for_type_node(param_decl_node->data.param_decl.type)->id == TypeTableEntryIdVoid;
 }
 
 static int count_non_void_params(CodeGen *g, ZigList<AstNode *> *params) {
@@ -146,6 +146,32 @@ static TypeTableEntry *get_expr_type(AstNode *node) {
     return expr->type_entry;
 }
 
+static LLVMValueRef gen_number_literal_raw(CodeGen *g, AstNode *source_node,
+        NumLitCodeGen *codegen_num_lit, AstNodeNumberLiteral *num_lit_node)
+{
+    TypeTableEntry *type_entry = codegen_num_lit->resolved_type;
+    assert(type_entry);
+
+    // override the expression type for number literals
+    get_resolved_expr(source_node)->type_entry = type_entry;
+
+    if (type_entry->id == TypeTableEntryIdInt) {
+        // here the union has int64_t and uint64_t and we purposefully read
+        // the uint64_t value in either case, because we want the twos
+        // complement representation
+
+        return LLVMConstInt(type_entry->type_ref,
+                num_lit_node->data.x_uint,
+                type_entry->data.integral.is_signed);
+    } else if (type_entry->id == TypeTableEntryIdFloat) {
+
+        return LLVMConstReal(type_entry->type_ref,
+                num_lit_node->data.x_float);
+    } else {
+        zig_panic("bad number literal type");
+    }
+}
+
 static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeFnCallExpr);
     AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
@@ -154,6 +180,7 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
 
     switch (builtin_fn->id) {
         case BuiltinFnIdInvalid:
+        case BuiltinFnIdTypeof:
             zig_unreachable();
         case BuiltinFnIdArithmeticWithOverflow:
             {
@@ -238,6 +265,74 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
                 LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 5, "");
                 return nullptr;
             }
+        case BuiltinFnIdSizeof:
+            {
+                assert(node->data.fn_call_expr.params.length == 1);
+                AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                TypeTableEntry *type_entry = get_type_for_type_node(type_node);
+
+                NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
+                AstNodeNumberLiteral num_lit_node;
+                num_lit_node.kind = NumLitU64; // this field isn't even read
+                num_lit_node.overflow = false;
+                num_lit_node.data.x_uint = type_entry->size_in_bits / 8;
+                return gen_number_literal_raw(g, node, codegen_num_lit, &num_lit_node);
+            }
+        case BuiltinFnIdMinValue:
+            {
+                assert(node->data.fn_call_expr.params.length == 1);
+                AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                TypeTableEntry *type_entry = get_type_for_type_node(type_node);
+
+
+                if (type_entry->id == TypeTableEntryIdInt) {
+                    if (type_entry->data.integral.is_signed) {
+                        return LLVMConstInt(type_entry->type_ref, 1ULL << (type_entry->size_in_bits - 1), false);
+                    } else {
+                        return LLVMConstNull(type_entry->type_ref);
+                    }
+                } else if (type_entry->id == TypeTableEntryIdFloat) {
+                    zig_panic("TODO codegen min_value float");
+                } else {
+                    zig_unreachable();
+                }
+            }
+        case BuiltinFnIdMaxValue:
+            {
+                assert(node->data.fn_call_expr.params.length == 1);
+                AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                TypeTableEntry *type_entry = get_type_for_type_node(type_node);
+
+
+                if (type_entry->id == TypeTableEntryIdInt) {
+                    if (type_entry->data.integral.is_signed) {
+                        return LLVMConstInt(type_entry->type_ref, (1ULL << (type_entry->size_in_bits - 1)) - 1, false);
+                    } else {
+                        return LLVMConstAllOnes(type_entry->type_ref);
+                    }
+                } else if (type_entry->id == TypeTableEntryIdFloat) {
+                    zig_panic("TODO codegen max_value float");
+                } else {
+                    zig_unreachable();
+                }
+            }
+        case BuiltinFnIdValueCount:
+            {
+                assert(node->data.fn_call_expr.params.length == 1);
+                AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                TypeTableEntry *type_entry = get_type_for_type_node(type_node);
+
+                if (type_entry->id == TypeTableEntryIdEnum) {
+                    NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
+                    AstNodeNumberLiteral num_lit_node;
+                    num_lit_node.kind = NumLitU64; // field ignored
+                    num_lit_node.overflow = false;
+                    num_lit_node.data.x_uint = type_entry->data.enumeration.field_count;
+                    return gen_number_literal_raw(g, node, codegen_num_lit, &num_lit_node);
+                } else {
+                    zig_unreachable();
+                }
+            }
     }
     zig_unreachable();
 }
@@ -692,6 +787,10 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
                 add_debug_source_node(g, node);
                 return LLVMBuildLoad(g->builder, expr, "");
             }
+        case PrefixOpMaybe:
+            {
+                zig_panic("TODO codegen PrefixOpMaybe");
+            }
     }
     zig_unreachable();
 }
@@ -1484,35 +1583,46 @@ static LLVMValueRef gen_null_literal(CodeGen *g, AstNode *node) {
     return tmp_struct_ptr;
 }
 
-static LLVMValueRef gen_struct_val_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeStructValueExpr);
+static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeContainerInitExpr);
 
     TypeTableEntry *type_entry = get_expr_type(node);
 
-    assert(type_entry->id == TypeTableEntryIdStruct);
+    if (type_entry->id == TypeTableEntryIdStruct) {
+        assert(node->data.container_init_expr.kind == ContainerInitKindStruct);
 
-    int field_count = type_entry->data.structure.field_count;
-    assert(field_count == node->data.struct_val_expr.fields.length);
+        int field_count = type_entry->data.structure.field_count;
+        assert(field_count == node->data.container_init_expr.entries.length);
 
-    StructValExprCodeGen *struct_val_expr_node = &node->data.struct_val_expr.codegen;
-    LLVMValueRef tmp_struct_ptr = struct_val_expr_node->ptr;
+        StructValExprCodeGen *struct_val_expr_node = &node->data.container_init_expr.resolved_struct_val_expr;
+        LLVMValueRef tmp_struct_ptr = struct_val_expr_node->ptr;
 
-    for (int i = 0; i < field_count; i += 1) {
-        AstNode *field_node = node->data.struct_val_expr.fields.at(i);
-        assert(field_node->type == NodeTypeStructValueField);
-        TypeStructField *type_struct_field = field_node->data.struct_val_field.type_struct_field;
-        if (type_struct_field->type_entry->id == TypeTableEntryIdVoid) {
-            continue;
+        for (int i = 0; i < field_count; i += 1) {
+            AstNode *field_node = node->data.container_init_expr.entries.at(i);
+            assert(field_node->type == NodeTypeStructValueField);
+            TypeStructField *type_struct_field = field_node->data.struct_val_field.type_struct_field;
+            if (type_struct_field->type_entry->id == TypeTableEntryIdVoid) {
+                continue;
+            }
+            assert(buf_eql_buf(type_struct_field->name, &field_node->data.struct_val_field.name));
+
+            add_debug_source_node(g, field_node);
+            LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, type_struct_field->gen_index, "");
+            LLVMValueRef value = gen_expr(g, field_node->data.struct_val_field.expr);
+            LLVMBuildStore(g->builder, value, field_ptr);
         }
-        assert(buf_eql_buf(type_struct_field->name, &field_node->data.struct_val_field.name));
 
-        add_debug_source_node(g, field_node);
-        LLVMValueRef field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, type_struct_field->gen_index, "");
-        LLVMValueRef value = gen_expr(g, field_node->data.struct_val_field.expr);
-        LLVMBuildStore(g->builder, value, field_ptr);
+        return tmp_struct_ptr;
+    } else if (type_entry->id == TypeTableEntryIdUnreachable) {
+        assert(node->data.container_init_expr.entries.length == 0);
+        add_debug_source_node(g, node);
+        return LLVMBuildUnreachable(g->builder);
+    } else if (type_entry->id == TypeTableEntryIdVoid) {
+        assert(node->data.container_init_expr.entries.length == 0);
+        return nullptr;
+    } else {
+        zig_unreachable();
     }
-
-    return tmp_struct_ptr;
 }
 
 static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) {
@@ -1659,93 +1769,39 @@ static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
             get_resolved_expr(node)->block_context, false, &init_val);
 }
 
-static LLVMValueRef gen_number_literal_raw(CodeGen *g, AstNode *source_node,
-        NumLitCodeGen *codegen_num_lit, AstNodeNumberLiteral *num_lit_node)
-{
-    TypeTableEntry *type_entry = codegen_num_lit->resolved_type;
-    assert(type_entry);
-
-    // override the expression type for number literals
-    get_resolved_expr(source_node)->type_entry = type_entry;
-
-    if (type_entry->id == TypeTableEntryIdInt) {
-        // here the union has int64_t and uint64_t and we purposefully read
-        // the uint64_t value in either case, because we want the twos
-        // complement representation
+static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeNumberLiteral);
 
-        return LLVMConstInt(type_entry->type_ref,
-                num_lit_node->data.x_uint,
-                type_entry->data.integral.is_signed);
-    } else if (type_entry->id == TypeTableEntryIdFloat) {
+    NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
+    assert(codegen_num_lit);
 
-        return LLVMConstReal(type_entry->type_ref,
-                num_lit_node->data.x_float);
-    } else {
-        zig_panic("bad number literal type");
-    }
+    return gen_number_literal_raw(g, node, codegen_num_lit, &node->data.number_literal);
 }
 
-static LLVMValueRef gen_compiler_fn_type(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeCompilerFnType);
-
-    Buf *name = &node->data.compiler_fn_type.name;
-    TypeTableEntry *type_entry = get_type_for_type_node(g, node->data.compiler_fn_type.type);
-    if (buf_eql_str(name, "sizeof")) {
-        NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
-        AstNodeNumberLiteral num_lit_node;
-        num_lit_node.kind = type_entry->data.num_lit.kind;
-        num_lit_node.overflow = false;
-        num_lit_node.data.x_uint = type_entry->size_in_bits / 8;
-        return gen_number_literal_raw(g, node, codegen_num_lit, &num_lit_node);
-    } else if (buf_eql_str(name, "min_value")) {
-        if (type_entry->id == TypeTableEntryIdInt) {
-            if (type_entry->data.integral.is_signed) {
-                return LLVMConstInt(type_entry->type_ref, 1ULL << (type_entry->size_in_bits - 1), false);
-            } else {
-                return LLVMConstNull(type_entry->type_ref);
-            }
-        } else if (type_entry->id == TypeTableEntryIdFloat) {
-            zig_panic("TODO codegen min_value float");
-        } else {
-            zig_unreachable();
-        }
-    } else if (buf_eql_str(name, "max_value")) {
-        if (type_entry->id == TypeTableEntryIdInt) {
-            if (type_entry->data.integral.is_signed) {
-                return LLVMConstInt(type_entry->type_ref, (1ULL << (type_entry->size_in_bits - 1)) - 1, false);
-            } else {
-                return LLVMConstAllOnes(type_entry->type_ref);
-            }
-        } else if (type_entry->id == TypeTableEntryIdFloat) {
-            zig_panic("TODO codegen max_value float");
-        } else {
-            zig_unreachable();
-        }
-    } else if (buf_eql_str(name, "value_count")) {
-        if (type_entry->id == TypeTableEntryIdEnum) {
-            NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
-            AstNodeNumberLiteral num_lit_node;
-            num_lit_node.kind = type_entry->data.num_lit.kind;
-            num_lit_node.overflow = false;
-            num_lit_node.data.x_uint = type_entry->data.enumeration.field_count;
-            return gen_number_literal_raw(g, node, codegen_num_lit, &num_lit_node);
+static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
+    VariableTableEntry *variable = find_variable(
+            get_resolved_expr(node)->block_context,
+            &node->data.symbol_expr.symbol);
+    assert(variable);
+    if (variable->type->id == TypeTableEntryIdVoid) {
+        return nullptr;
+    } else if (variable->is_ptr) {
+        assert(variable->value_ref);
+        if (variable->type->id == TypeTableEntryIdArray) {
+            return variable->value_ref;
+        } else if (variable->type->id == TypeTableEntryIdStruct ||
+                    variable->type->id == TypeTableEntryIdMaybe)
+        {
+            return variable->value_ref;
         } else {
-            zig_unreachable();
+            add_debug_source_node(g, node);
+            return LLVMBuildLoad(g->builder, variable->value_ref, "");
         }
     } else {
-        zig_unreachable();
+        return variable->value_ref;
     }
 }
 
-static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeNumberLiteral);
-
-    NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
-    assert(codegen_num_lit);
-
-    return gen_number_literal_raw(g, node, codegen_num_lit, &node->data.number_literal);
-}
-
 static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
     switch (node->type) {
         case NodeTypeBinOpExpr:
@@ -1766,11 +1822,6 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
             return gen_slice_expr(g, node);
         case NodeTypeFieldAccessExpr:
             return gen_field_access_expr(g, node, false);
-        case NodeTypeUnreachable:
-            add_debug_source_node(g, node);
-            return LLVMBuildUnreachable(g->builder);
-        case NodeTypeVoid:
-            return nullptr;
         case NodeTypeBoolLiteral:
             if (node->data.bool_literal.value)
                 return LLVMConstAllOnes(LLVMInt1Type());
@@ -1802,29 +1853,7 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
         case NodeTypeCharLiteral:
             return LLVMConstInt(LLVMInt8Type(), node->data.char_literal.value, false);
         case NodeTypeSymbol:
-            {
-                VariableTableEntry *variable = find_variable(
-                        get_resolved_expr(node)->block_context,
-                        &node->data.symbol_expr.symbol);
-                assert(variable);
-                if (variable->type->id == TypeTableEntryIdVoid) {
-                    return nullptr;
-                } else if (variable->is_ptr) {
-                    assert(variable->value_ref);
-                    if (variable->type->id == TypeTableEntryIdArray) {
-                        return variable->value_ref;
-                    } else if (variable->type->id == TypeTableEntryIdStruct ||
-                               variable->type->id == TypeTableEntryIdMaybe)
-                    {
-                        return variable->value_ref;
-                    } else {
-                        add_debug_source_node(g, node);
-                        return LLVMBuildLoad(g->builder, variable->value_ref, "");
-                    }
-                } else {
-                    return variable->value_ref;
-                }
-            }
+            return gen_symbol(g, node);
         case NodeTypeBlock:
             return gen_block(g, node, nullptr);
         case NodeTypeGoto:
@@ -1846,24 +1875,21 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
                 LLVMPositionBuilderAtEnd(g->builder, basic_block);
                 return nullptr;
             }
-        case NodeTypeStructValueExpr:
-            return gen_struct_val_expr(g, node);
-        case NodeTypeCompilerFnType:
-            return gen_compiler_fn_type(g, node);
+        case NodeTypeContainerInitExpr:
+            return gen_container_init_expr(g, node);
         case NodeTypeRoot:
         case NodeTypeRootExportDecl:
         case NodeTypeFnProto:
         case NodeTypeFnDef:
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
-        case NodeTypeType:
         case NodeTypeExternBlock:
         case NodeTypeDirective:
         case NodeTypeUse:
         case NodeTypeStructDecl:
         case NodeTypeStructField:
         case NodeTypeStructValueField:
-        case NodeTypeCompilerFnExpr:
+        case NodeTypeArrayType:
             zig_unreachable();
     }
     zig_unreachable();
@@ -1872,7 +1898,7 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
 static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
     LLVMValueRef val = gen_expr_no_cast(g, node);
 
-    if (node->type == NodeTypeVoid) {
+    if (is_node_void_expr(node)) {
         return val;
     }
 
@@ -1966,7 +1992,7 @@ static void do_code_gen(CodeGen *g) {
         assert(proto_node->type == NodeTypeFnProto);
         AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
 
-        LLVMTypeRef ret_type = get_type_for_type_node(g, fn_proto->return_type)->type_ref;
+        LLVMTypeRef ret_type = get_type_for_type_node(fn_proto->return_type)->type_ref;
         int param_count = count_non_void_params(g, &fn_proto->params);
         LLVMTypeRef *param_types = allocate<LLVMTypeRef>(param_count);
         int gen_param_index = 0;
@@ -2009,7 +2035,7 @@ static void do_code_gen(CodeGen *g) {
             TypeTableEntry *param_type = fn_proto_type_from_type_node(g, type_node);
             LLVMValueRef argument_val = LLVMGetParam(fn, gen_param_index);
             if (param_type->id == TypeTableEntryIdPointer &&
-                param_type->data.pointer.is_noalias)
+                false) // TODO test if parameter is noalias
             {
                 LLVMAddAttribute(argument_val, LLVMNoAliasAttribute);
             } else if (param_type->id == TypeTableEntryIdPointer &&
@@ -2267,7 +2293,7 @@ static void define_builtin_types(CodeGen *g) {
         g->builtin_types.entry_u64 = entry;
         g->primitive_type_table.put(&entry->name, entry);
     }
-    g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true, false);
+    g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
         entry->type_ref = LLVMInt8Type();
@@ -2413,7 +2439,7 @@ static void define_builtin_fns_int(CodeGen *g, TypeTableEntry *type_entry) {
         builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
         builtin_fn->param_types[0] = type_entry;
         builtin_fn->param_types[1] = type_entry;
-        builtin_fn->param_types[2] = get_pointer_to_type(g, type_entry, false, false);
+        builtin_fn->param_types[2] = get_pointer_to_type(g, type_entry, false);
 
 
         const char *signed_str = type_entry->data.integral.is_signed ?
@@ -2437,6 +2463,23 @@ static void define_builtin_fns_int(CodeGen *g, TypeTableEntry *type_entry) {
     }
 }
 
+static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name) {
+    BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
+    buf_init_from_str(&builtin_fn->name, name);
+    builtin_fn->id = id;
+    g->builtin_fn_table.put(&builtin_fn->name, builtin_fn);
+    return builtin_fn;
+}
+
+static BuiltinFnEntry *create_one_arg_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name) {
+    BuiltinFnEntry *builtin_fn = create_builtin_fn(g, id, name);
+    builtin_fn->return_type = nullptr; // manually determined later
+    builtin_fn->param_count = 1;
+    builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
+    builtin_fn->param_types[0] = nullptr; // manually checked later
+    return builtin_fn;
+}
+
 static void define_builtin_fns(CodeGen *g) {
     define_builtin_fns_int(g, g->builtin_types.entry_u8);
     define_builtin_fns_int(g, g->builtin_types.entry_u16);
@@ -2447,9 +2490,7 @@ static void define_builtin_fns(CodeGen *g) {
     define_builtin_fns_int(g, g->builtin_types.entry_i32);
     define_builtin_fns_int(g, g->builtin_types.entry_i64);
     {
-        BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
-        buf_init_from_str(&builtin_fn->name, "memcpy");
-        builtin_fn->id = BuiltinFnIdMemcpy;
+        BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy");
         builtin_fn->return_type = g->builtin_types.entry_void;
         builtin_fn->param_count = 3;
         builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
@@ -2470,12 +2511,9 @@ static void define_builtin_fns(CodeGen *g) {
         assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
 
         g->memcpy_fn_val = builtin_fn->fn_val;
-        g->builtin_fn_table.put(&builtin_fn->name, builtin_fn);
     }
     {
-        BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1);
-        buf_init_from_str(&builtin_fn->name, "memset");
-        builtin_fn->id = BuiltinFnIdMemset;
+        BuiltinFnEntry *builtin_fn = create_builtin_fn(g, BuiltinFnIdMemset, "memset");
         builtin_fn->return_type = g->builtin_types.entry_void;
         builtin_fn->param_count = 3;
         builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count);
@@ -2496,8 +2534,12 @@ static void define_builtin_fns(CodeGen *g) {
         assert(LLVMGetIntrinsicID(builtin_fn->fn_val));
 
         g->memset_fn_val = builtin_fn->fn_val;
-        g->builtin_fn_table.put(&builtin_fn->name, builtin_fn);
     }
+    create_one_arg_builtin_fn(g, BuiltinFnIdSizeof, "sizeof");
+    create_one_arg_builtin_fn(g, BuiltinFnIdMaxValue, "max_value");
+    create_one_arg_builtin_fn(g, BuiltinFnIdMinValue, "min_value");
+    create_one_arg_builtin_fn(g, BuiltinFnIdValueCount, "value_count");
+    create_one_arg_builtin_fn(g, BuiltinFnIdTypeof, "typeof");
 }
 
 
@@ -2780,9 +2822,8 @@ void codegen_add_root_code(CodeGen *g, Buf *src_dir, Buf *src_basename, Buf *sou
 }
 
 static void to_c_type(CodeGen *g, AstNode *type_node, Buf *out_buf) {
-    assert(type_node->type == NodeTypeType);
-
-    TypeTableEntry *type_entry = type_node->data.type.entry;
+    zig_panic("TODO this function needs some love");
+    TypeTableEntry *type_entry = get_resolved_expr(type_node)->type_entry;
     assert(type_entry);
 
     if (type_entry == g->builtin_types.entry_u8) {
src/parser.cpp
@@ -62,6 +62,7 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
         case PrefixOpAddressOf: return "&";
         case PrefixOpConstAddressOf: return "&const";
         case PrefixOpDereference: return "*";
+        case PrefixOpMaybe: return "?";
     }
     zig_unreachable();
 }
@@ -80,8 +81,6 @@ const char *node_type_str(NodeType node_type) {
             return "FnProto";
         case NodeTypeParamDecl:
             return "ParamDecl";
-        case NodeTypeType:
-            return "Type";
         case NodeTypeBlock:
             return "Block";
         case NodeTypeBinOpExpr:
@@ -108,16 +107,12 @@ const char *node_type_str(NodeType node_type) {
             return "StringLiteral";
         case NodeTypeCharLiteral:
             return "CharLiteral";
-        case NodeTypeUnreachable:
-            return "Unreachable";
         case NodeTypeSymbol:
             return "Symbol";
         case NodeTypePrefixOpExpr:
             return "PrefixOpExpr";
         case NodeTypeUse:
             return "Use";
-        case NodeTypeVoid:
-            return "Void";
         case NodeTypeBoolLiteral:
             return "BoolLiteral";
         case NodeTypeNullLiteral:
@@ -144,14 +139,12 @@ const char *node_type_str(NodeType node_type) {
             return "StructDecl";
         case NodeTypeStructField:
             return "StructField";
-        case NodeTypeStructValueExpr:
-            return "StructValueExpr";
         case NodeTypeStructValueField:
             return "StructValueField";
-        case NodeTypeCompilerFnExpr:
-            return "CompilerFnExpr";
-        case NodeTypeCompilerFnType:
-            return "CompilerFnType";
+        case NodeTypeContainerInitExpr:
+            return "ContainerInitExpr";
+        case NodeTypeArrayType:
+            return "ArrayType";
     }
     zig_unreachable();
 }
@@ -214,47 +207,6 @@ void ast_print(AstNode *node, int indent) {
 
                 break;
             }
-        case NodeTypeType:
-            switch (node->data.type.type) {
-                case AstNodeTypeTypePrimitive:
-                    {
-                        Buf *name_buf = &node->data.type.primitive_name;
-                        fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(name_buf));
-                        break;
-                    }
-                case AstNodeTypeTypePointer:
-                    {
-                        const char *const_or_mut_str = node->data.type.is_const ? "const " : "";
-                        const char *noalias_or_not_str = node->data.type.is_noalias ? "noalias " : "";
-                        fprintf(stderr, "%s%s PointerType\n", const_or_mut_str, noalias_or_not_str);
-
-                        ast_print(node->data.type.child_type, indent + 2);
-                        break;
-                    }
-                case AstNodeTypeTypeArray:
-                    {
-                        const char *const_or_mut_str = node->data.type.is_const ? "const " : "";
-                        const char *noalias_or_not_str = node->data.type.is_noalias ? "noalias " : "";
-                        fprintf(stderr, "%s%s ArrayType\n", const_or_mut_str, noalias_or_not_str);
-                        if (node->data.type.array_size)
-                            ast_print(node->data.type.array_size, indent + 2);
-                        ast_print(node->data.type.child_type, indent + 2);
-                        break;
-                    }
-                case AstNodeTypeTypeMaybe:
-                    {
-                        fprintf(stderr, "MaybeType\n");
-                        ast_print(node->data.type.child_type, indent + 2);
-                        break;
-                    }
-                case AstNodeTypeTypeCompilerExpr:
-                    {
-                        fprintf(stderr, "CompilerExprType\n");
-                        ast_print(node->data.type.compiler_expr, indent + 2);
-                        break;
-                    }
-            }
-            break;
         case NodeTypeReturnExpr:
             fprintf(stderr, "%s\n", node_type_str(node->type));
             if (node->data.return_expr.expr)
@@ -348,18 +300,12 @@ void ast_print(AstNode *node, int indent) {
                 fprintf(stderr, "%s '%c'\n", node_type_str(node->type), node->data.char_literal.value);
                 break;
             }
-        case NodeTypeUnreachable:
-            fprintf(stderr, "Unreachable\n");
-            break;
         case NodeTypeSymbol:
             fprintf(stderr, "Symbol %s\n", buf_ptr(&node->data.symbol_expr.symbol));
             break;
         case NodeTypeUse:
             fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path));
             break;
-        case NodeTypeVoid:
-            fprintf(stderr, "%s\n", node_type_str(node->type));
-            break;
         case NodeTypeBoolLiteral:
             fprintf(stderr, "%s '%s'\n", node_type_str(node->type),
                     node->data.bool_literal.value ? "true" : "false");
@@ -431,24 +377,28 @@ void ast_print(AstNode *node, int indent) {
                 ast_print(node->data.struct_field.type, indent + 2);
             }
             break;
-        case NodeTypeStructValueExpr:
-            fprintf(stderr, "%s\n", node_type_str(node->type));
-            ast_print(node->data.struct_val_expr.type, indent + 2);
-            for (int i = 0; i < node->data.struct_val_expr.fields.length; i += 1) {
-                AstNode *child = node->data.struct_val_expr.fields.at(i);
-                ast_print(child, indent + 2);
-            }
-            break;
         case NodeTypeStructValueField:
             fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.struct_val_field.name));
             ast_print(node->data.struct_val_field.expr, indent + 2);
             break;
-        case NodeTypeCompilerFnExpr:
-            fprintf(stderr, "%s\n", node_type_str(node->type));
-            break;
-        case NodeTypeCompilerFnType:
+        case NodeTypeContainerInitExpr:
             fprintf(stderr, "%s\n", node_type_str(node->type));
+            ast_print(node->data.container_init_expr.type, indent + 2);
+            for (int i = 0; i < node->data.container_init_expr.entries.length; i += 1) {
+                AstNode *child = node->data.container_init_expr.entries.at(i);
+                ast_print(child, indent + 2);
+            }
             break;
+        case NodeTypeArrayType:
+            {
+                const char *const_str = node->data.array_type.is_const ? "const" : "var";
+                fprintf(stderr, "%s %s\n", node_type_str(node->type), const_str);
+                if (node->data.array_type.size) {
+                    ast_print(node->data.array_type.size, indent + 2);
+                }
+                ast_print(node->data.array_type.child_type, indent + 2);
+                break;
+            }
     }
 }
 
@@ -539,9 +489,8 @@ static AstNode *ast_create_node_with_node(ParseContext *pc, NodeType type, AstNo
 }
 
 static AstNode *ast_create_void_type_node(ParseContext *pc, Token *token) {
-    AstNode *node = ast_create_node(pc, NodeTypeType, token);
-    node->data.type.type = AstNodeTypeTypePrimitive;
-    buf_init_from_str(&node->data.type.primitive_name, "void");
+    AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
+    buf_init_from_str(&node->data.symbol_expr.symbol, "void");
     return node;
 }
 
@@ -961,7 +910,7 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma
 static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory);
 static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory);
 static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory);
-static AstNode *ast_parse_type(ParseContext *pc, int *token_index);
+static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, bool mandatory);
 
 static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
     if (token->id != token_id) {
@@ -1022,172 +971,42 @@ static void ast_parse_directives(ParseContext *pc, int *token_index,
     zig_unreachable();
 }
 
-static void ast_parse_type_assume_amp(ParseContext *pc, int *token_index, AstNode *node) {
-    node->data.type.type = AstNodeTypeTypePointer;
-    Token *first_type_token = &pc->tokens->at(*token_index);
-    if (first_type_token->id == TokenIdKeywordConst) {
-        node->data.type.is_const = true;
-        *token_index += 1;
-        first_type_token = &pc->tokens->at(*token_index);
-        if (first_type_token->id == TokenIdKeywordNoAlias) {
-            node->data.type.is_noalias = true;
-            *token_index += 1;
-        }
-    } else if (first_type_token->id == TokenIdKeywordNoAlias) {
-        node->data.type.is_noalias = true;
-        *token_index += 1;
-    }
-
-    node->data.type.child_type = ast_parse_type(pc, token_index);
-}
-
 /*
-CompilerFnType : token(NumberSign) token(Symbol) token(LParen) Expression token(RParen)
+ParamDecl : option(token(NoAlias)) token(Symbol) token(Colon) UnwrapMaybeExpression | token(Ellipsis)
 */
-static AstNode *ast_parse_compiler_fn_type(ParseContext *pc, int *token_index, bool mandatory) {
-    Token *token = &pc->tokens->at(*token_index);
+static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) {
+    Token *first_token = &pc->tokens->at(*token_index);
 
-    if (token->id == TokenIdNumberSign) {
+    if (first_token->id == TokenIdEllipsis) {
         *token_index += 1;
-    } else if (mandatory) {
-        ast_invalid_token_error(pc, token);
-    } else {
         return nullptr;
     }
 
-    Token *name_symbol = ast_eat_token(pc, token_index, TokenIdSymbol);
-    ast_eat_token(pc, token_index, TokenIdLParen);
-
-    AstNode *node = ast_create_node(pc, NodeTypeCompilerFnType, token);
-    ast_buf_from_token(pc, name_symbol, &node->data.compiler_fn_type.name);
-    node->data.compiler_fn_type.type = ast_parse_type(pc, token_index);
-
-    ast_eat_token(pc, token_index, TokenIdRParen);
-    return node;
-}
+    AstNode *node = ast_create_node(pc, NodeTypeParamDecl, first_token);
+    Token *name_token;
 
-/*
-CompilerFnExpr : token(NumberSign) token(Symbol) token(LParen) Expression token(RParen)
-*/
-static AstNode *ast_parse_compiler_fn_call(ParseContext *pc, int *token_index, bool mandatory) {
-    Token *token = &pc->tokens->at(*token_index);
-
-    if (token->id == TokenIdNumberSign) {
+    if (first_token->id == TokenIdKeywordNoAlias) {
+        node->data.param_decl.is_noalias = true;
+        *token_index += 1;
+        name_token = ast_eat_token(pc, token_index, TokenIdSymbol);
+    } else if (first_token->id == TokenIdSymbol) {
+        name_token = first_token;
         *token_index += 1;
-    } else if (mandatory) {
-        ast_invalid_token_error(pc, token);
     } else {
-        return nullptr;
+        ast_invalid_token_error(pc, first_token);
     }
 
-    Token *name_symbol = ast_eat_token(pc, token_index, TokenIdSymbol);
-    ast_eat_token(pc, token_index, TokenIdLParen);
-
-    AstNode *node = ast_create_node(pc, NodeTypeCompilerFnExpr, token);
-    ast_buf_from_token(pc, name_symbol, &node->data.compiler_fn_expr.name);
-    node->data.compiler_fn_expr.expr = ast_parse_expression(pc, token_index, true);
-
-    ast_eat_token(pc, token_index, TokenIdRParen);
-    return node;
-}
-
-/*
-Type : token(Symbol) | token(Unreachable) | token(Void) | PointerType | ArrayType | MaybeType | CompilerFnExpr
-PointerType : token(Ampersand) option(token(Const)) option(token(NoAlias)) Type
-ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) option(token(NoAlias)) Type
-*/
-static AstNode *ast_parse_type(ParseContext *pc, int *token_index) {
-    Token *token = &pc->tokens->at(*token_index);
-    AstNode *node = ast_create_node(pc, NodeTypeType, token);
-
-    AstNode *compiler_fn_expr = ast_parse_compiler_fn_call(pc, token_index, false);
-    if (compiler_fn_expr) {
-        node->data.type.type = AstNodeTypeTypeCompilerExpr;
-        node->data.type.compiler_expr = compiler_fn_expr;
-        return node;
-    }
+    ast_buf_from_token(pc, name_token, &node->data.param_decl.name);
 
+    Token *colon = &pc->tokens->at(*token_index);
     *token_index += 1;
+    ast_expect_token(pc, colon, TokenIdColon);
 
-    if (token->id == TokenIdKeywordUnreachable) {
-        node->data.type.type = AstNodeTypeTypePrimitive;
-        buf_init_from_str(&node->data.type.primitive_name, "unreachable");
-    } else if (token->id == TokenIdKeywordVoid) {
-        node->data.type.type = AstNodeTypeTypePrimitive;
-        buf_init_from_str(&node->data.type.primitive_name, "void");
-    } else if (token->id == TokenIdSymbol) {
-        node->data.type.type = AstNodeTypeTypePrimitive;
-        ast_buf_from_token(pc, token, &node->data.type.primitive_name);
-    } else if (token->id == TokenIdAmpersand) {
-        ast_parse_type_assume_amp(pc, token_index, node);
-    } else if (token->id == TokenIdMaybe) {
-        node->data.type.type = AstNodeTypeTypeMaybe;
-        node->data.type.child_type = ast_parse_type(pc, token_index);
-    } else if (token->id == TokenIdBoolAnd) {
-        // Pretend that we got 2 ampersand tokens
-        node->data.type.type = AstNodeTypeTypePointer;
-
-        node->data.type.child_type = ast_create_node_no_line_info(pc, NodeTypeType);
-        node->data.type.child_type->line = token->start_line;
-        node->data.type.child_type->column = token->start_column + 1;
-
-        ast_parse_type_assume_amp(pc, token_index, node->data.type.child_type);
-    } else if (token->id == TokenIdLBracket) {
-        node->data.type.type = AstNodeTypeTypeArray;
-
-        node->data.type.array_size = ast_parse_expression(pc, token_index, false);
-
-        ast_eat_token(pc, token_index, TokenIdRBracket);
-
-        Token *const_tok = &pc->tokens->at(*token_index);
-        if (const_tok->id == TokenIdKeywordConst) {
-            *token_index += 1;
-            node->data.type.is_const = true;
-
-            Token *next_tok = &pc->tokens->at(*token_index);
-            if (next_tok->id == TokenIdKeywordNoAlias) {
-                *token_index += 1;
-                node->data.type.is_noalias = true;
-            }
-        } else if (const_tok->id == TokenIdKeywordNoAlias) {
-            *token_index += 1;
-            node->data.type.is_noalias = true;
-        }
-
-        node->data.type.child_type = ast_parse_type(pc, token_index);
-    } else {
-        ast_invalid_token_error(pc, token);
-    }
+    node->data.param_decl.type = ast_parse_unwrap_maybe_expr(pc, token_index, true);
 
     return node;
 }
 
-/*
-ParamDecl : token(Symbol) token(Colon) Type | token(Ellipsis)
-*/
-static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) {
-    Token *param_name = &pc->tokens->at(*token_index);
-    *token_index += 1;
-
-    if (param_name->id == TokenIdSymbol) {
-        AstNode *node = ast_create_node(pc, NodeTypeParamDecl, param_name);
-
-        ast_buf_from_token(pc, param_name, &node->data.param_decl.name);
-
-        Token *colon = &pc->tokens->at(*token_index);
-        *token_index += 1;
-        ast_expect_token(pc, colon, TokenIdColon);
-
-        node->data.param_decl.type = ast_parse_type(pc, token_index);
-
-        return node;
-    } else if (param_name->id == TokenIdEllipsis) {
-        return nullptr;
-    } else {
-        ast_invalid_token_error(pc, param_name);
-    }
-}
-
 
 static void ast_parse_param_decl_list(ParseContext *pc, int *token_index,
         ZigList<AstNode *> *params, bool *is_var_args)
@@ -1274,52 +1093,222 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool
 }
 
 /*
-StructValueExpression : token(Symbol) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace)
-StructValueExpressionField : token(Dot) token(Symbol) token(Eq) Expression
+ArrayType : token(LBracket) option(Expression) token(RBracket) option(token(Const)) Expression
 */
-static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) {
-    Token *first_token = &pc->tokens->at(*token_index);
-    AstNode *node = ast_create_node(pc, NodeTypeStructValueExpr, first_token);
+static AstNode *ast_parse_array_type_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *l_bracket = &pc->tokens->at(*token_index);
+    if (l_bracket->id != TokenIdLBracket) {
+        if (mandatory) {
+            ast_invalid_token_error(pc, l_bracket);
+        } else {
+            return nullptr;
+        }
+    }
 
-    node->data.struct_val_expr.type = ast_parse_type(pc, token_index);
+    *token_index += 1;
 
-    ast_eat_token(pc, token_index, TokenIdLBrace);
+    AstNode *node = ast_create_node(pc, NodeTypeArrayType, l_bracket);
+    node->data.array_type.size = ast_parse_expression(pc, token_index, false);
+
+    ast_eat_token(pc, token_index, TokenIdRBracket);
+
+    Token *const_tok = &pc->tokens->at(*token_index);
+    if (const_tok->id == TokenIdKeywordConst) {
+        *token_index += 1;
+        node->data.array_type.is_const = true;
+    }
+
+    node->data.array_type.child_type = ast_parse_expression(pc, token_index, true);
+
+    return node;
+}
+
+/*
+AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen)
+*/
+static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode *node) {
+    ast_eat_token(pc, token_index, TokenIdLBracket);
+    Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol);
+    ast_eat_token(pc, token_index, TokenIdRBracket);
+
+    Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral);
+
+    ast_eat_token(pc, token_index, TokenIdLParen);
+    AstNode *expr_node = ast_parse_expression(pc, token_index, true);
+    ast_eat_token(pc, token_index, TokenIdRParen);
+
+    AsmInput *asm_input = allocate<AsmInput>(1);
+    ast_buf_from_token(pc, alias, &asm_input->asm_symbolic_name);
+    parse_string_literal(pc, constraint, &asm_input->constraint, nullptr, nullptr);
+    asm_input->expr = expr_node;
+    node->data.asm_expr.input_list.append(asm_input);
+}
+
+/*
+AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Arrow) Expression) token(RParen)
+*/
+static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) {
+    ast_eat_token(pc, token_index, TokenIdLBracket);
+    Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol);
+    ast_eat_token(pc, token_index, TokenIdRBracket);
+
+    Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral);
+
+    AsmOutput *asm_output = allocate<AsmOutput>(1);
+
+    ast_eat_token(pc, token_index, TokenIdLParen);
+
+    Token *token = &pc->tokens->at(*token_index);
+    *token_index += 1;
+    if (token->id == TokenIdSymbol) {
+        ast_buf_from_token(pc, token, &asm_output->variable_name);
+    } else if (token->id == TokenIdArrow) {
+        asm_output->return_type = ast_parse_expression(pc, token_index, true);
+    } else {
+        ast_invalid_token_error(pc, token);
+    }
+
+    ast_eat_token(pc, token_index, TokenIdRParen);
+
+    ast_buf_from_token(pc, alias, &asm_output->asm_symbolic_name);
+    parse_string_literal(pc, constraint, &asm_output->constraint, nullptr, nullptr);
+    node->data.asm_expr.output_list.append(asm_output);
+}
+
+/*
+AsmClobbers: token(Colon) list(token(String), token(Comma))
+*/
+static void ast_parse_asm_clobbers(ParseContext *pc, int *token_index, AstNode *node) {
+    Token *colon_tok = &pc->tokens->at(*token_index);
+
+    if (colon_tok->id != TokenIdColon)
+        return;
+
+    *token_index += 1;
 
     for (;;) {
-        Token *token = &pc->tokens->at(*token_index);
+        Token *string_tok = &pc->tokens->at(*token_index);
+        ast_expect_token(pc, string_tok, TokenIdStringLiteral);
         *token_index += 1;
 
-        if (token->id == TokenIdRBrace) {
-            return node;
-        } else if (token->id == TokenIdDot) {
-            Token *field_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
-            ast_eat_token(pc, token_index, TokenIdEq);
+        Buf *clobber_buf = buf_alloc();
+        parse_string_literal(pc, string_tok, clobber_buf, nullptr, nullptr);
+        node->data.asm_expr.clobber_list.append(clobber_buf);
+
+        Token *comma = &pc->tokens->at(*token_index);
+
+        if (comma->id == TokenIdComma) {
+            *token_index += 1;
+            continue;
+        } else {
+            break;
+        }
+    }
+}
 
-            AstNode *field_node = ast_create_node(pc, NodeTypeStructValueField, token);
+/*
+AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers)
+*/
+static void ast_parse_asm_input(ParseContext *pc, int *token_index, AstNode *node) {
+    Token *colon_tok = &pc->tokens->at(*token_index);
 
-            ast_buf_from_token(pc, field_name_tok, &field_node->data.struct_val_field.name);
-            field_node->data.struct_val_field.expr = ast_parse_expression(pc, token_index, true);
+    if (colon_tok->id != TokenIdColon)
+        return;
 
-            node->data.struct_val_expr.fields.append(field_node);
+    *token_index += 1;
 
-            Token *comma_tok = &pc->tokens->at(*token_index);
-            if (comma_tok->id == TokenIdComma) {
-                *token_index += 1;
-            } else if (comma_tok->id != TokenIdRBrace) {
-                ast_invalid_token_error(pc, comma_tok);
-            } else {
-                *token_index += 1;
-                return node;
-            }
+    for (;;) {
+        ast_parse_asm_input_item(pc, token_index, node);
+
+        Token *comma = &pc->tokens->at(*token_index);
+
+        if (comma->id == TokenIdComma) {
+            *token_index += 1;
+            continue;
         } else {
-            ast_invalid_token_error(pc, token);
+            break;
         }
     }
+
+    ast_parse_asm_clobbers(pc, token_index, node);
 }
 
 /*
-PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | (token(AtSign) token(Symbol) FnCallExpression)
-KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null)
+AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput)
+*/
+static void ast_parse_asm_output(ParseContext *pc, int *token_index, AstNode *node) {
+    Token *colon_tok = &pc->tokens->at(*token_index);
+
+    if (colon_tok->id != TokenIdColon)
+        return;
+
+    *token_index += 1;
+
+    for (;;) {
+        ast_parse_asm_output_item(pc, token_index, node);
+
+        Token *comma = &pc->tokens->at(*token_index);
+
+        if (comma->id == TokenIdComma) {
+            *token_index += 1;
+            continue;
+        } else {
+            break;
+        }
+    }
+
+    ast_parse_asm_input(pc, token_index, node);
+}
+
+/*
+AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen)
+*/
+static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *asm_token = &pc->tokens->at(*token_index);
+
+    if (asm_token->id != TokenIdKeywordAsm) {
+        if (mandatory) {
+            ast_invalid_token_error(pc, asm_token);
+        } else {
+            return nullptr;
+        }
+    }
+
+    AstNode *node = ast_create_node(pc, NodeTypeAsmExpr, asm_token);
+
+    *token_index += 1;
+    Token *lparen_tok = &pc->tokens->at(*token_index);
+
+    if (lparen_tok->id == TokenIdKeywordVolatile) {
+        node->data.asm_expr.is_volatile = true;
+
+        *token_index += 1;
+        lparen_tok = &pc->tokens->at(*token_index);
+    }
+
+    ast_expect_token(pc, lparen_tok, TokenIdLParen);
+    *token_index += 1;
+
+    Token *template_tok = &pc->tokens->at(*token_index);
+    ast_expect_token(pc, template_tok, TokenIdStringLiteral);
+    *token_index += 1;
+
+    parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template, nullptr,
+            &node->data.asm_expr.offset_map);
+    parse_asm_template(pc, node);
+
+    ast_parse_asm_output(pc, token_index, node);
+
+    Token *rparen_tok = &pc->tokens->at(*token_index);
+    ast_expect_token(pc, rparen_tok, TokenIdRParen);
+    *token_index += 1;
+
+    return node;
+}
+
+/*
+PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | token(Symbol) | (token(AtSign) token(Symbol) FnCallExpression) | ArrayType | AsmExpression
+KeywordLiteral : token(True) | token(False) | token(Null) | token(Break) | token(Continue)
 */
 static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
@@ -1339,14 +1328,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         node->data.char_literal.value = parse_char_literal(pc, token);
         *token_index += 1;
         return node;
-    } else if (token->id == TokenIdKeywordUnreachable) {
-        AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token);
-        *token_index += 1;
-        return node;
-    } else if (token->id == TokenIdKeywordVoid) {
-        AstNode *node = ast_create_node(pc, NodeTypeVoid, token);
-        *token_index += 1;
-        return node;
     } else if (token->id == TokenIdKeywordTrue) {
         AstNode *node = ast_create_node(pc, NodeTypeBoolLiteral, token);
         node->data.bool_literal.value = true;
@@ -1357,8 +1338,16 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         node->data.bool_literal.value = false;
         *token_index += 1;
         return node;
-    } else if (token->id == TokenIdKeywordNull) {
-        AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token);
+    } else if (token->id == TokenIdKeywordNull) {
+        AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token);
+        *token_index += 1;
+        return node;
+    } else if (token->id == TokenIdKeywordBreak) {
+        AstNode *node = ast_create_node(pc, NodeTypeBreak, token);
+        *token_index += 1;
+        return node;
+    } else if (token->id == TokenIdKeywordContinue) {
+        AstNode *node = ast_create_node(pc, NodeTypeContinue, token);
         *token_index += 1;
         return node;
     } else if (token->id == TokenIdAtSign) {
@@ -1374,16 +1363,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         node->data.fn_call_expr.is_builtin = true;
         return node;
     } else if (token->id == TokenIdSymbol) {
-        Token *next_token = &pc->tokens->at(*token_index + 1);
-
-        if (next_token->id == TokenIdLBrace) {
-            return ast_parse_struct_val_expr(pc, token_index);
-        } else {
-            *token_index += 1;
-            AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
-            ast_buf_from_token(pc, token, &node->data.symbol_expr.symbol);
-            return node;
-        }
+        *token_index += 1;
+        AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
+        ast_buf_from_token(pc, token, &node->data.symbol_expr.symbol);
+        return node;
     } else if (token->id == TokenIdKeywordGoto) {
         AstNode *node = ast_create_node(pc, NodeTypeGoto, token);
         *token_index += 1;
@@ -1394,14 +1377,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
 
         ast_buf_from_token(pc, dest_symbol, &node->data.goto_expr.name);
         return node;
-    } else if (token->id == TokenIdKeywordBreak) {
-        AstNode *node = ast_create_node(pc, NodeTypeBreak, token);
-        *token_index += 1;
-        return node;
-    } else if (token->id == TokenIdKeywordContinue) {
-        AstNode *node = ast_create_node(pc, NodeTypeContinue, token);
-        *token_index += 1;
-        return node;
     }
 
     AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
@@ -1414,9 +1389,14 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         return block_expr_node;
     }
 
-    AstNode *compiler_fn_type = ast_parse_compiler_fn_type(pc, token_index, false);
-    if (compiler_fn_type) {
-        return compiler_fn_type;
+    AstNode *array_type_node = ast_parse_array_type_expr(pc, token_index, false);
+    if (array_type_node) {
+        return array_type_node;
+    }
+
+    AstNode *asm_expr = ast_parse_asm_expr(pc, token_index, false);
+    if (asm_expr) {
+        return asm_expr;
     }
 
     if (!mandatory)
@@ -1426,11 +1406,14 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
 }
 
 /*
-SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
+SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ContainerInitExpression)
 FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
 ArrayAccessExpression : token(LBracket) Expression token(RBracket)
 SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) token(RBracket) option(token(Const))
 FieldAccessExpression : token(Dot) token(Symbol)
+ContainerInitExpression : token(LBrace) ContainerInitBody token(RBrace)
+ContainerInitBody : list(StructLiteralField, token(Comma)) | list(Expression, token(Comma))
+StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression
 */
 static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, bool mandatory) {
     AstNode *primary_expr = ast_parse_primary_expr(pc, token_index, mandatory);
@@ -1439,16 +1422,16 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo
     }
 
     while (true) {
-        Token *token = &pc->tokens->at(*token_index);
-        if (token->id == TokenIdLParen) {
+        Token *first_token = &pc->tokens->at(*token_index);
+        if (first_token->id == TokenIdLParen) {
             *token_index += 1;
 
-            AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token);
+            AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, first_token);
             node->data.fn_call_expr.fn_ref_expr = primary_expr;
             ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params);
 
             primary_expr = node;
-        } else if (token->id == TokenIdLBracket) {
+        } else if (first_token->id == TokenIdLBracket) {
             *token_index += 1;
 
             AstNode *expr_node = ast_parse_expression(pc, token_index, true);
@@ -1458,7 +1441,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo
             if (ellipsis_or_r_bracket->id == TokenIdEllipsis) {
                 *token_index += 1;
 
-                AstNode *node = ast_create_node(pc, NodeTypeSliceExpr, token);
+                AstNode *node = ast_create_node(pc, NodeTypeSliceExpr, first_token);
                 node->data.slice_expr.array_ref_expr = primary_expr;
                 node->data.slice_expr.start = expr_node;
                 node->data.slice_expr.end = ast_parse_expression(pc, token_index, false);
@@ -1475,23 +1458,88 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo
             } else if (ellipsis_or_r_bracket->id == TokenIdRBracket) {
                 *token_index += 1;
 
-                AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, token);
+                AstNode *node = ast_create_node(pc, NodeTypeArrayAccessExpr, first_token);
                 node->data.array_access_expr.array_ref_expr = primary_expr;
                 node->data.array_access_expr.subscript = expr_node;
 
                 primary_expr = node;
             } else {
-                ast_invalid_token_error(pc, token);
+                ast_invalid_token_error(pc, first_token);
             }
-        } else if (token->id == TokenIdDot) {
+        } else if (first_token->id == TokenIdDot) {
             *token_index += 1;
 
             Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol);
 
-            AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, token);
+            AstNode *node = ast_create_node(pc, NodeTypeFieldAccessExpr, first_token);
             node->data.field_access_expr.struct_expr = primary_expr;
             ast_buf_from_token(pc, name_token, &node->data.field_access_expr.field_name);
 
+            primary_expr = node;
+        } else if (first_token->id == TokenIdLBrace) {
+            *token_index += 1;
+
+            AstNode *node = ast_create_node(pc, NodeTypeContainerInitExpr, first_token);
+            node->data.container_init_expr.type = primary_expr;
+
+            Token *token = &pc->tokens->at(*token_index);
+            if (token->id == TokenIdDot) {
+                for (;;) {
+                    if (token->id == TokenIdDot) {
+                        ast_eat_token(pc, token_index, TokenIdDot);
+                        Token *field_name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
+                        ast_eat_token(pc, token_index, TokenIdEq);
+
+                        AstNode *field_node = ast_create_node(pc, NodeTypeStructValueField, token);
+
+                        ast_buf_from_token(pc, field_name_tok, &field_node->data.struct_val_field.name);
+                        field_node->data.struct_val_field.expr = ast_parse_expression(pc, token_index, true);
+
+                        node->data.container_init_expr.entries.append(field_node);
+
+                        Token *comma_tok = &pc->tokens->at(*token_index);
+                        if (comma_tok->id == TokenIdComma) {
+                            *token_index += 1;
+                            token = &pc->tokens->at(*token_index);
+                            continue;
+                        } else if (comma_tok->id != TokenIdRBrace) {
+                            ast_invalid_token_error(pc, comma_tok);
+                        } else {
+                            *token_index += 1;
+                            break;
+                        }
+                    } else if (token->id == TokenIdRBrace) {
+                        *token_index += 1;
+                        break;
+                    } else {
+                        ast_invalid_token_error(pc, token);
+                    }
+                }
+
+            } else {
+                for (;;) {
+                    if (token->id == TokenIdRBrace) {
+                        *token_index += 1;
+                        break;
+                    } else {
+                        AstNode *elem_node = ast_parse_expression(pc, token_index, true);
+                        node->data.container_init_expr.entries.append(elem_node);
+
+                        Token *comma_tok = &pc->tokens->at(*token_index);
+                        if (comma_tok->id == TokenIdComma) {
+                            *token_index += 1;
+                            token = &pc->tokens->at(*token_index);
+                            continue;
+                        } else if (comma_tok->id != TokenIdRBrace) {
+                            ast_invalid_token_error(pc, comma_tok);
+                        } else {
+                            *token_index += 1;
+                            break;
+                        }
+                    }
+                }
+            }
+
             primary_expr = node;
         } else {
             return primary_expr;
@@ -1506,56 +1554,54 @@ static PrefixOp tok_to_prefix_op(Token *token) {
         case TokenIdTilde: return PrefixOpBinNot;
         case TokenIdAmpersand: return PrefixOpAddressOf;
         case TokenIdStar: return PrefixOpDereference;
+        case TokenIdMaybe: return PrefixOpMaybe;
+        case TokenIdBoolAnd: return PrefixOpAddressOf;
         default: return PrefixOpInvalid;
     }
 }
 
 /*
+PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression
 PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const)))
 */
-static PrefixOp ast_parse_prefix_op(ParseContext *pc, int *token_index, bool mandatory) {
+static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
-    PrefixOp result = tok_to_prefix_op(token);
-    if (result == PrefixOpInvalid) {
-        if (mandatory) {
-            ast_invalid_token_error(pc, token);
-        } else {
-            return PrefixOpInvalid;
-        }
+    PrefixOp prefix_op = tok_to_prefix_op(token);
+    if (prefix_op == PrefixOpInvalid) {
+        return ast_parse_suffix_op_expr(pc, token_index, mandatory);
     }
     *token_index += 1;
 
-    if (result == PrefixOpAddressOf) {
+    AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token);
+    AstNode *parent_node = node;
+    if (token->id == TokenIdBoolAnd) {
+        // pretend that we got 2 ampersand tokens
+
+        parent_node = ast_create_node(pc, NodeTypePrefixOpExpr, token);
+        parent_node->data.prefix_op_expr.primary_expr = node;
+        parent_node->data.prefix_op_expr.prefix_op = PrefixOpAddressOf;
+
+        node->column += 1;
+    }
+
+    if (prefix_op == PrefixOpAddressOf) {
         Token *token = &pc->tokens->at(*token_index);
         if (token->id == TokenIdKeywordConst) {
             *token_index += 1;
-            result = PrefixOpConstAddressOf;
+            prefix_op = PrefixOpConstAddressOf;
         }
     }
 
-    return result;
-}
-
-/*
-PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression
-*/
-static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory) {
-    Token *token = &pc->tokens->at(*token_index);
-    PrefixOp prefix_op = ast_parse_prefix_op(pc, token_index, false);
-    if (prefix_op == PrefixOpInvalid)
-        return ast_parse_suffix_op_expr(pc, token_index, mandatory);
-
     AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, true);
-    AstNode *node = ast_create_node(pc, NodeTypePrefixOpExpr, token);
     node->data.prefix_op_expr.primary_expr = prefix_op_expr;
     node->data.prefix_op_expr.prefix_op = prefix_op;
 
-    return node;
+    return parent_node;
 }
 
 
 /*
-CastExpression : CastExpression token(as) Type | PrefixOpExpression
+CastExpression : CastExpression token(as) PrimaryExpression | PrefixOpExpression
 */
 static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bool mandatory) {
     AstNode *operand_1 = ast_parse_prefix_op_expr(pc, token_index, mandatory);
@@ -1571,7 +1617,7 @@ static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bo
         AstNode *node = ast_create_node(pc, NodeTypeCastExpr, as_kw);
         node->data.cast_expr.expr = operand_1;
 
-        node->data.cast_expr.type = ast_parse_type(pc, token_index);
+        node->data.cast_expr.type = ast_parse_primary_expr(pc, token_index, true);
 
         operand_1 = node;
     }
@@ -1899,7 +1945,7 @@ static AstNode *ast_parse_else(ParseContext *pc, int *token_index, bool mandator
 /*
 IfExpression : IfVarExpression | IfBoolExpression
 IfBoolExpression : token(If) token(LParen) Expression token(RParen) Expression option(Else)
-IfVarExpression : token(If) token(LParen) (token(Const) | token(Var)) token(Symbol) option(token(Colon) Type) Token(MaybeAssign) Expression token(RParen) Expression Option(Else)
+IfVarExpression : token(If) token(LParen) (token(Const) | token(Var)) token(Symbol) option(Expression) Token(MaybeAssign) Expression token(RParen) Expression Option(Else)
 */
 static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) {
     Token *if_tok = &pc->tokens->at(*token_index);
@@ -1924,11 +1970,12 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda
         ast_buf_from_token(pc, name_token, &node->data.if_var_expr.var_decl.symbol);
 
         Token *eq_or_colon = &pc->tokens->at(*token_index);
-        *token_index += 1;
         if (eq_or_colon->id == TokenIdMaybeAssign) {
+            *token_index += 1;
             node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true);
         } else if (eq_or_colon->id == TokenIdColon) {
-            node->data.if_var_expr.var_decl.type = ast_parse_type(pc, token_index);
+            *token_index += 1;
+            node->data.if_var_expr.var_decl.type = ast_parse_expression(pc, token_index, true);
 
             ast_eat_token(pc, token_index, TokenIdMaybeAssign);
             node->data.if_var_expr.var_decl.expr = ast_parse_expression(pc, token_index, true);
@@ -1967,7 +2014,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m
 }
 
 /*
-VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression))
+VariableDeclaration : option(FnVisibleMod) (token(Var) | token(Const)) token(Symbol) (token(Eq) Expression | token(Colon) UnwrapMaybeExpression option(token(Eq) Expression))
 */
 static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) {
     Token *first_token = &pc->tokens->at(*token_index);
@@ -2008,8 +2055,7 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token
         node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true);
         return node;
     } else if (eq_or_colon->id == TokenIdColon) {
-        node->data.variable_declaration.type = ast_parse_type(pc, token_index);
-
+        node->data.variable_declaration.type = ast_parse_unwrap_maybe_expr(pc, token_index, true);
         Token *eq_token = &pc->tokens->at(*token_index);
         if (eq_token->id == TokenIdEq) {
             *token_index += 1;
@@ -2138,6 +2184,7 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, int *token_index, bool manda
 /*
 UnwrapMaybeExpression : BoolOrExpression token(DoubleQuestion) BoolOrExpression | BoolOrExpression
 */
+// this is currently the first child expression of assignment
 static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index, bool mandatory) {
     AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory);
     if (!lhs)
@@ -2185,190 +2232,7 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand
 }
 
 /*
-AsmInputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) Expression token(RParen)
-*/
-static void ast_parse_asm_input_item(ParseContext *pc, int *token_index, AstNode *node) {
-    ast_eat_token(pc, token_index, TokenIdLBracket);
-    Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol);
-    ast_eat_token(pc, token_index, TokenIdRBracket);
-
-    Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral);
-
-    ast_eat_token(pc, token_index, TokenIdLParen);
-    AstNode *expr_node = ast_parse_expression(pc, token_index, true);
-    ast_eat_token(pc, token_index, TokenIdRParen);
-
-    AsmInput *asm_input = allocate<AsmInput>(1);
-    ast_buf_from_token(pc, alias, &asm_input->asm_symbolic_name);
-    parse_string_literal(pc, constraint, &asm_input->constraint, nullptr, nullptr);
-    asm_input->expr = expr_node;
-    node->data.asm_expr.input_list.append(asm_input);
-}
-
-/*
-AsmOutputItem : token(LBracket) token(Symbol) token(RBracket) token(String) token(LParen) (token(Symbol) | token(Arrow) Type) token(RParen)
-*/
-static void ast_parse_asm_output_item(ParseContext *pc, int *token_index, AstNode *node) {
-    ast_eat_token(pc, token_index, TokenIdLBracket);
-    Token *alias = ast_eat_token(pc, token_index, TokenIdSymbol);
-    ast_eat_token(pc, token_index, TokenIdRBracket);
-
-    Token *constraint = ast_eat_token(pc, token_index, TokenIdStringLiteral);
-
-    AsmOutput *asm_output = allocate<AsmOutput>(1);
-
-    ast_eat_token(pc, token_index, TokenIdLParen);
-
-    Token *token = &pc->tokens->at(*token_index);
-    *token_index += 1;
-    if (token->id == TokenIdSymbol) {
-        ast_buf_from_token(pc, token, &asm_output->variable_name);
-    } else if (token->id == TokenIdArrow) {
-        asm_output->return_type = ast_parse_type(pc, token_index);
-    } else {
-        ast_invalid_token_error(pc, token);
-    }
-
-    ast_eat_token(pc, token_index, TokenIdRParen);
-
-    ast_buf_from_token(pc, alias, &asm_output->asm_symbolic_name);
-    parse_string_literal(pc, constraint, &asm_output->constraint, nullptr, nullptr);
-    node->data.asm_expr.output_list.append(asm_output);
-}
-
-/*
-AsmClobbers: token(Colon) list(token(String), token(Comma))
-*/
-static void ast_parse_asm_clobbers(ParseContext *pc, int *token_index, AstNode *node) {
-    Token *colon_tok = &pc->tokens->at(*token_index);
-
-    if (colon_tok->id != TokenIdColon)
-        return;
-
-    *token_index += 1;
-
-    for (;;) {
-        Token *string_tok = &pc->tokens->at(*token_index);
-        ast_expect_token(pc, string_tok, TokenIdStringLiteral);
-        *token_index += 1;
-
-        Buf *clobber_buf = buf_alloc();
-        parse_string_literal(pc, string_tok, clobber_buf, nullptr, nullptr);
-        node->data.asm_expr.clobber_list.append(clobber_buf);
-
-        Token *comma = &pc->tokens->at(*token_index);
-
-        if (comma->id == TokenIdComma) {
-            *token_index += 1;
-            continue;
-        } else {
-            break;
-        }
-    }
-}
-
-/*
-AsmInput : token(Colon) list(AsmInputItem, token(Comma)) option(AsmClobbers)
-*/
-static void ast_parse_asm_input(ParseContext *pc, int *token_index, AstNode *node) {
-    Token *colon_tok = &pc->tokens->at(*token_index);
-
-    if (colon_tok->id != TokenIdColon)
-        return;
-
-    *token_index += 1;
-
-    for (;;) {
-        ast_parse_asm_input_item(pc, token_index, node);
-
-        Token *comma = &pc->tokens->at(*token_index);
-
-        if (comma->id == TokenIdComma) {
-            *token_index += 1;
-            continue;
-        } else {
-            break;
-        }
-    }
-
-    ast_parse_asm_clobbers(pc, token_index, node);
-}
-
-/*
-AsmOutput : token(Colon) list(AsmOutputItem, token(Comma)) option(AsmInput)
-*/
-static void ast_parse_asm_output(ParseContext *pc, int *token_index, AstNode *node) {
-    Token *colon_tok = &pc->tokens->at(*token_index);
-
-    if (colon_tok->id != TokenIdColon)
-        return;
-
-    *token_index += 1;
-
-    for (;;) {
-        ast_parse_asm_output_item(pc, token_index, node);
-
-        Token *comma = &pc->tokens->at(*token_index);
-
-        if (comma->id == TokenIdComma) {
-            *token_index += 1;
-            continue;
-        } else {
-            break;
-        }
-    }
-
-    ast_parse_asm_input(pc, token_index, node);
-}
-
-/*
-AsmExpression : token(Asm) option(token(Volatile)) token(LParen) token(String) option(AsmOutput) token(RParen)
-*/
-static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mandatory) {
-    Token *asm_token = &pc->tokens->at(*token_index);
-
-    if (asm_token->id != TokenIdKeywordAsm) {
-        if (mandatory) {
-            ast_invalid_token_error(pc, asm_token);
-        } else {
-            return nullptr;
-        }
-    }
-
-    AstNode *node = ast_create_node(pc, NodeTypeAsmExpr, asm_token);
-
-    *token_index += 1;
-    Token *lparen_tok = &pc->tokens->at(*token_index);
-
-    if (lparen_tok->id == TokenIdKeywordVolatile) {
-        node->data.asm_expr.is_volatile = true;
-
-        *token_index += 1;
-        lparen_tok = &pc->tokens->at(*token_index);
-    }
-
-    ast_expect_token(pc, lparen_tok, TokenIdLParen);
-    *token_index += 1;
-
-    Token *template_tok = &pc->tokens->at(*token_index);
-    ast_expect_token(pc, template_tok, TokenIdStringLiteral);
-    *token_index += 1;
-
-    parse_string_literal(pc, template_tok, &node->data.asm_expr.asm_template, nullptr,
-            &node->data.asm_expr.offset_map);
-    parse_asm_template(pc, node);
-
-    ast_parse_asm_output(pc, token_index, node);
-
-    Token *rparen_tok = &pc->tokens->at(*token_index);
-    ast_expect_token(pc, rparen_tok, TokenIdRParen);
-    *token_index += 1;
-
-    return node;
-}
-
-/*
-NonBlockExpression : ReturnExpression | AssignmentExpression | AsmExpression
+NonBlockExpression : ReturnExpression | AssignmentExpression
 */
 static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
@@ -2381,10 +2245,6 @@ static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, boo
     if (ass_expr)
         return ass_expr;
 
-    AstNode *asm_expr = ast_parse_asm_expr(pc, token_index, false);
-    if (asm_expr)
-        return asm_expr;
-
     if (mandatory)
         ast_invalid_token_error(pc, token);
 
@@ -2440,6 +2300,14 @@ static AstNode *ast_parse_label(ParseContext *pc, int *token_index, bool mandato
     return node;
 }
 
+static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) {
+    AstNode *node = ast_create_node(pc, NodeTypeContainerInitExpr, token);
+    node->data.container_init_expr.type = ast_create_node(pc, NodeTypeSymbol, token);
+    node->data.container_init_expr.kind = ContainerInitKindArray;
+    buf_init_from_str(&node->data.container_init_expr.type->data.symbol_expr.symbol, "void");
+    return node;
+}
+
 /*
 Block : token(LBrace) list(option(Statement), token(Semicolon)) token(RBrace)
 Statement : Label | VariableDeclaration token(Semicolon) | NonBlockExpression token(Semicolon) | BlockExpression
@@ -2478,7 +2346,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato
                 if (!statement_node) {
                     statement_node = ast_parse_non_block_expr(pc, token_index, false);
                     if (!statement_node) {
-                        statement_node = ast_create_node(pc, NodeTypeVoid, last_token);
+                        statement_node = ast_create_void_expr(pc, last_token);
                     }
                 }
             }
@@ -2501,7 +2369,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato
 }
 
 /*
-FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type)
+FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(Expression)
 */
 static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory) {
     Token *first_token = &pc->tokens->at(*token_index);
@@ -2552,19 +2420,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand
 
     ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
 
-    Token *arrow = &pc->tokens->at(*token_index);
-    if (arrow->id == TokenIdArrow) {
-        *token_index += 1;
-        node->data.fn_proto.return_type = ast_parse_type(pc, token_index);
-    } else {
-        node->data.fn_proto.return_type = ast_create_void_type_node(pc, arrow);
+    Token *next_token = &pc->tokens->at(*token_index);
+    node->data.fn_proto.return_type = ast_parse_expression(pc, token_index, false);
+    if (!node->data.fn_proto.return_type) {
+        node->data.fn_proto.return_type = ast_create_void_type_node(pc, next_token);
     }
 
     return node;
 }
 
 /*
-FnDef : FnProto Block
+FnDef : FnProto token(FatArrow) Block
 */
 static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandatory) {
     AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory);
@@ -2573,6 +2439,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandat
     AstNode *node = ast_create_node_with_node(pc, NodeTypeFnDef, fn_proto);
 
     node->data.fn_def.fn_proto = fn_proto;
+    ast_eat_token(pc, token_index, TokenIdFatArrow);
     node->data.fn_def.body = ast_parse_block(pc, token_index, true);
 
     return node;
@@ -2709,7 +2576,7 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index) {
 /*
 ContainerDecl : many(Directive) option(FnVisibleMod) (token(Struct) | token(Enum)) token(Symbol) token(LBrace) many(StructMember) token(RBrace)
 StructMember: StructField | FnDecl
-StructField : token(Symbol) token(Colon) Type token(Comma)
+StructField : token(Symbol) option(token(Colon) Expression) token(Comma))
 */
 static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) {
     Token *first_token = &pc->tokens->at(*token_index);
@@ -2792,16 +2659,16 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) {
 
             ast_buf_from_token(pc, token, &field_node->data.struct_field.name);
 
-            Token *colon_tok = &pc->tokens->at(*token_index);
-            if (colon_tok->id == TokenIdColon) {
+            Token *expr_or_comma = &pc->tokens->at(*token_index);
+            if (expr_or_comma->id == TokenIdComma) {
+                field_node->data.struct_field.type = ast_create_void_type_node(pc, expr_or_comma);
                 *token_index += 1;
-                field_node->data.struct_field.type = ast_parse_type(pc, token_index);
             } else {
-                field_node->data.struct_field.type = ast_create_void_type_node(pc, colon_tok);
+                ast_eat_token(pc, token_index, TokenIdColon);
+                field_node->data.struct_field.type = ast_parse_expression(pc, token_index, true);
+                ast_eat_token(pc, token_index, TokenIdComma);
             }
 
-            ast_eat_token(pc, token_index, TokenIdComma);
-
             node->data.struct_decl.fields.append(field_node);
         } else {
             ast_invalid_token_error(pc, token);
src/tokenizer.cpp
@@ -207,8 +207,6 @@ static void end_token(Tokenize *t) {
         t->cur_tok->id = TokenIdKeywordConst;
     } else if (mem_eql_str(token_mem, token_len, "extern")) {
         t->cur_tok->id = TokenIdKeywordExtern;
-    } else if (mem_eql_str(token_mem, token_len, "unreachable")) {
-        t->cur_tok->id = TokenIdKeywordUnreachable;
     } else if (mem_eql_str(token_mem, token_len, "pub")) {
         t->cur_tok->id = TokenIdKeywordPub;
     } else if (mem_eql_str(token_mem, token_len, "export")) {
@@ -217,8 +215,6 @@ static void end_token(Tokenize *t) {
         t->cur_tok->id = TokenIdKeywordAs;
     } else if (mem_eql_str(token_mem, token_len, "use")) {
         t->cur_tok->id = TokenIdKeywordUse;
-    } else if (mem_eql_str(token_mem, token_len, "void")) {
-        t->cur_tok->id = TokenIdKeywordVoid;
     } 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")) {
@@ -553,6 +549,11 @@ void tokenize(Buf *buf, Tokenization *out) {
                         end_token(&t);
                         t.state = TokenizeStateStart;
                         break;
+                    case '>':
+                        t.cur_tok->id = TokenIdFatArrow;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
                     default:
                         t.pos -= 1;
                         end_token(&t);
@@ -1009,12 +1010,10 @@ static const char * token_name(Token *token) {
         case TokenIdKeywordVar: return "Var";
         case TokenIdKeywordReturn: return "Return";
         case TokenIdKeywordExtern: return "Extern";
-        case TokenIdKeywordUnreachable: return "Unreachable";
         case TokenIdKeywordPub: return "Pub";
         case TokenIdKeywordExport: return "Export";
         case TokenIdKeywordAs: return "As";
         case TokenIdKeywordUse: return "Use";
-        case TokenIdKeywordVoid: return "Void";
         case TokenIdKeywordTrue: return "True";
         case TokenIdKeywordFalse: return "False";
         case TokenIdKeywordIf: return "If";
@@ -1044,6 +1043,7 @@ static const char * token_name(Token *token) {
         case TokenIdPlus: return "Plus";
         case TokenIdColon: return "Colon";
         case TokenIdArrow: return "Arrow";
+        case TokenIdFatArrow: return "FatArrow";
         case TokenIdDash: return "Dash";
         case TokenIdNumberSign: return "NumberSign";
         case TokenIdBinOr: return "BinOr";
src/tokenizer.hpp
@@ -18,12 +18,10 @@ enum TokenId {
     TokenIdKeywordVar,
     TokenIdKeywordConst,
     TokenIdKeywordExtern,
-    TokenIdKeywordUnreachable,
     TokenIdKeywordPub,
     TokenIdKeywordExport,
     TokenIdKeywordAs,
     TokenIdKeywordUse,
-    TokenIdKeywordVoid,
     TokenIdKeywordTrue,
     TokenIdKeywordFalse,
     TokenIdKeywordIf,
@@ -53,6 +51,7 @@ enum TokenId {
     TokenIdPlus,
     TokenIdColon,
     TokenIdArrow,
+    TokenIdFatArrow,
     TokenIdDash,
     TokenIdNumberSign,
     TokenIdBoolOr,
std/bootstrap.zig
@@ -3,10 +3,35 @@ use "syscall.zig";
 // The compiler treats this file special by implicitly importing the function `main`
 // from the root source file.
 
+var env: &&u8;
+
 #attribute("naked")
-export fn _start() -> unreachable {
-    const argc = asm("mov (%%rsp), %[argc]" : [argc] "=r" (-> isize));
-    const argv = asm("lea 0x8(%%rsp), %[argv]" : [argv] "=r" (-> &&u8));
-    const env = asm("lea 0x10(%%rsp,%%rdi,8), %[env]" : [env] "=r" (-> &&u8));
-    exit(main(argc, argv, env))
+export fn _start() unreachable => {
+    const argc = asm("mov (%%rsp), %[argc]": [argc] "=r" (-> isize));
+    const argv = asm("lea 0x8(%%rsp), %[argv]": [argv] "=r" (-> &&u8));
+    env = asm("lea 0x10(%%rsp,%%rdi,8), %[env]": [env] "=r" (-> &&u8));
+
+    exit(main(argc, argv, env));
+
+/*
+    var args = @alloca_array([]u8, argc);
+    var i : @typeof(argc) = 0;
+    // TODO for in loop over the array
+    while (i < argc) {
+        const ptr = argv[i];
+        args[i] = ptr[0...strlen(ptr)];
+        i += 1;
+    }
+    exit(main(args))
+    */
+}
+
+/*
+fn strlen(ptr: &u8) isize => {
+    var count: isize = 0;
+    while (ptr[count]) {
+        count += 1;
+    }
+    return count;
 }
+*/
std/builtin.zig
@@ -1,8 +1,8 @@
 // These functions are provided when not linking against libc because LLVM
 // sometimes generates code that calls them.
 
-export fn memset(dest: &u8, c: u8, n: usize) -> &u8 {
-    var index : #typeof(n) = 0;
+export fn memset(dest: &u8, c: u8, n: usize) &u8 => {
+    var index : @typeof(n) = 0;
     while (index != n) {
         dest[index] = c;
         index += 1;
@@ -10,8 +10,8 @@ export fn memset(dest: &u8, c: u8, n: usize) -> &u8 {
     return dest;
 }
 
-export fn memcpy(dest: &noalias u8, src: &const noalias u8, n: usize) -> &u8 {
-    var index : #typeof(n) = 0;
+export fn memcpy(noalias dest: &u8, noalias src: &const u8, n: usize) &u8 => {
+    var index : @typeof(n) = 0;
     while (index != n) {
         dest[index] = src[index];
         index += 1;
std/rand.zig
@@ -4,13 +4,13 @@ const ARRAY_SIZE : u16 = 624;
 /// Use `rand_init` to initialize this state.
 pub struct Rand {
     array: [ARRAY_SIZE]u32,
-    index: #typeof(ARRAY_SIZE),
+    index: @typeof(ARRAY_SIZE),
 
     /// Initialize random state with the given seed.
-    pub fn init(r: &Rand, seed: u32) {
+    pub fn init(r: &Rand, seed: u32) => {
         r.index = 0;
         r.array[0] = seed;
-        var i : #typeof(ARRAY_SIZE) = 1;
+        var i : @typeof(ARRAY_SIZE) = 1;
         while (i < ARRAY_SIZE) {
             const prev_value : u64 = r.array[i - 1];
             r.array[i] = ((prev_value ^ (prev_value << 30)) * 0x6c078965 + i) as u32;
@@ -20,7 +20,7 @@ pub struct Rand {
 
 
     /// Get 32 bits of randomness.
-    pub fn get_u32(r: &Rand) -> u32 {
+    pub fn get_u32(r: &Rand) u32 => {
         if (r.index == 0) {
             r.generate_numbers();
         }
@@ -37,13 +37,13 @@ pub struct Rand {
     }
 
     /// Fill `buf` with randomness.
-    pub fn get_bytes(r: &Rand, buf: []u8) {
+    pub fn get_bytes(r: &Rand, buf: []u8) => {
         var bytes_left = r.get_bytes_aligned(buf);
         if (bytes_left > 0) {
-            var rand_val_array : [#sizeof(u32)]u8;
-            *(rand_val_array.ptr as &u32) = r.get_u32();
+            var rand_val_array : [@sizeof(u32)]u8;
+            *(rand_val_array.ptr as (&u32)) = r.get_u32();
             while (bytes_left > 0) {
-                buf[buf.len - bytes_left] = rand_val_array[#sizeof(u32) - bytes_left];
+                buf[buf.len - bytes_left] = rand_val_array[@sizeof(u32) - bytes_left];
                 bytes_left -= 1;
             }
         }
@@ -51,23 +51,23 @@ pub struct Rand {
 
     /// Get a random unsigned integer with even distribution between `start`
     /// inclusive and `end` exclusive.
-    pub fn range_u64(r: &Rand, start: u64, end: u64) -> u64 {
+    pub fn range_u64(r: &Rand, start: u64, end: u64) u64 => {
         const range = end - start;
-        const leftover = #max_value(u64) % range;
-        const upper_bound = #max_value(u64) - leftover;
-        var rand_val_array : [#sizeof(u64)]u8;
+        const leftover = @max_value(u64) % range;
+        const upper_bound = @max_value(u64) - leftover;
+        var rand_val_array : [@sizeof(u64)]u8;
 
         while (true) {
             r.get_bytes_aligned(rand_val_array);
-            const rand_val = *(rand_val_array.ptr as &u64);
+            const rand_val = *(rand_val_array.ptr as (&u64));
             if (rand_val < upper_bound) {
                 return start + (rand_val % range);
             }
         }
     }
 
-    fn generate_numbers(r: &Rand) {
-        var i : #typeof(ARRAY_SIZE) = 0;
+    fn generate_numbers(r: &Rand) => {
+        var i : @typeof(ARRAY_SIZE) = 0;
         while (i < ARRAY_SIZE) {
             const y : u32 = (r.array[i] & 0x80000000) + (r.array[(i + 1) % ARRAY_SIZE] & 0x7fffffff);
             const untempered : u32 = r.array[(i + 397) % ARRAY_SIZE] ^ (y >> 1);
@@ -82,11 +82,11 @@ pub struct Rand {
     }
 
     // does not populate the remaining (buf.len % 4) bytes
-    fn get_bytes_aligned(r: &Rand, buf: []u8) -> usize {
+    fn get_bytes_aligned(r: &Rand, buf: []u8) usize => {
         var bytes_left = buf.len;
         while (bytes_left >= 4) {
-            *(&buf[buf.len - bytes_left] as &u32) = r.get_u32();
-            bytes_left -= #sizeof(u32);
+            *(&buf[buf.len - bytes_left] as (&u32)) = r.get_u32();
+            bytes_left -= @sizeof(u32);
         }
         return bytes_left;
     }
std/std.zig
@@ -5,25 +5,25 @@ pub const stdout_fileno : isize = 1;
 pub const stderr_fileno : isize = 2;
 
 // TODO error handling
-pub fn os_get_random_bytes(buf: &u8, count: usize) -> isize {
+pub fn os_get_random_bytes(buf: &u8, count: usize) isize => {
     getrandom(buf, count, 0)
 }
 
 // TODO error handling
 // TODO handle buffering and flushing (mutex protected)
-pub fn print_str(str: []const u8) -> isize {
+pub fn print_str(str: []const u8) isize => {
     fprint_str(stdout_fileno, str)
 }
 
 // TODO error handling
 // TODO handle buffering and flushing (mutex protected)
-pub fn fprint_str(fd: isize, str: []const u8) -> isize {
+pub fn fprint_str(fd: isize, str: []const u8) isize => {
     write(fd, str.ptr, str.len)
 }
 
 // TODO handle buffering and flushing (mutex protected)
 // TODO error handling
-pub fn print_u64(x: u64) -> isize {
+pub fn print_u64(x: u64) isize => {
     var buf: [max_u64_base10_digits]u8;
     const len = buf_print_u64(buf, x);
     return write(stdout_fileno, buf.ptr, len);
@@ -31,14 +31,14 @@ pub fn print_u64(x: u64) -> isize {
 
 // TODO handle buffering and flushing (mutex protected)
 // TODO error handling
-pub fn print_i64(x: i64) -> isize {
+pub fn print_i64(x: i64) isize => {
     var buf: [max_u64_base10_digits]u8;
     const len = buf_print_i64(buf, x);
     return write(stdout_fileno, buf.ptr, len);
 }
 
 // TODO error handling
-pub fn readline(buf: []u8, out_len: &usize) -> bool {
+pub fn readline(buf: []u8, out_len: &usize) bool => {
     const amt_read = read(stdin_fileno, buf.ptr, buf.len);
     if (amt_read < 0) {
         return true;
@@ -48,10 +48,10 @@ pub fn readline(buf: []u8, out_len: &usize) -> bool {
 }
 
 // TODO return ?u64 when we support returning struct byval
-pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool {
+pub fn parse_u64(buf: []u8, radix: u8, result: &u64) bool => {
     var x : u64 = 0;
 
-    var i : #typeof(buf.len) = 0;
+    var i : @typeof(buf.len) = 0;
     while (i < buf.len) {
         const c = buf[i];
         const digit = char_to_digit(c);
@@ -77,7 +77,7 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool {
     return false;
 }
 
-fn char_to_digit(c: u8) -> u8 {
+fn char_to_digit(c: u8) u8 => {
     if ('0' <= c && c <= '9') {
         c - '0'
     } else if ('A' <= c && c <= 'Z') {
@@ -85,13 +85,13 @@ fn char_to_digit(c: u8) -> u8 {
     } else if ('a' <= c && c <= 'z') {
         c - 'a' + 10
     } else {
-        #max_value(u8)
+        @max_value(u8)
     }
 }
 
 const max_u64_base10_digits: usize = 20;
 
-fn buf_print_i64(out_buf: []u8, x: i64) -> usize {
+fn buf_print_i64(out_buf: []u8, x: i64) usize => {
     if (x < 0) {
         out_buf[0] = '-';
         return 1 + buf_print_u64(out_buf[1...], ((-(x + 1)) as u64) + 1);
@@ -100,7 +100,7 @@ fn buf_print_i64(out_buf: []u8, x: i64) -> usize {
     }
 }
 
-fn buf_print_u64(out_buf: []u8, x: u64) -> usize {
+fn buf_print_u64(out_buf: []u8, x: u64) usize => {
     var buf: [max_u64_base10_digits]u8;
     var a = x;
     var index = buf.len;
std/syscall.zig
@@ -3,34 +3,34 @@ const SYS_write : usize = 1;
 const SYS_exit : usize = 60;
 const SYS_getrandom : usize = 318;
 
-fn syscall1(number: usize, arg1: usize) -> usize {
+fn syscall1(number: usize, arg1: usize) usize => {
     asm volatile ("syscall"
         : [ret] "={rax}" (-> usize)
         : [number] "{rax}" (number), [arg1] "{rdi}" (arg1)
         : "rcx", "r11")
 }
 
-fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) -> usize {
+fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize => {
     asm volatile ("syscall"
         : [ret] "={rax}" (-> usize)
         : [number] "{rax}" (number), [arg1] "{rdi}" (arg1), [arg2] "{rsi}" (arg2), [arg3] "{rdx}" (arg3)
         : "rcx", "r11")
 }
 
-pub fn read(fd: isize, buf: &u8, count: usize) -> isize {
+pub fn read(fd: isize, buf: &u8, count: usize) isize => {
     syscall3(SYS_read, fd as usize, buf as usize, count) as isize
 }
 
-pub fn write(fd: isize, buf: &const u8, count: usize) -> isize {
+pub fn write(fd: isize, buf: &const u8, count: usize) isize => {
     syscall3(SYS_write, fd as usize, buf as usize, count) as isize
 }
 
-pub fn exit(status: i32) -> unreachable {
+pub fn exit(status: i32) unreachable => {
     syscall1(SYS_exit, status as usize);
-    unreachable
+    unreachable{}
 }
 
-pub fn getrandom(buf: &u8, count: usize, flags: u32) -> isize {
+pub fn getrandom(buf: &u8, count: usize, flags: u32) isize => {
     syscall3(SYS_getrandom, buf as usize, count, flags as usize) as isize
 }
 
test/run_tests.cpp
@@ -96,50 +96,50 @@ static TestCase *add_compile_fail_case(const char *case_name, const char *source
 
 static void add_compiling_test_cases(void) {
     add_simple_case("hello world with libc", R"SOURCE(
-        #link("c")
-        extern {
-            fn puts(s: &const u8) -> i32;
-        }
+#link("c")
+extern {
+    fn puts(s: &const u8) i32;
+}
 
-        export fn main(argc: i32, argv: &&u8, env: &&u8) -> i32 {
-            puts(c"Hello, world!");
-            return 0;
-        }
+export fn main(argc: i32, argv: &&u8, env: &&u8) i32 => {
+    puts(c"Hello, world!");
+    return 0;
+}
     )SOURCE", "Hello, world!\n");
 
     add_simple_case("function call", R"SOURCE(
-        use "std.zig";
-        use "syscall.zig";
+use "std.zig";
+use "syscall.zig";
 
-        fn empty_function_1() {}
-        fn empty_function_2() { return; }
+fn empty_function_1() => {}
+fn empty_function_2() => { return; }
 
-        pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
-            empty_function_1();
-            empty_function_2();
-            this_is_a_function();
-        }
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
+    empty_function_1();
+    empty_function_2();
+    this_is_a_function();
+}
 
-        fn this_is_a_function() -> unreachable {
-            print_str("OK\n");
-            exit(0);
-        }
+fn this_is_a_function() unreachable => {
+    print_str("OK\n");
+    exit(0);
+}
     )SOURCE", "OK\n");
 
     add_simple_case("comments", R"SOURCE(
-        use "std.zig";
-
-        /**
-         * multi line doc comment
-         */
-        fn another_function() {}
-
-        /// this is a documentation comment
-        /// doc comment line 2
-        pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
-            print_str(/* mid-line comment /* nested */ */ "OK\n");
-            return 0;
-        }
+use "std.zig";
+
+/**
+    * multi line doc comment
+    */
+fn another_function() => {}
+
+/// this is a documentation comment
+/// doc comment line 2
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
+    print_str(/* mid-line comment /* nested */ */ "OK\n");
+    return 0;
+}
     )SOURCE", "OK\n");
 
     {
@@ -147,13 +147,13 @@ static void add_compiling_test_cases(void) {
 use "std.zig";
 use "foo.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     private_function();
     print_str("OK 2\n");
     return 0;
 }
 
-fn private_function() {
+fn private_function() => {
     print_text();
 }
         )SOURCE", "OK 1\nOK 2\n");
@@ -163,11 +163,11 @@ use "std.zig";
 
 // purposefully conflicting function with main.zig
 // but it's private so it should be OK
-fn private_function() {
+fn private_function() => {
     print_str("OK 1\n");
 }
 
-pub fn print_text() {
+pub fn print_text() => {
     private_function();
 }
         )SOURCE");
@@ -178,7 +178,7 @@ pub fn print_text() {
 use "foo.zig";
 use "bar.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     foo_function();
     bar_function();
     return 0;
@@ -187,7 +187,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
 
         add_source_file(tc, "foo.zig", R"SOURCE(
 use "std.zig";
-pub fn foo_function() {
+pub fn foo_function() => {
     print_str("OK\n");
 }
         )SOURCE");
@@ -196,7 +196,7 @@ pub fn foo_function() {
 use "other.zig";
 use "std.zig";
 
-pub fn bar_function() {
+pub fn bar_function() => {
     if (foo_function()) {
         print_str("OK\n");
     }
@@ -204,7 +204,7 @@ pub fn bar_function() {
         )SOURCE");
 
         add_source_file(tc, "other.zig", R"SOURCE(
-pub fn foo_function() -> bool {
+pub fn foo_function() bool => {
     // this one conflicts with the one from foo
     return true;
 }
@@ -212,65 +212,65 @@ pub fn foo_function() -> bool {
     }
 
     add_simple_case("if statements", R"SOURCE(
-        use "std.zig";
+use "std.zig";
 
-        pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
-            if (1 != 0) {
-                print_str("1 is true\n");
-            } else {
-                print_str("1 is false\n");
-            }
-            if (0 != 0) {
-                print_str("0 is true\n");
-            } else if (1 - 1 != 0) {
-                print_str("1 - 1 is true\n");
-            }
-            if (!(0 != 0)) {
-                print_str("!0 is true\n");
-            }
-            return 0;
-        }
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
+    if (1 != 0) {
+        print_str("1 is true\n");
+    } else {
+        print_str("1 is false\n");
+    }
+    if (0 != 0) {
+        print_str("0 is true\n");
+    } else if (1 - 1 != 0) {
+        print_str("1 - 1 is true\n");
+    }
+    if (!(0 != 0)) {
+        print_str("!0 is true\n");
+    }
+    return 0;
+}
     )SOURCE", "1 is true\n!0 is true\n");
 
     add_simple_case("params", R"SOURCE(
-        use "std.zig";
+use "std.zig";
 
-        fn add(a: i32, b: i32) -> i32 {
-            a + b
-        }
+fn add(a: i32, b: i32) i32 => {
+    a + b
+}
 
-        pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
-            if (add(22, 11) == 33) {
-                print_str("pass\n");
-            }
-            return 0;
-        }
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
+    if (add(22, 11) == 33) {
+        print_str("pass\n");
+    }
+    return 0;
+}
     )SOURCE", "pass\n");
 
     add_simple_case("goto", R"SOURCE(
-        use "std.zig";
+use "std.zig";
 
-        fn loop(a : i32) {
-            if (a == 0) {
-                goto done;
-            }
-            print_str("loop\n");
-            loop(a - 1);
+fn loop(a : i32) => {
+    if (a == 0) {
+        goto done;
+    }
+    print_str("loop\n");
+    loop(a - 1);
 
-        done:
-            return;
-        }
+done:
+    return;
+}
 
-        pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
-            loop(3);
-            return 0;
-        }
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
+    loop(3);
+    return 0;
+}
     )SOURCE", "loop\nloop\nloop\n");
 
     add_simple_case("local variables", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     const a : i32 = 1;
     const b = 2 as i32;
     if (a + b == 3) {
@@ -283,7 +283,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
     add_simple_case("bool literals", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     if (true)   { print_str("OK 1\n"); }
     if (false)  { print_str("BAD 1\n"); }
     if (!true)  { print_str("BAD 2\n"); }
@@ -295,7 +295,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
     add_simple_case("separate block scopes", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     if (true) {
         const no_conflict : i32 = 5;
         if (no_conflict == 5) { print_str("OK 1\n"); }
@@ -313,12 +313,12 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
     add_simple_case("void parameters", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
-    void_fun(1, void, 2);
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
+    void_fun(1, void{}, 2);
     return 0;
 }
 
-fn void_fun(a : i32, b : void, c : i32) {
+fn void_fun(a : i32, b : void, c : i32) => {
     const v = b;
     const vv : void = if (a == 1) {v} else {};
     if (a + c == 3) { print_str("OK\n"); }
@@ -333,16 +333,16 @@ struct Foo {
     b : i32,
     c : void,
 }
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     const foo = Foo {
-        .a = void,
+        .a = void{},
         .b = 1,
-        .c = void,
+        .c = void{},
     };
     if (foo.b != 1) {
         print_str("BAD\n");
     }
-    if (#sizeof(Foo) != 4) {
+    if (@sizeof(Foo) != 4) {
         print_str("BAD\n");
     }
     print_str("OK\n");
@@ -354,7 +354,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
     add_simple_case("mutable local variables", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     var zero : i32 = 0;
     if (zero == 0) { print_str("zero\n"); }
 
@@ -374,7 +374,7 @@ done:
     add_simple_case("arrays", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     var array : [5]u32;
 
     var i : u32 = 0;
@@ -401,7 +401,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
 
     return 0;
 }
-fn get_array_len(a: []u32) -> usize {
+fn get_array_len(a: []u32) usize => {
     a.len
 }
     )SOURCE", "OK\n");
@@ -410,7 +410,7 @@ fn get_array_len(a: []u32) -> usize {
     add_simple_case("hello world without libc", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
+pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     print_str("Hello, world!\n");
     return 0;
 }
@@ -420,7 +420,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
     add_simple_case("a + b + c", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
+pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     if (false || false || false) { print_str("BAD 1\n"); }
     if (true && true && false)   { print_str("BAD 2\n"); }
     if (1 | 2 | 4 != 7)          { print_str("BAD 3\n"); }
@@ -442,7 +442,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
     add_simple_case("short circuit", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
+pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     if (true || { print_str("BAD 1\n"); false }) {
       print_str("OK 1\n");
     }
@@ -465,7 +465,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
     add_simple_case("modify operators", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
+pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     var i : i32 = 0;
     i += 5;  if (i != 5)  { print_str("BAD +=\n"); }
     i -= 2;  if (i != 3)  { print_str("BAD -=\n"); }
@@ -488,10 +488,10 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
     add_simple_case("number literals", R"SOURCE(
 #link("c")
 extern {
-    fn printf(__format: &const u8, ...) -> i32;
+    fn printf(__format: &const u8, ...) i32;
 }
 
-export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
+export fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     printf(c"\n");
 
     printf(c"0: %llu\n",
@@ -617,9 +617,9 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
     add_simple_case("structs", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
+pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     var foo : Foo;
-    @memset(&foo, 0, #sizeof(Foo));
+    @memset(&foo, 0, @sizeof(Foo));
     foo.a += 1;
     foo.b = foo.a == 1;
     test_foo(foo);
@@ -638,12 +638,12 @@ struct Foo {
     b : bool,
     c : f32,
 }
-fn test_foo(foo : Foo) {
+fn test_foo(foo : Foo) => {
     if (!foo.b) {
         print_str("BAD\n");
     }
 }
-fn test_mutation(foo : &Foo) {
+fn test_mutation(foo : &Foo) => {
     foo.c = 100;
 }
 struct Node {
@@ -654,7 +654,7 @@ struct Node {
 struct Val {
     x: i32,
 }
-fn test_point_to_self() {
+fn test_point_to_self() => {
     var root : Node;
     root.val.x = 1;
 
@@ -668,7 +668,7 @@ fn test_point_to_self() {
         print_str("BAD\n");
     }
 }
-fn test_byval_assign() {
+fn test_byval_assign() => {
     var foo1 : Foo;
     var foo2 : Foo;
 
@@ -680,7 +680,7 @@ fn test_byval_assign() {
 
     if (foo2.a != 1234) { print_str("BAD - byval assignment failed\n"); }
 }
-fn test_initializer() {
+fn test_initializer() => {
     const val = Val { .x = 42 };
     if (val.x != 42) { print_str("BAD\n"); }
 }
@@ -692,7 +692,7 @@ use "std.zig";
 const g1 : i32 = 1233 + 1;
 var g2 : i32 = 0;
 
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
+pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     if (g2 != 0) { print_str("BAD\n"); }
     g2 = g1;
     if (g2 != 1234) { print_str("BAD\n"); }
@@ -703,7 +703,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
 
     add_simple_case("while loop", R"SOURCE(
 use "std.zig";
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
+pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     var i : i32 = 0;
     while (i < 4) {
         print_str("loop\n");
@@ -711,7 +711,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
     }
     return f();
 }
-fn f() -> i32 {
+fn f() i32 => {
     while (true) {
         return 0;
     }
@@ -720,7 +720,7 @@ fn f() -> i32 {
 
     add_simple_case("continue and break", R"SOURCE(
 use "std.zig";
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
+pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     var i : i32 = 0;
     while (true) {
         print_str("loop\n");
@@ -736,7 +736,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
 
     add_simple_case("maybe type", R"SOURCE(
 use "std.zig";
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     const x : ?bool = true;
 
     if (const y ?= x) {
@@ -759,7 +759,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
 
     const final_x : ?i32 = 13;
 
-    const num = final_x ?? unreachable;
+    const num = final_x ?? unreachable{};
 
     if (num != 13) {
         print_str("BAD\n");
@@ -771,26 +771,26 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
 
     add_simple_case("implicit cast after unreachable", R"SOURCE(
 use "std.zig";
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
+pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     const x = outer();
     if (x == 1234) {
         print_str("OK\n");
     }
     return 0;
 }
-fn inner() -> i32 { 1234 }
-fn outer() -> isize {
+fn inner() i32 => { 1234 }
+fn outer() isize => {
     return inner();
 }
     )SOURCE", "OK\n");
 
-    add_simple_case("#sizeof() and #typeof()", R"SOURCE(
+    add_simple_case("@sizeof() and @typeof()", R"SOURCE(
 use "std.zig";
 const x: u16 = 13;
-const z: #typeof(x) = 19;
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
-    const y: #typeof(x) = 120;
-    print_u64(#sizeof(#typeof(y)));
+const z: @typeof(x) = 19;
+pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
+    const y: @typeof(x) = 120;
+    print_u64(@sizeof(@typeof(y)));
     print_str("\n");
     return 0;
 }
@@ -800,11 +800,11 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
 use "std.zig";
 struct Rand {
     seed: u32,
-    pub fn get_seed(r: Rand) -> u32 {
+    pub fn get_seed(r: Rand) u32 => {
         r.seed
     }
 }
-pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
+pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     const r = Rand {.seed = 1234};
     if (r.get_seed() != 1234) {
         print_str("BAD seed\n");
@@ -817,7 +817,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
     add_simple_case("pointer dereferencing", R"SOURCE(
 use "std.zig";
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     var x = 3 as i32;
     const y = &x;
 
@@ -839,9 +839,9 @@ use "std.zig";
 
 const ARRAY_SIZE : u8 = 20;
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     var array : [ARRAY_SIZE]u8;
-    print_u64(#sizeof(#typeof(array)));
+    print_u64(@sizeof(@typeof(array)));
     print_str("\n");
     return 0;
 }
@@ -849,69 +849,69 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
 
     add_simple_case("#min_value() and #max_value()", R"SOURCE(
 use "std.zig";
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     print_str("max u8: ");
-    print_u64(#max_value(u8));
+    print_u64(@max_value(u8));
     print_str("\n");
 
     print_str("max u16: ");
-    print_u64(#max_value(u16));
+    print_u64(@max_value(u16));
     print_str("\n");
 
     print_str("max u32: ");
-    print_u64(#max_value(u32));
+    print_u64(@max_value(u32));
     print_str("\n");
 
     print_str("max u64: ");
-    print_u64(#max_value(u64));
+    print_u64(@max_value(u64));
     print_str("\n");
 
     print_str("max i8: ");
-    print_i64(#max_value(i8));
+    print_i64(@max_value(i8));
     print_str("\n");
 
     print_str("max i16: ");
-    print_i64(#max_value(i16));
+    print_i64(@max_value(i16));
     print_str("\n");
 
     print_str("max i32: ");
-    print_i64(#max_value(i32));
+    print_i64(@max_value(i32));
     print_str("\n");
 
     print_str("max i64: ");
-    print_i64(#max_value(i64));
+    print_i64(@max_value(i64));
     print_str("\n");
 
     print_str("min u8: ");
-    print_u64(#min_value(u8));
+    print_u64(@min_value(u8));
     print_str("\n");
 
     print_str("min u16: ");
-    print_u64(#min_value(u16));
+    print_u64(@min_value(u16));
     print_str("\n");
 
     print_str("min u32: ");
-    print_u64(#min_value(u32));
+    print_u64(@min_value(u32));
     print_str("\n");
 
     print_str("min u64: ");
-    print_u64(#min_value(u64));
+    print_u64(@min_value(u64));
     print_str("\n");
 
     print_str("min i8: ");
-    print_i64(#min_value(i8));
+    print_i64(@min_value(i8));
     print_str("\n");
 
     print_str("min i16: ");
-    print_i64(#min_value(i16));
+    print_i64(@min_value(i16));
     print_str("\n");
 
     print_str("min i32: ");
-    print_i64(#min_value(i32));
+    print_i64(@min_value(i32));
     print_str("\n");
 
     print_str("min i64: ");
-    print_i64(#min_value(i64));
+    print_i64(@min_value(i64));
     print_str("\n");
 
     return 0;
@@ -937,7 +937,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
 
     add_simple_case("slicing", R"SOURCE(
 use "std.zig";
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     var array : [20]i32;
 
     array[5] = 1234;
@@ -965,13 +965,13 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
 
     add_simple_case("else if expression", R"SOURCE(
 use "std.zig";
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     if (f(1) == 1) {
         print_str("OK\n");
     }
     return 0;
 }
-fn f(c: u8) -> u8 {
+fn f(c: u8) u8 => {
     if (c == 0) {
         0
     } else if (c == 1) {
@@ -984,7 +984,7 @@ fn f(c: u8) -> u8 {
 
     add_simple_case("overflow intrinsics", R"SOURCE(
 use "std.zig";
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     var result: u8;
     if (!@add_with_overflow_u8(250, 100, &result)) {
         print_str("BAD\n");
@@ -1002,7 +1002,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
 
     add_simple_case("memcpy and memset intrinsics", R"SOURCE(
 use "std.zig";
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     var foo : [20]u8;
     var bar : [20]u8;
 
@@ -1020,13 +1020,13 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
 
     add_simple_case("order-independent declarations", R"SOURCE(
 use "std.zig";
-const z : #typeof(stdin_fileno) = 0;
-const x : #typeof(y) = 1234;
+const z : @typeof(stdin_fileno) = 0;
+const x : @typeof(y) = 1234;
 const y : u16 = 5678;
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     print_ok(x)
 }
-fn print_ok(val: #typeof(x)) -> #typeof(foo) {
+fn print_ok(val: @typeof(x)) @typeof(foo) => {
     print_str("OK\n");
     return 0;
 }
@@ -1054,7 +1054,7 @@ enum Bar {
     D,
 }
 
-pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
+pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     const foo1 = Foo.One(13);
     const foo2 = Foo.Two(Point { .x = 1234, .y = 5678, });
     const bar = Bar.B;
@@ -1063,18 +1063,18 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
         print_str("BAD\n");
     }
 
-    if (#value_count(Foo) != 3) {
+    if (@value_count(Foo) != 3) {
         print_str("BAD\n");
     }
 
-    if (#value_count(Bar) != 4) {
+    if (@value_count(Bar) != 4) {
         print_str("BAD\n");
     }
 
-    if (#sizeof(Foo) != 17) {
+    if (@sizeof(Foo) != 17) {
         print_str("BAD\n");
     }
-    if (#sizeof(Bar) != 1) {
+    if (@sizeof(Bar) != 1) {
         print_str("BAD\n");
     }
 
@@ -1090,8 +1090,8 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 {
 
 static void add_compile_failure_test_cases(void) {
     add_compile_fail_case("multiple function definitions", R"SOURCE(
-fn a() {}
-fn a() {}
+fn a() => {}
+fn a() => {}
     )SOURCE", 1, ".tmp_source.zig:3:1: error: redefinition of 'a'");
 
     add_compile_fail_case("bad directive", R"SOURCE(
@@ -1100,46 +1100,46 @@ extern {
     fn b();
 }
 #bogus2("")
-fn a() {}
+fn a() => {}
     )SOURCE", 2, ".tmp_source.zig:2:1: error: invalid directive: 'bogus1'",
                  ".tmp_source.zig:6:1: error: invalid directive: 'bogus2'");
 
     add_compile_fail_case("unreachable with return", R"SOURCE(
-fn a() -> unreachable {return;}
+fn a() unreachable => {return;}
     )SOURCE", 1, ".tmp_source.zig:2:24: error: expected type 'unreachable', got 'void'");
 
     add_compile_fail_case("control reaches end of non-void function", R"SOURCE(
-fn a() -> i32 {}
+fn a() i32 => {}
     )SOURCE", 1, ".tmp_source.zig:2:15: error: expected type 'i32', got 'void'");
 
     add_compile_fail_case("undefined function call", R"SOURCE(
-fn a() {
+fn a() => {
     b();
 }
     )SOURCE", 1, ".tmp_source.zig:3:5: error: undefined function: 'b'");
 
     add_compile_fail_case("wrong number of arguments", R"SOURCE(
-fn a() {
+fn a() => {
     b(1);
 }
-fn b(a: i32, b: i32, c: i32) { }
+fn b(a: i32, b: i32, c: i32) => { }
     )SOURCE", 1, ".tmp_source.zig:3:6: error: expected 3 arguments, got 1");
 
     add_compile_fail_case("invalid type", R"SOURCE(
-fn a() -> bogus {}
-    )SOURCE", 1, ".tmp_source.zig:2:11: error: invalid type name: 'bogus'");
+fn a() bogus => {}
+    )SOURCE", 1, ".tmp_source.zig:2:8: error: use of undeclared identifier 'bogus'");
 
     add_compile_fail_case("pointer to unreachable", R"SOURCE(
-fn a() -> &unreachable {}
-    )SOURCE", 1, ".tmp_source.zig:2:11: error: pointer to unreachable not allowed");
+fn a() &unreachable => {}
+    )SOURCE", 1, ".tmp_source.zig:2:8: error: pointer to unreachable not allowed");
 
     add_compile_fail_case("unreachable code", R"SOURCE(
-fn a() {
+fn a() => {
     return;
     b();
 }
 
-fn b() {}
+fn b() => {}
     )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code");
 
     add_compile_fail_case("bad version string", R"SOURCE(
@@ -1152,7 +1152,7 @@ use "bogus-does-not-exist.zig";
     )SOURCE", 1, ".tmp_source.zig:2:1: error: unable to find 'bogus-does-not-exist.zig'");
 
     add_compile_fail_case("undeclared identifier", R"SOURCE(
-fn a() {
+fn a() => {
     b +
     c
 }
@@ -1161,99 +1161,99 @@ fn a() {
             ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'");
 
     add_compile_fail_case("goto cause unreachable code", R"SOURCE(
-fn a() {
+fn a() => {
     goto done;
     b();
 done:
     return;
 }
-fn b() {}
+fn b() => {}
     )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code");
 
     add_compile_fail_case("parameter redeclaration", R"SOURCE(
-fn f(a : i32, a : i32) {
+fn f(a : i32, a : i32) => {
 }
     )SOURCE", 1, ".tmp_source.zig:2:1: error: redeclaration of parameter 'a'");
 
     add_compile_fail_case("local variable redeclaration", R"SOURCE(
-fn f() {
+fn f() => {
     const a : i32 = 0;
     const a = 0;
 }
     )SOURCE", 1, ".tmp_source.zig:4:5: error: redeclaration of variable 'a'");
 
     add_compile_fail_case("local variable redeclares parameter", R"SOURCE(
-fn f(a : i32) {
+fn f(a : i32) => {
     const a = 0;
 }
     )SOURCE", 1, ".tmp_source.zig:3:5: error: redeclaration of variable 'a'");
 
     add_compile_fail_case("variable has wrong type", R"SOURCE(
-fn f() -> i32 {
+fn f() i32 => {
     const a = c"a";
     a
 }
     )SOURCE", 1, ".tmp_source.zig:2:15: error: expected type 'i32', got '&const u8'");
 
     add_compile_fail_case("if condition is bool, not int", R"SOURCE(
-fn f() {
+fn f() => {
     if (0) {}
 }
     )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got '(u8 literal)'");
 
     add_compile_fail_case("assign unreachable", R"SOURCE(
-fn f() {
+fn f() => {
     const a = return;
 }
     )SOURCE", 1, ".tmp_source.zig:3:5: error: variable initialization is unreachable");
 
     add_compile_fail_case("unreachable variable", R"SOURCE(
-fn f() {
+fn f() => {
     const a : unreachable = return;
 }
     )SOURCE", 1, ".tmp_source.zig:3:15: error: variable of type 'unreachable' not allowed");
 
     add_compile_fail_case("unreachable parameter", R"SOURCE(
-fn f(a : unreachable) {}
+fn f(a : unreachable) => {}
     )SOURCE", 1, ".tmp_source.zig:2:10: error: parameter of type 'unreachable' not allowed");
 
     add_compile_fail_case("exporting a void parameter", R"SOURCE(
-export fn f(a : void) {}
+export fn f(a : void) => {}
     )SOURCE", 1, ".tmp_source.zig:2:17: error: parameter of type 'void' not allowed on exported functions");
 
     add_compile_fail_case("unused label", R"SOURCE(
-fn f() {
+fn f() => {
 a_label:
 }
     )SOURCE", 1, ".tmp_source.zig:3:1: error: label 'a_label' defined but not used");
 
     add_compile_fail_case("bad assignment target", R"SOURCE(
-fn f() {
+fn f() => {
     3 = 3;
 }
     )SOURCE", 1, ".tmp_source.zig:3:5: error: invalid assignment target");
 
     add_compile_fail_case("assign to constant variable", R"SOURCE(
-fn f() {
+fn f() => {
     const a = 3;
     a = 4;
 }
     )SOURCE", 1, ".tmp_source.zig:4:5: error: cannot assign to constant");
 
     add_compile_fail_case("use of undeclared identifier", R"SOURCE(
-fn f() {
+fn f() => {
     b = 3;
 }
     )SOURCE", 1, ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'");
 
     add_compile_fail_case("const is a statement, not an expression", R"SOURCE(
-fn f() {
+fn f() => {
     (const a = 0);
 }
     )SOURCE", 1, ".tmp_source.zig:3:6: error: invalid token: 'const'");
 
     add_compile_fail_case("array access errors", R"SOURCE(
-fn f() {
+fn f() => {
     var bad : bool;
     i[i] = i[i];
     bad[bad] = bad[bad];
@@ -1268,19 +1268,19 @@ fn f() {
                  ".tmp_source.zig:5:20: error: expected type 'usize', got 'bool'");
 
     add_compile_fail_case("variadic functions only allowed in extern", R"SOURCE(
-fn f(...) {}
+fn f(...) => {}
     )SOURCE", 1, ".tmp_source.zig:2:1: error: variadic arguments only allowed in extern functions");
 
     add_compile_fail_case("write to const global variable", R"SOURCE(
 const x : i32 = 99;
-fn f() {
+fn f() => {
     x = 1;
 }
     )SOURCE", 1, ".tmp_source.zig:4:5: error: cannot assign to constant");
 
 
     add_compile_fail_case("missing else clause", R"SOURCE(
-fn f() {
+fn f() => {
     const x : i32 = if (true) { 1 };
     const y = if (true) { 1 as i32 };
 }
@@ -1299,7 +1299,7 @@ struct C { a : A, }
 
     add_compile_fail_case("invalid struct field", R"SOURCE(
 struct A { x : i32, }
-fn f() {
+fn f() => {
     var a : A;
     a.foo = 1;
     const y = a.bar;
@@ -1315,7 +1315,7 @@ struct A { y : i32, }
 
     add_compile_fail_case("byvalue struct on exported functions", R"SOURCE(
 struct A { x : i32, }
-export fn f(a : A) {}
+export fn f(a : A) => {}
     )SOURCE", 1, ".tmp_source.zig:3:13: error: byvalue struct parameters not yet supported on exported functions");
 
     add_compile_fail_case("duplicate field in struct value expression", R"SOURCE(
@@ -1324,7 +1324,7 @@ struct A {
     y : i32,
     z : i32,
 }
-fn f() {
+fn f() => {
     const a = A {
         .z = 1,
         .y = 2,
@@ -1340,13 +1340,13 @@ struct A {
     y : i32,
     z : i32,
 }
-fn f() {
+fn f() => {
     const a = A {
         .z = 4,
         .y = 2,
     };
 }
-    )SOURCE", 1, ".tmp_source.zig:8:15: error: missing field: 'x'");
+    )SOURCE", 1, ".tmp_source.zig:8:17: error: missing field: 'x'");
 
     add_compile_fail_case("invalid field in struct value expression", R"SOURCE(
 struct A {
@@ -1354,7 +1354,7 @@ struct A {
     y : i32,
     z : i32,
 }
-fn f() {
+fn f() => {
     const a = A {
         .z = 4,
         .y = 2,
@@ -1364,37 +1364,37 @@ fn f() {
     )SOURCE", 1, ".tmp_source.zig:11:9: error: no member named 'foo' in 'A'");
 
     add_compile_fail_case("invalid break expression", R"SOURCE(
-fn f() {
+fn f() => {
     break;
 }
     )SOURCE", 1, ".tmp_source.zig:3:5: error: 'break' expression outside loop");
 
     add_compile_fail_case("invalid continue expression", R"SOURCE(
-fn f() {
+fn f() => {
     continue;
 }
     )SOURCE", 1, ".tmp_source.zig:3:5: error: 'continue' expression outside loop");
 
     add_compile_fail_case("invalid maybe type", R"SOURCE(
-fn f() {
+fn f() => {
     if (const x ?= true) { }
 }
     )SOURCE", 1, ".tmp_source.zig:3:20: error: expected maybe type");
 
     add_compile_fail_case("cast unreachable", R"SOURCE(
-fn f() -> i32 {
+fn f() i32 => {
     (return 1) as i32
 }
     )SOURCE", 1, ".tmp_source.zig:3:16: error: invalid cast from type 'unreachable' to 'i32'");
 
-    add_compile_fail_case("invalid compiler fn", R"SOURCE(
-fn f() -> #bogus(foo) {
+    add_compile_fail_case("invalid builtin fn", R"SOURCE(
+fn f() @bogus(foo) => {
 }
-    )SOURCE", 1, ".tmp_source.zig:2:11: error: invalid compiler function: 'bogus'");
+    )SOURCE", 1, ".tmp_source.zig:2:8: error: invalid builtin function: 'bogus'");
 
     add_compile_fail_case("top level decl dependency loop", R"SOURCE(
-const a : #typeof(b) = 0;
-const b : #typeof(a) = 0;
+const a : @typeof(b) = 0;
+const b : @typeof(a) = 0;
     )SOURCE", 1, ".tmp_source.zig:3:19: error: use of undeclared identifier 'a'");
 }