Commit e411467e1d

Andrew Kelley <superjoe30@gmail.com>
2015-12-14 10:46:37
add number literal type
it gets implicitly casted to whatever is needed. closes #24
1 parent 3d8eb10
doc/langref.md
@@ -200,10 +200,11 @@ as
 
 ### Numbers
 
- Number literals | Example     | Exponentiation
--------------------------------------------
- Decimal integer | 98222       | N/A
- Hex integer     | 0xff        | N/A
- Octal integer   | 0o77        | N/A
- Binary integer  | 0b11110000  | N/A
- Floating-point  | 123.0E+77   | Optional
+ Number literals    | Example     | Exponentiation
+--------------------------------------------------
+ Decimal integer    | 98222       | N/A
+ Hex integer        | 0xff        | N/A
+ Octal integer      | 0o77        | N/A
+ Binary integer     | 0b11110000  | N/A
+ Floating-point     | 123.0E+77   | Optional
+ Hex floating point | TODO        | TODO
example/arrays/arrays.zig
@@ -1,15 +1,11 @@
 export executable "arrays";
 
-#link("c")
-extern {
-    fn puts(s: *const u8) -> i32;
-    fn exit(code: i32) -> unreachable;
-}
+use "std.zig";
 
-export fn _start() -> unreachable {
+export fn main(argc: isize, argv: *mut *mut u8, env: *mut *mut u8) -> i32 {
     let mut array : [i32; 5];
 
-    let mut i = 0;
+    let mut i : i32 = 0;
 loop_start:
     if i == 5 {
         goto loop_end;
@@ -21,21 +17,22 @@ loop_start:
 loop_end:
 
     i = 0;
-    let mut accumulator = 0;
+    let mut accumulator : i32 = 0;
 loop_2_start:
     if i == 5 {
         goto loop_2_end;
     }
 
-    accumulator = accumulator + array[i];
+    accumulator += array[i];
 
     i = i + 1;
     goto loop_2_start;
 loop_2_end:
 
     if accumulator == 15 {
-        puts("OK");
+        print_str("OK" as string);
     }
 
-    exit(0);
+
+    return 0;
 }
example/expressions/expressions.zig
@@ -14,19 +14,19 @@ fn other_exit() -> unreachable {
 
 export fn _start() -> unreachable {
     let a : i32 = 1;
-    let b = 2;
+    let b = 2 as i32;
     // let c : i32; // not yet support for const variables
     // let d; // parse error
     if (a + b == 3) {
-        let no_conflict = 5;
-        if (no_conflict == 5) { puts("OK 1"); }
+        let no_conflict : i32 = 5;
+        if (no_conflict == 5) { puts(c"OK 1"); }
     }
 
     let c = {
-        let no_conflict = 10;
+        let no_conflict : i32 = 10;
         no_conflict
     };
-    if (c == 10) { puts("OK 2"); }
+    if (c == 10) { puts(c"OK 2"); }
 
     void_fun(1, void, 2);
 
@@ -44,12 +44,12 @@ fn void_fun(a : i32, b : void, c : i32) -> void {
 }
 
 fn test_mutable_vars() {
-    let mut i = 0;
+    let mut i : i32 = 0;
 loop_start:
     if i == 3 {
         goto done;
     }
-    puts("loop");
+    puts(c"loop");
     i = i + 1;
     goto loop_start;
 done:
example/hello_world/hello_libc.zig
@@ -7,6 +7,6 @@ extern {
 }
 
 export fn _start() -> unreachable {
-    printf("Hello, world!\n");
+    printf(c"Hello, world!\n");
     exit(0);
 }
example/multiple_files/foo.zig
@@ -3,7 +3,7 @@ use "libc.zig";
 // purposefully conflicting function with main.zig
 // but it's private so it should be OK
 fn private_function() {
-    puts("it works!");
+    puts(c"it works!");
 }
 
 pub fn print_text() {
example/multiple_files/libc.zig
@@ -1,5 +1,5 @@
 #link("c")
 extern {
-    pub fn puts(s: *mut u8) -> i32;
+    pub fn puts(s: *const u8) -> i32;
     pub fn exit(code: i32) -> unreachable;
 }
example/structs/structs.zig
@@ -22,6 +22,6 @@ struct Foo {
 
 fn test_foo(foo : Foo) {
     if foo.b {
-        print_str("OK" as string);
+        print_str("OK\n" as string);
     }
 }
src/analyze.cpp
@@ -122,7 +122,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
     }
 }
 
-static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, int array_size) {
+static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) {
     auto existing_entry = child_type->arrays_by_size.maybe_get(array_size);
     if (existing_entry) {
         return existing_entry->value;
@@ -130,7 +130,7 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
         entry->type_ref = LLVMArrayType(child_type->type_ref, array_size);
         buf_resize(&entry->name, 0);
-        buf_appendf(&entry->name, "[%s; %d]", buf_ptr(&child_type->name), array_size);
+        buf_appendf(&entry->name, "[%s; %" PRIu64 "]", buf_ptr(&child_type->name), array_size);
 
         entry->size_in_bits = child_type->size_in_bits * array_size;
         entry->align_in_bits = child_type->align_in_bits;
@@ -145,11 +145,6 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in
     }
 }
 
-static int parse_int(Buf *number) {
-    // TODO: think about integer size of array sizes
-    return atoi(buf_ptr(number));
-}
-
 static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeType);
     alloc_codegen_node(node);
@@ -192,16 +187,15 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
                 }
 
                 AstNode *size_node = node->data.type.array_size;
-                int size; // TODO: think about integer size of array sizes
-                if (size_node->type != NodeTypeNumberLiteral) {
-                    add_node_error(g, size_node,
-                        buf_create_from_str("array size must be literal number"));
-                    size = -1;
+                if (size_node->type == NodeTypeNumberLiteral &&
+                    is_num_lit_unsigned(size_node->data.number_literal.kind))
+                {
+                    type_node->entry = get_array_type(g, child_type, size_node->data.number_literal.data.x_uint);
                 } else {
-                    size = parse_int(&size_node->data.number);
+                    add_node_error(g, size_node,
+                        buf_create_from_str("array size must be literal unsigned integer"));
+                    type_node->entry = g->builtin_types.entry_invalid;
                 }
-
-                type_node->entry = get_array_type(g, child_type, size);
                 return type_node->entry;
             }
     }
@@ -625,6 +619,44 @@ static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructF
     *out_i = -1;
 }
 
+static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) {
+    NumLit num_lit = literal_type->data.num_lit.kind;
+    uint64_t lit_size_in_bits = num_lit_bit_count(num_lit);
+
+    switch (other_type->id) {
+        case TypeTableEntryIdInvalid:
+        case TypeTableEntryIdNumberLiteral:
+            zig_unreachable();
+        case TypeTableEntryIdVoid:
+        case TypeTableEntryIdBool:
+        case TypeTableEntryIdUnreachable:
+        case TypeTableEntryIdPointer:
+        case TypeTableEntryIdArray:
+        case TypeTableEntryIdStruct:
+            return false;
+        case TypeTableEntryIdInt:
+            if (is_num_lit_signed(num_lit)) {
+                if (!other_type->data.integral.is_signed) {
+                    return false;
+                }
+
+                return lit_size_in_bits <= other_type->size_in_bits;
+            } else if (is_num_lit_unsigned(num_lit)) {
+
+                return lit_size_in_bits <= other_type->size_in_bits;
+            } else {
+                return false;
+            }
+        case TypeTableEntryIdFloat:
+            if (is_num_lit_float(num_lit)) {
+                return lit_size_in_bits <= other_type->size_in_bits;
+            } else {
+                return false;
+            }
+    }
+}
+
+
 static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         AstNode *node)
 {
@@ -790,6 +822,16 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
         cast_node->op = CastOpArrayToString;
         context->cast_expr_alloca_list.append(node);
         return wanted_type;
+    } else if (actual_type->id == TypeTableEntryIdNumberLiteral &&
+               num_lit_fits_in_other_type(g, actual_type, wanted_type))
+    {
+        AstNode *literal_node = node->data.cast_expr.expr;
+        assert(literal_node->codegen_node);
+        NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node;
+        assert(!codegen_num_lit->resolved_type);
+        codegen_num_lit->resolved_type = wanted_type;
+        cast_node->op = CastOpNothing;
+        return wanted_type;
     } else {
         add_node_error(g, node,
             buf_sprintf("invalid cast from type '%s' to '%s'",
@@ -799,6 +841,314 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
     }
 }
 
+static TypeTableEntry * resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node,
+        TypeTableEntry *non_literal_type, AstNode *literal_node, TypeTableEntry *literal_type)
+{
+    assert(literal_node->codegen_node);
+    NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node;
+
+    if (num_lit_fits_in_other_type(g, literal_type, non_literal_type)) {
+        assert(!codegen_num_lit->resolved_type);
+        codegen_num_lit->resolved_type = non_literal_type;
+        return non_literal_type;
+    } else {
+        return nullptr;
+    }
+}
+
+static TypeTableEntry * resolve_number_literals(CodeGen *g, AstNode *node1, AstNode *node2) {
+    TypeTableEntry *type1 = node1->codegen_node->expr_node.type_entry;
+    TypeTableEntry *type2 = node2->codegen_node->expr_node.type_entry;
+
+    if (type1->id == TypeTableEntryIdNumberLiteral &&
+        type2->id == TypeTableEntryIdNumberLiteral)
+    {
+        assert(node1->codegen_node);
+        assert(node2->codegen_node);
+
+        NumberLiteralNode *codegen_num_lit_1 = &node1->codegen_node->data.num_lit_node;
+        NumberLiteralNode *codegen_num_lit_2 = &node2->codegen_node->data.num_lit_node;
+
+        assert(!codegen_num_lit_1->resolved_type);
+        assert(!codegen_num_lit_2->resolved_type);
+
+        if (is_num_lit_float(type1->data.num_lit.kind) &&
+            is_num_lit_float(type2->data.num_lit.kind))
+        {
+            codegen_num_lit_1->resolved_type = g->builtin_types.entry_f64;
+            codegen_num_lit_2->resolved_type = g->builtin_types.entry_f64;
+            return g->builtin_types.entry_f64;
+        } else if (is_num_lit_signed(type1->data.num_lit.kind) &&
+                   is_num_lit_signed(type2->data.num_lit.kind))
+        {
+            codegen_num_lit_1->resolved_type = g->builtin_types.entry_i64;
+            codegen_num_lit_2->resolved_type = g->builtin_types.entry_i64;
+            return g->builtin_types.entry_i64;
+        } else if (is_num_lit_unsigned(type1->data.num_lit.kind) &&
+                   is_num_lit_unsigned(type2->data.num_lit.kind))
+        {
+            codegen_num_lit_1->resolved_type = g->builtin_types.entry_u64;
+            codegen_num_lit_2->resolved_type = g->builtin_types.entry_u64;
+            return g->builtin_types.entry_u64;
+        } else {
+            return nullptr;
+        }
+    } else if (type1->id == TypeTableEntryIdNumberLiteral) {
+        return resolve_rhs_number_literal(g, node2, type2, node1, type1);
+    } else {
+        assert(type2->id == TypeTableEntryIdNumberLiteral);
+        return resolve_rhs_number_literal(g, node1, type1, node2, type2);
+    }
+}
+
+static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node)
+{
+    switch (node->data.bin_op_expr.bin_op) {
+        case BinOpTypeAssign:
+        case BinOpTypeAssignTimes:
+        case BinOpTypeAssignDiv:
+        case BinOpTypeAssignMod:
+        case BinOpTypeAssignPlus:
+        case BinOpTypeAssignMinus:
+        case BinOpTypeAssignBitShiftLeft:
+        case BinOpTypeAssignBitShiftRight:
+        case BinOpTypeAssignBitAnd:
+        case BinOpTypeAssignBitXor:
+        case BinOpTypeAssignBitOr:
+        case BinOpTypeAssignBoolAnd:
+        case BinOpTypeAssignBoolOr:
+            {
+                AstNode *lhs_node = node->data.bin_op_expr.op1;
+                TypeTableEntry *expected_rhs_type = nullptr;
+                if (lhs_node->type == NodeTypeSymbol) {
+                    Buf *name = &lhs_node->data.symbol;
+                    LocalVariableTableEntry *var = find_local_variable(context, name);
+                    if (var) {
+                        if (var->is_const) {
+                            add_node_error(g, lhs_node,
+                                buf_sprintf("cannot assign to constant variable"));
+                        } else {
+                            if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) {
+                                if (var->type->id != TypeTableEntryIdInvalid) {
+                                    add_node_error(g, lhs_node,
+                                        buf_sprintf("operator not allowed for type '%s'",
+                                            buf_ptr(&var->type->name)));
+                                }
+                            } else {
+                                expected_rhs_type = var->type;
+                            }
+                        }
+                    } else {
+                        add_node_error(g, lhs_node,
+                                buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
+                    }
+                } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
+                    expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node);
+                } else if (lhs_node->type == NodeTypeFieldAccessExpr) {
+                    alloc_codegen_node(lhs_node);
+                    expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node);
+                } else {
+                    add_node_error(g, lhs_node,
+                            buf_sprintf("assignment target must be variable, field, or array element"));
+                }
+                analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);
+                return g->builtin_types.entry_void;
+            }
+        case BinOpTypeBoolOr:
+        case BinOpTypeBoolAnd:
+            analyze_expression(g, import, context, g->builtin_types.entry_bool,
+                    node->data.bin_op_expr.op1);
+            analyze_expression(g, import, context, g->builtin_types.entry_bool,
+                    node->data.bin_op_expr.op2);
+            return g->builtin_types.entry_bool;
+        case BinOpTypeCmpEq:
+        case BinOpTypeCmpNotEq:
+        case BinOpTypeCmpLessThan:
+        case BinOpTypeCmpGreaterThan:
+        case BinOpTypeCmpLessOrEq:
+        case BinOpTypeCmpGreaterOrEq:
+            {
+                AstNode *op1 = node->data.bin_op_expr.op1;
+                AstNode *op2 = node->data.bin_op_expr.op2;
+                TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1);
+                TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2);
+                bool cmp_ok = false;
+                if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) {
+                    cmp_ok = true;
+                } else if (lhs_type->id == TypeTableEntryIdNumberLiteral ||
+                           rhs_type->id == TypeTableEntryIdNumberLiteral)
+                {
+                    cmp_ok = resolve_number_literals(g, op1, op2);
+                } else if (lhs_type->id == TypeTableEntryIdInt) {
+                    if (rhs_type->id == TypeTableEntryIdInt &&
+                        lhs_type->data.integral.is_signed == rhs_type->data.integral.is_signed &&
+                        lhs_type->size_in_bits == rhs_type->size_in_bits)
+                    {
+                        cmp_ok = true;
+                    }
+                } else if (lhs_type->id == TypeTableEntryIdFloat) {
+                    if (rhs_type->id == TypeTableEntryIdFloat &&
+                        lhs_type->size_in_bits == rhs_type->size_in_bits)
+                    {
+                        cmp_ok = true;
+                    }
+                }
+                if (!cmp_ok) {
+                    add_node_error(g, node, buf_sprintf("unable to compare '%s' with '%s'",
+                            buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
+                }
+                return g->builtin_types.entry_bool;
+            }
+        case BinOpTypeBinOr:
+        case BinOpTypeBinXor:
+        case BinOpTypeBinAnd:
+            {
+                // TODO: don't require i32
+                analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
+                analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
+                return g->builtin_types.entry_i32;
+            }
+        case BinOpTypeBitShiftLeft:
+        case BinOpTypeBitShiftRight:
+            {
+                // TODO: don't require i32
+                analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
+                analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
+                return g->builtin_types.entry_i32;
+            }
+        case BinOpTypeAdd:
+        case BinOpTypeSub:
+            {
+                AstNode *op1 = node->data.bin_op_expr.op1;
+                AstNode *op2 = node->data.bin_op_expr.op2;
+                TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1);
+                TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2);
+
+                TypeTableEntry *return_type = nullptr;
+
+                if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) {
+                    return_type = g->builtin_types.entry_invalid;
+                } else if (lhs_type->id == TypeTableEntryIdNumberLiteral ||
+                           rhs_type->id == TypeTableEntryIdNumberLiteral)
+                {
+                    return_type = resolve_number_literals(g, op1, op2);
+                } else if (lhs_type->id == TypeTableEntryIdInt &&
+                           lhs_type == rhs_type)
+                {
+                    return_type = lhs_type;
+                } else if (lhs_type->id == TypeTableEntryIdFloat &&
+                           lhs_type == rhs_type)
+                {
+                    return_type = lhs_type;
+                }
+                if (!return_type) {
+                    if (node->data.bin_op_expr.bin_op == BinOpTypeAdd) {
+                        add_node_error(g, node, buf_sprintf("unable to add '%s' and '%s'",
+                                buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
+                    } else {
+                        add_node_error(g, node, buf_sprintf("unable to subtract '%s' and '%s'",
+                                buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
+                    }
+                    return g->builtin_types.entry_invalid;
+                }
+                return return_type;
+            }
+        case BinOpTypeMult:
+        case BinOpTypeDiv:
+        case BinOpTypeMod:
+            {
+                // TODO: don't require i32
+                analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
+                analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
+                return g->builtin_types.entry_i32;
+            }
+        case BinOpTypeInvalid:
+            zig_unreachable();
+    }
+    zig_unreachable();
+}
+
+static TypeTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node)
+{
+    AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
+
+    TypeTableEntry *explicit_type = nullptr;
+    if (variable_declaration->type != nullptr) {
+        explicit_type = resolve_type(g, variable_declaration->type);
+        if (explicit_type->id == TypeTableEntryIdUnreachable) {
+            add_node_error(g, variable_declaration->type,
+                buf_sprintf("variable of type 'unreachable' not allowed"));
+            explicit_type = g->builtin_types.entry_invalid;
+        }
+    }
+
+    TypeTableEntry *implicit_type = nullptr;
+    if (variable_declaration->expr != nullptr) {
+        implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr);
+        if (implicit_type->id == TypeTableEntryIdUnreachable) {
+            add_node_error(g, node,
+                buf_sprintf("variable initialization is unreachable"));
+            implicit_type = g->builtin_types.entry_invalid;
+        } else if (implicit_type->id == TypeTableEntryIdNumberLiteral) {
+            add_node_error(g, node,
+                buf_sprintf("unable to infer variable type"));
+            implicit_type = g->builtin_types.entry_invalid;
+        }
+    }
+
+    if (implicit_type == nullptr && variable_declaration->is_const) {
+        add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'."));
+        implicit_type = g->builtin_types.entry_invalid;
+    }
+
+    TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
+    assert(type != nullptr); // should have been caught by the parser
+
+    LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol);
+    if (existing_variable) {
+        add_node_error(g, node,
+            buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol)));
+    } else {
+        LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
+        buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol);
+        variable_entry->type = type;
+        variable_entry->is_const = variable_declaration->is_const;
+        variable_entry->is_ptr = true;
+        variable_entry->decl_node = node;
+        context->variable_table.put(&variable_entry->name, variable_entry);
+    }
+    return g->builtin_types.entry_void;
+}
+
+static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node)
+{
+    TypeTableEntry *num_lit_type = g->num_lit_types[node->data.number_literal.kind];
+    if (node->data.number_literal.overflow) {
+        add_node_error(g, node,
+                buf_sprintf("number literal too large to be represented in any type"));
+        return g->builtin_types.entry_invalid;
+    } else if (expected_type) {
+        if (expected_type->id == TypeTableEntryIdInvalid) {
+            return g->builtin_types.entry_invalid;
+        } else if (num_lit_fits_in_other_type(g, num_lit_type, expected_type)) {
+            NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node;
+            assert(!codegen_num_lit->resolved_type);
+            codegen_num_lit->resolved_type = expected_type;
+
+            return expected_type;
+        } else {
+            add_node_error(g, node, buf_sprintf("expected type '%s', got '%s'",
+                        buf_ptr(&expected_type->name), buf_ptr(&num_lit_type->name)));
+            return g->builtin_types.entry_invalid;
+        }
+    } else {
+        return num_lit_type;
+    }
+}
+
 static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
@@ -854,51 +1204,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                 break;
             }
         case NodeTypeVariableDeclaration:
-            {
-                AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;;
-
-                TypeTableEntry *explicit_type = nullptr;
-                if (variable_declaration->type != nullptr) {
-                    explicit_type = resolve_type(g, variable_declaration->type);
-                    if (explicit_type->id == TypeTableEntryIdUnreachable) {
-                        add_node_error(g, variable_declaration->type,
-                            buf_sprintf("variable of type 'unreachable' not allowed"));
-                    }
-                }
-
-                TypeTableEntry *implicit_type = nullptr;
-                if (variable_declaration->expr != nullptr) {
-                    implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr);
-                    if (implicit_type->id == TypeTableEntryIdUnreachable) {
-                        add_node_error(g, node,
-                            buf_sprintf("variable initialization is unreachable"));
-                    }
-                }
-
-                if (implicit_type == nullptr && variable_declaration->is_const) {
-                    add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'."));
-                }
-
-                TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
-                assert(type != nullptr); // should have been caught by the parser
-
-                LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol);
-                if (existing_variable) {
-                    add_node_error(g, node,
-                        buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol)));
-                } else {
-                    LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
-                    buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol);
-                    variable_entry->type = type;
-                    variable_entry->is_const = variable_declaration->is_const;
-                    variable_entry->is_ptr = true;
-                    variable_entry->decl_node = node;
-                    context->variable_table.put(&variable_entry->name, variable_entry);
-                }
-                return_type = g->builtin_types.entry_void;
-                break;
-            }
-
+            return_type = analyze_variable_declaration(g, import, context, expected_type, node);
+            break;
         case NodeTypeGoto:
             {
                 FnTableEntry *fn_table_entry = get_context_fn_entry(context);
@@ -928,120 +1235,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
                 break;
             }
         case NodeTypeBinOpExpr:
-            {
-                switch (node->data.bin_op_expr.bin_op) {
-                    case BinOpTypeAssign:
-                    case BinOpTypeAssignTimes:
-                    case BinOpTypeAssignDiv:
-                    case BinOpTypeAssignMod:
-                    case BinOpTypeAssignPlus:
-                    case BinOpTypeAssignMinus:
-                    case BinOpTypeAssignBitShiftLeft:
-                    case BinOpTypeAssignBitShiftRight:
-                    case BinOpTypeAssignBitAnd:
-                    case BinOpTypeAssignBitXor:
-                    case BinOpTypeAssignBitOr:
-                    case BinOpTypeAssignBoolAnd:
-                    case BinOpTypeAssignBoolOr:
-                        {
-                            AstNode *lhs_node = node->data.bin_op_expr.op1;
-                            TypeTableEntry *expected_rhs_type = nullptr;
-                            if (lhs_node->type == NodeTypeSymbol) {
-                                Buf *name = &lhs_node->data.symbol;
-                                LocalVariableTableEntry *var = find_local_variable(context, name);
-                                if (var) {
-                                    if (var->is_const) {
-                                        add_node_error(g, lhs_node,
-                                            buf_sprintf("cannot assign to constant variable"));
-                                    } else {
-                                        if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) {
-                                            add_node_error(g, lhs_node,
-                                                buf_sprintf("operator not allowed for type '%s'", buf_ptr(&var->type->name)));
-                                        } else {
-                                            expected_rhs_type = var->type;
-                                        }
-                                    }
-                                } else {
-                                    add_node_error(g, lhs_node,
-                                            buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
-                                }
-                            } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
-                                expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node);
-                            } else if (lhs_node->type == NodeTypeFieldAccessExpr) {
-                                alloc_codegen_node(lhs_node);
-                                expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node);
-                            } else {
-                                add_node_error(g, lhs_node,
-                                        buf_sprintf("assignment target must be variable, field, or array element"));
-                            }
-                            analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);
-                            return_type = g->builtin_types.entry_void;
-                            break;
-                        }
-                    case BinOpTypeBoolOr:
-                    case BinOpTypeBoolAnd:
-                        analyze_expression(g, import, context, g->builtin_types.entry_bool,
-                                node->data.bin_op_expr.op1);
-                        analyze_expression(g, import, context, g->builtin_types.entry_bool,
-                                node->data.bin_op_expr.op2);
-                        return_type = g->builtin_types.entry_bool;
-                        break;
-                    case BinOpTypeCmpEq:
-                    case BinOpTypeCmpNotEq:
-                    case BinOpTypeCmpLessThan:
-                    case BinOpTypeCmpGreaterThan:
-                    case BinOpTypeCmpLessOrEq:
-                    case BinOpTypeCmpGreaterOrEq:
-                        // TODO think how should type checking for these work?
-                        analyze_expression(g, import, context, g->builtin_types.entry_i32,
-                                node->data.bin_op_expr.op1);
-                        analyze_expression(g, import, context, g->builtin_types.entry_i32,
-                                node->data.bin_op_expr.op2);
-                        return_type = g->builtin_types.entry_bool;
-                        break;
-                    case BinOpTypeBinOr:
-                    case BinOpTypeBinXor:
-                    case BinOpTypeBinAnd:
-                        {
-                            // TODO: don't require i32
-                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
-                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
-                            return_type = g->builtin_types.entry_i32;
-                            break;
-                        }
-                    case BinOpTypeBitShiftLeft:
-                    case BinOpTypeBitShiftRight:
-                        {
-                            // TODO: don't require i32
-                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
-                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
-                            return_type = g->builtin_types.entry_i32;
-                            break;
-                        }
-                    case BinOpTypeAdd:
-                    case BinOpTypeSub:
-                        // TODO think how should type checking for these work?
-                        analyze_expression(g, import, context, g->builtin_types.entry_i32,
-                                node->data.bin_op_expr.op1);
-                        analyze_expression(g, import, context, g->builtin_types.entry_i32,
-                                node->data.bin_op_expr.op2);
-                        return_type = g->builtin_types.entry_i32;
-                        break;
-                    case BinOpTypeMult:
-                    case BinOpTypeDiv:
-                    case BinOpTypeMod:
-                        {
-                            // TODO: don't require i32
-                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
-                            analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
-                            return_type = g->builtin_types.entry_i32;
-                            break;
-                        }
-                    case BinOpTypeInvalid:
-                        zig_unreachable();
-                }
-                break;
-            }
+            return_type = analyze_bin_op_expr(g, import, context, expected_type, node);
+            break;
 
         case NodeTypeFnCallExpr:
             {
@@ -1116,10 +1311,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
             return_type = analyze_field_access_expr(g, import, context, node);
             break;
         case NodeTypeNumberLiteral:
-            // TODO: generic literal int type
-            return_type = g->builtin_types.entry_i32;
+            return_type = analyze_number_literal_expr(g, import, context, expected_type, node);
             break;
-
         case NodeTypeStringLiteral:
             if (node->data.string_literal.c) {
                 return_type = g->builtin_types.entry_c_string_literal;
src/analyze.hpp
@@ -42,6 +42,10 @@ struct TypeTableEntryStruct {
     TypeStructField *fields;
 };
 
+struct TypeTableEntryNumLit {
+    NumLit kind;
+};
+
 enum TypeTableEntryId {
     TypeTableEntryIdInvalid,
     TypeTableEntryIdVoid,
@@ -52,6 +56,7 @@ enum TypeTableEntryId {
     TypeTableEntryIdPointer,
     TypeTableEntryIdArray,
     TypeTableEntryIdStruct,
+    TypeTableEntryIdNumberLiteral,
 };
 
 struct TypeTableEntry {
@@ -69,12 +74,13 @@ struct TypeTableEntry {
         TypeTableEntryInt integral;
         TypeTableEntryArray array;
         TypeTableEntryStruct structure;
+        TypeTableEntryNumLit num_lit;
     } data;
 
     // use these fields to make sure we don't duplicate type table entries for the same type
     TypeTableEntry *pointer_const_parent;
     TypeTableEntry *pointer_mut_parent;
-    HashMap<int, TypeTableEntry *, int_hash, int_eq> arrays_by_size;
+    HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size;
 
 };
 
@@ -134,10 +140,13 @@ struct CodeGen {
     struct {
         TypeTableEntry *entry_bool;
         TypeTableEntry *entry_u8;
+        TypeTableEntry *entry_u64;
         TypeTableEntry *entry_i32;
+        TypeTableEntry *entry_i64;
         TypeTableEntry *entry_isize;
         TypeTableEntry *entry_usize;
         TypeTableEntry *entry_f32;
+        TypeTableEntry *entry_f64;
         TypeTableEntry *entry_c_string_literal;
         TypeTableEntry *entry_string;
         TypeTableEntry *entry_void;
@@ -145,6 +154,8 @@ struct CodeGen {
         TypeTableEntry *entry_invalid;
     } builtin_types;
 
+    TypeTableEntry *num_lit_types[NumLitCount];
+
     LLVMTargetDataRef target_data_ref;
     unsigned pointer_size_bytes;
     bool is_static;
@@ -242,6 +253,7 @@ enum CastOp {
     CastOpPtrToInt,
     CastOpIntWidenOrShorten,
     CastOpArrayToString,
+    CastOpNothing,
 };
 
 struct CastNode {
@@ -251,6 +263,10 @@ struct CastNode {
     LLVMValueRef ptr;
 };
 
+struct NumberLiteralNode {
+    TypeTableEntry *resolved_type;
+};
+
 struct CodeGenNode {
     union {
         TypeNode type_node; // for NodeTypeType
@@ -262,6 +278,7 @@ struct CodeGenNode {
         StructDeclNode struct_decl_node; // for NodeTypeStructDecl
         FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr
         CastNode cast_node; // for NodeTypeCastExpr
+        NumberLiteralNode num_lit_node; // for NodeTypeNumberLiteral
     } data;
     ExprNode expr_node; // for all the expression nodes
 };
src/codegen.cpp
@@ -279,6 +279,8 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
     CastNode *cast_node = &node->codegen_node->data.cast_node;
 
     switch (cast_node->op) {
+        case CastOpNothing:
+            return expr_val;
         case CastOpPtrToInt:
             return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
         case CastOpIntWidenOrShorten:
@@ -901,11 +903,29 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
             return gen_asm_expr(g, node);
         case NodeTypeNumberLiteral:
             {
-                Buf *number_str = &node->data.number;
-                LLVMTypeRef number_type = LLVMInt32Type();
-                LLVMValueRef number_val = LLVMConstIntOfStringAndSize(number_type,
-                        buf_ptr(number_str), buf_len(number_str), 10);
-                return number_val;
+                NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node;
+                assert(codegen_num_lit);
+                TypeTableEntry *type_entry = codegen_num_lit->resolved_type;
+                assert(type_entry);
+
+                // TODO this is kinda iffy. make sure josh is on board with this
+                node->codegen_node->expr_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,
+                            node->data.number_literal.data.x_uint,
+                            type_entry->data.integral.is_signed);
+                } else if (type_entry->id == TypeTableEntryIdFloat) {
+
+                    return LLVMConstReal(type_entry->type_ref,
+                            node->data.number_literal.data.x_float);
+                } else {
+                    zig_panic("bad number literal type");
+                }
             }
         case NodeTypeStringLiteral:
             {
@@ -1186,6 +1206,20 @@ static void do_code_gen(CodeGen *g) {
 #endif
 }
 
+static const NumLit num_lit_kinds[] = {
+    NumLitF32,
+    NumLitF64,
+    NumLitF128,
+    NumLitI8,
+    NumLitU8,
+    NumLitI16,
+    NumLitU16,
+    NumLitI32,
+    NumLitU32,
+    NumLitI64,
+    NumLitU64,
+};
+
 static void define_builtin_types(CodeGen *g) {
     {
         // if this type is anywhere in the AST, we should never hit codegen.
@@ -1193,6 +1227,19 @@ static void define_builtin_types(CodeGen *g) {
         buf_init_from_str(&entry->name, "(invalid)");
         g->builtin_types.entry_invalid = entry;
     }
+
+    assert(NumLitCount == array_length(num_lit_kinds));
+    for (int i = 0; i < NumLitCount; i += 1) {
+        NumLit num_lit_kind = num_lit_kinds[i];
+        // This type should just create a constant with whatever actual number
+        // type is expected at the time.
+        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumberLiteral);
+        buf_resize(&entry->name, 0);
+        buf_appendf(&entry->name, "(%s literal)", num_lit_str(num_lit_kind));
+        entry->data.num_lit.kind = num_lit_kind;
+        g->num_lit_types[i] = entry;
+    }
+
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool);
         entry->type_ref = LLVMInt1Type();
@@ -1217,6 +1264,19 @@ static void define_builtin_types(CodeGen *g) {
         g->type_table.put(&entry->name, entry);
         g->builtin_types.entry_u8 = entry;
     }
+    {
+        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
+        entry->type_ref = LLVMInt64Type();
+        buf_init_from_str(&entry->name, "u64");
+        entry->size_in_bits = 64;
+        entry->align_in_bits = 64;
+        entry->data.integral.is_signed = false;
+        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
+                entry->size_in_bits, entry->align_in_bits,
+                LLVMZigEncoding_DW_ATE_unsigned());
+        g->type_table.put(&entry->name, entry);
+        g->builtin_types.entry_u64 = entry;
+    }
     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);
@@ -1231,6 +1291,19 @@ static void define_builtin_types(CodeGen *g) {
         g->type_table.put(&entry->name, entry);
         g->builtin_types.entry_i32 = entry;
     }
+    {
+        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
+        entry->type_ref = LLVMInt64Type();
+        buf_init_from_str(&entry->name, "i64");
+        entry->size_in_bits = 64;
+        entry->align_in_bits = 64;
+        entry->data.integral.is_signed = true;
+        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
+                entry->size_in_bits, entry->align_in_bits,
+                LLVMZigEncoding_DW_ATE_signed());
+        g->type_table.put(&entry->name, entry);
+        g->builtin_types.entry_i64 = entry;
+    }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
         entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
@@ -1269,6 +1342,18 @@ static void define_builtin_types(CodeGen *g) {
         g->type_table.put(&entry->name, entry);
         g->builtin_types.entry_f32 = entry;
     }
+    {
+        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
+        entry->type_ref = LLVMFloatType();
+        buf_init_from_str(&entry->name, "f64");
+        entry->size_in_bits = 64;
+        entry->align_in_bits = 64;
+        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
+                entry->size_in_bits, entry->align_in_bits,
+                LLVMZigEncoding_DW_ATE_float());
+        g->type_table.put(&entry->name, entry);
+        g->builtin_types.entry_f64 = entry;
+    }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid);
         entry->type_ref = LLVMVoidType();
src/parseh.cpp
@@ -544,7 +544,7 @@ void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FI
 
     char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS");
     if (ZIG_PARSEH_CFLAGS) {
-        Buf tmp_buf = {0};
+        Buf tmp_buf = BUF_INIT;
         char *start = ZIG_PARSEH_CFLAGS;
         char *space = strstr(start, " ");
         while (space) {
src/parser.cpp
@@ -12,6 +12,7 @@
 #include <stdarg.h>
 #include <stdio.h>
 
+
 static const char *bin_op_str(BinOpType bin_op) {
     switch (bin_op) {
         case BinOpTypeInvalid:             return "(invalid)";
@@ -273,9 +274,19 @@ void ast_print(AstNode *node, int indent) {
             ast_print(node->data.prefix_op_expr.primary_expr, indent + 2);
             break;
         case NodeTypeNumberLiteral:
-            fprintf(stderr, "NumberLiteral %s\n",
-                    buf_ptr(&node->data.number));
-            break;
+            {
+                NumLit num_lit = node->data.number_literal.kind;
+                const char *name = node_type_str(node->type);
+                const char *kind_str = num_lit_str(num_lit);
+                if (is_num_lit_signed(num_lit)) {
+                    fprintf(stderr, "%s %s %" PRId64 "\n", name, kind_str, node->data.number_literal.data.x_int);
+                } else if (is_num_lit_unsigned(num_lit)) {
+                    fprintf(stderr, "%s %s %" PRIu64 "\n", name, kind_str, node->data.number_literal.data.x_uint);
+                } else {
+                    fprintf(stderr, "%s %s %f\n", name, kind_str, node->data.number_literal.data.x_float);
+                }
+                break;
+            }
         case NodeTypeStringLiteral:
             {
                 const char *c = node->data.string_literal.c ? "c" : "";
@@ -574,6 +585,186 @@ static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool
     if (offset_map) offset_map->append(pos);
 }
 
+enum ParseNumLitState {
+    ParseNumLitStateStart,
+    ParseNumLitStateBase,
+    ParseNumLitStateDigits,
+    ParseNumLitStateExpectFirstDigit,
+    ParseNumLitStateDecimal,
+    ParseNumLitStateESign,
+    ParseNumLitStateEDigit,
+};
+
+static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLiteral *num_lit) {
+    ParseNumLitState state = ParseNumLitStateStart;
+    unsigned long long base = 10;
+    bool negative = false;
+    int digits_start;
+    int digits_end;
+    int decimal_start = -1;
+    int decimal_end;
+    bool e_present = false;
+    bool e_positive;
+    int e_digit_start;
+    int e_digit_end;
+
+    for (int i = token->start_pos; i < token->end_pos; i += 1) {
+        uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i);
+        switch (state) {
+            case ParseNumLitStateStart:
+                if (c == '-') {
+                    negative = true;
+                } else if (c == '0') {
+                    state = ParseNumLitStateBase;
+                } else if (c >= '1' && c <= '9') {
+                    digits_start = i;
+                    state = ParseNumLitStateDigits;
+                } else {
+                    zig_unreachable();
+                }
+                break;
+            case ParseNumLitStateBase:
+                if (c == 'x') {
+                    base = 16;
+                    state = ParseNumLitStateExpectFirstDigit;
+                } else if (c == 'o') {
+                    base = 8;
+                    state = ParseNumLitStateExpectFirstDigit;
+                } else if (c == 'b') {
+                    base = 2;
+                    state = ParseNumLitStateExpectFirstDigit;
+                } else {
+                    zig_unreachable();
+                }
+                break;
+
+            case ParseNumLitStateExpectFirstDigit:
+                state = ParseNumLitStateDigits;
+                break;
+
+            case ParseNumLitStateDigits:
+                if (c == '.') {
+                    assert(base == 10);
+                    digits_end = i;
+                    decimal_start = i + 1;
+                    state = ParseNumLitStateDecimal;
+                }
+                break;
+            case ParseNumLitStateDecimal:
+                if (c == 'E') {
+                    e_present = false;
+                    decimal_end = i;
+                    state = ParseNumLitStateESign;
+                }
+                break;
+            case ParseNumLitStateESign:
+                if (c == '+') {
+                    e_positive = true;
+                    e_digit_start = i + 1;
+                    state = ParseNumLitStateEDigit;
+                } else if (c == '-') {
+                    e_positive = false;
+                    e_digit_start = i + 1;
+                    state = ParseNumLitStateEDigit;
+                } else {
+                    zig_unreachable();
+                }
+                break;
+            case ParseNumLitStateEDigit:
+                assert(c >= '0' && c <= '9');
+                break;
+        }
+    }
+
+    switch (state) {
+        case ParseNumLitStateDigits:
+            digits_end = token->end_pos;
+            break;
+        case ParseNumLitStateDecimal:
+            decimal_end = token->end_pos;
+            break;
+        case ParseNumLitStateEDigit:
+            e_digit_end = token->end_pos;
+            break;
+        case ParseNumLitStateBase:
+            num_lit->kind = NumLitU8;
+            num_lit->data.x_uint = 0;
+            return;
+        case ParseNumLitStateESign:
+        case ParseNumLitStateExpectFirstDigit:
+        case ParseNumLitStateStart:
+            zig_unreachable();
+    }
+
+    if (decimal_start >= 0) {
+        // float
+        double x;
+
+        (void)x;
+        zig_panic("TODO parse float");
+    } else {
+        // integer
+        unsigned long long x = 0;
+
+        unsigned long long mult = 1;
+        for (int i = digits_end - 1; ; i -= 1) {
+            uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i);
+            unsigned long long digit = (c - '0');
+
+            // digit *= mult
+            if (__builtin_umulll_overflow(digit, mult, &digit)) {
+                num_lit->overflow = true;
+                return;
+            }
+
+            // x += digit
+            if (__builtin_uaddll_overflow(x, digit, &x)) {
+                num_lit->overflow = true;
+                return;
+            }
+
+            if (i == digits_start)
+                break;
+
+            // mult *= base
+            if (__builtin_umulll_overflow(mult, base, &mult)) {
+                num_lit->overflow = true;
+                return;
+            }
+        }
+
+        if (negative) {
+            if (x <= 128ull) {
+                num_lit->kind = NumLitI8;
+            } else if (x <= 32768ull) {
+                num_lit->kind = NumLitI16;
+            } else if (x <= 2147483648ull) {
+                num_lit->kind = NumLitI32;
+            } else if (x <= 9223372036854775808ull) {
+                num_lit->kind = NumLitI64;
+            } else {
+                num_lit->overflow = true;
+                return;
+            }
+
+            num_lit->data.x_int = -((int64_t)x);
+        } else {
+            num_lit->data.x_uint = x;
+
+            if (x <= UINT8_MAX) {
+                num_lit->kind = NumLitU8;
+            } else if (x <= UINT16_MAX) {
+                num_lit->kind = NumLitU16;
+            } else if (x <= UINT32_MAX) {
+                num_lit->kind = NumLitU32;
+            } else {
+                num_lit->kind = NumLitU64;
+            }
+        }
+    }
+}
+
+
 __attribute__ ((noreturn))
 static void ast_invalid_token_error(ParseContext *pc, Token *token) {
     Buf token_value = BUF_INIT;
@@ -829,7 +1020,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
 
     if (token->id == TokenIdNumberLiteral) {
         AstNode *node = ast_create_node(pc, NodeTypeNumberLiteral, token);
-        ast_buf_from_token(pc, token, &node->data.number);
+        parse_number_literal(pc, token, &node->data.number_literal);
         *token_index += 1;
         return node;
     } else if (token->id == TokenIdStringLiteral) {
@@ -2152,3 +2343,121 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, Er
     pc.root = ast_parse_root(&pc, &token_index);
     return pc.root;
 }
+
+const char *num_lit_str(NumLit num_lit) {
+    switch (num_lit) {
+        case NumLitF32:
+            return "f32";
+        case NumLitF64:
+            return "f64";
+        case NumLitF128:
+            return "f128";
+        case NumLitI8:
+            return "i8";
+        case NumLitI16:
+            return "i16";
+        case NumLitI32:
+            return "i32";
+        case NumLitI64:
+            return "i64";
+        case NumLitU8:
+            return "u8";
+        case NumLitU16:
+            return "u16";
+        case NumLitU32:
+            return "u32";
+        case NumLitU64:
+            return "u64";
+        case NumLitCount:
+            zig_unreachable();
+    }
+    zig_unreachable();
+}
+
+bool is_num_lit_signed(NumLit num_lit) {
+    switch (num_lit) {
+        case NumLitI8:
+        case NumLitI16:
+        case NumLitI32:
+        case NumLitI64:
+            return true;
+
+        case NumLitF32:
+        case NumLitF64:
+        case NumLitF128:
+        case NumLitU8:
+        case NumLitU16:
+        case NumLitU32:
+        case NumLitU64:
+            return false;
+        case NumLitCount:
+            zig_unreachable();
+    }
+    zig_unreachable();
+}
+
+bool is_num_lit_unsigned(NumLit num_lit) {
+    switch (num_lit) {
+        case NumLitF32:
+        case NumLitF64:
+        case NumLitF128:
+        case NumLitI8:
+        case NumLitI16:
+        case NumLitI32:
+        case NumLitI64:
+            return false;
+        case NumLitU8:
+        case NumLitU16:
+        case NumLitU32:
+        case NumLitU64:
+            return true;
+        case NumLitCount:
+            zig_unreachable();
+    }
+    zig_unreachable();
+}
+
+bool is_num_lit_float(NumLit num_lit) {
+    switch (num_lit) {
+        case NumLitF32:
+        case NumLitF64:
+        case NumLitF128:
+            return true;
+        case NumLitI8:
+        case NumLitI16:
+        case NumLitI32:
+        case NumLitI64:
+        case NumLitU8:
+        case NumLitU16:
+        case NumLitU32:
+        case NumLitU64:
+            return false;
+        case NumLitCount:
+            zig_unreachable();
+    }
+    zig_unreachable();
+}
+
+uint64_t num_lit_bit_count(NumLit num_lit) {
+    switch (num_lit) {
+        case NumLitI8:
+        case NumLitU8:
+            return 8;
+        case NumLitI16:
+        case NumLitU16:
+            return 16;
+        case NumLitI32:
+        case NumLitU32:
+        case NumLitF32:
+            return 32;
+        case NumLitI64:
+        case NumLitU64:
+        case NumLitF64:
+            return 64;
+        case NumLitF128:
+            return 128;
+        case NumLitCount:
+            zig_unreachable();
+    }
+    zig_unreachable();
+}
src/parser.hpp
@@ -267,6 +267,36 @@ struct AstNodeStringLiteral {
     bool c;
 };
 
+enum NumLit {
+    NumLitF32,
+    NumLitF64,
+    NumLitF128,
+    NumLitI8,
+    NumLitU8,
+    NumLitI16,
+    NumLitU16,
+    NumLitI32,
+    NumLitU32,
+    NumLitI64,
+    NumLitU64,
+
+    NumLitCount
+};
+
+struct AstNodeNumberLiteral {
+    NumLit kind;
+
+    // overflow is true if when parsing the number, we discovered it would not
+    // fit without losing data in a uint64_t, int64_t, or double
+    bool overflow;
+
+    union {
+        uint64_t x_uint;
+        int64_t x_int;
+        double x_float;
+    } data;
+};
+
 struct AstNode {
     enum NodeType type;
     int line;
@@ -300,7 +330,7 @@ struct AstNode {
         AstNodeStructDecl struct_decl;
         AstNodeStructField struct_field;
         AstNodeStringLiteral string_literal;
-        Buf number;
+        AstNodeNumberLiteral number_literal;
         Buf symbol;
         bool bool_literal;
     } data;
@@ -329,4 +359,11 @@ const char *node_type_str(NodeType node_type);
 
 void ast_print(AstNode *node, int indent);
 
+const char *num_lit_str(NumLit num_lit);
+bool is_num_lit_signed(NumLit num_lit);
+bool is_num_lit_unsigned(NumLit num_lit);
+bool is_num_lit_float(NumLit num_lit);
+uint64_t num_lit_bit_count(NumLit num_lit);
+
+
 #endif
src/util.cpp
@@ -21,8 +21,16 @@ void zig_panic(const char *format, ...) {
 }
 
 uint32_t int_hash(int i) {
-    return *reinterpret_cast<uint32_t*>(&i);
+    return (uint32_t)(i % UINT32_MAX);
 }
 bool int_eq(int a, int b) {
     return a == b;
 }
+
+uint32_t uint64_hash(uint64_t i) {
+    return (uint32_t)(i % UINT32_MAX);
+}
+
+bool uint64_eq(uint64_t a, uint64_t b) {
+    return a == b;
+}
src/util.hpp
@@ -81,5 +81,7 @@ static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str)
 
 uint32_t int_hash(int i);
 bool int_eq(int a, int b);
+uint32_t uint64_hash(uint64_t i);
+bool uint64_eq(uint64_t a, uint64_t b);
 
 #endif
std/std.zig
@@ -15,10 +15,9 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize {
 
 // TODO error handling
 // TODO handle buffering and flushing
-// TODO non-i32 integer literals so we can remove the casts
 // TODO constants for SYS_write and stdout_fileno
 pub fn print_str(str : string) -> isize {
-    let SYS_write = 1;
-    let stdout_fileno = 1;
-    return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize);
+    let SYS_write : isize = 1;
+    let stdout_fileno : isize = 1;
+    return syscall3(SYS_write, stdout_fileno, str.ptr as isize, str.len as isize);
 }
test/run_tests.cpp
@@ -266,7 +266,7 @@ extern {
 
 export fn _start() -> unreachable {
     let a : i32 = 1;
-    let b = 2;
+    let b = 2 as i32;
     if (a + b == 3) {
         puts(c"OK");
     }
@@ -299,12 +299,12 @@ extern {
 
 export fn _start() -> unreachable {
     if (true) {
-        let no_conflict = 5;
+        let no_conflict : i32 = 5;
         if (no_conflict == 5) { puts(c"OK 1"); }
     }
 
     let c = {
-        let no_conflict = 10;
+        let no_conflict = 10 as i32;
         no_conflict
     };
     if (c == 10) { puts(c"OK 2"); }
@@ -343,7 +343,7 @@ export fn _start() -> unreachable {
     let mut zero : i32;
     if (zero == 0) { puts(c"zero"); }
 
-    let mut i = 0;
+    let mut i = 0 as i32;
 loop_start:
     if i == 3 {
         goto done;
@@ -366,7 +366,7 @@ extern {
 export fn _start() -> unreachable {
     let mut array : [i32; 5];
 
-    let mut i = 0;
+    let mut i : i32 = 0;
 loop_start:
     if i == 5 {
         goto loop_end;
@@ -378,7 +378,7 @@ loop_start:
 loop_end:
 
     i = 0;
-    let mut accumulator = 0;
+    let mut accumulator = 0 as i32;
 loop_2_start:
     if i == 5 {
         goto loop_2_end;
@@ -611,7 +611,7 @@ fn f() -> i32 {
 fn f() {
     if (0) {}
 }
-    )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got 'i32'");
+    )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got '(u8 literal)'");
 
     add_compile_fail_case("assign unreachable", R"SOURCE(
 fn f() {
@@ -685,13 +685,12 @@ fn f(...) {}
 
 }
 
-static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) {
+static void print_compiler_invocation(TestCase *test_case) {
     printf("%s", zig_exe);
     for (int i = 0; i < test_case->compiler_args.length; i += 1) {
         printf(" %s", test_case->compiler_args.at(i));
     }
     printf("\n");
-    printf("%s\n", buf_ptr(zig_stderr));
 }
 
 static void run_test(TestCase *test_case) {
@@ -716,21 +715,24 @@ static void run_test(TestCase *test_case) {
                     printf("========= Expected this compile error: =========\n");
                     printf("%s\n", err_text);
                     printf("================================================\n");
-                    print_compiler_invocation(test_case, &zig_stderr);
+                    print_compiler_invocation(test_case);
+                    printf("%s\n", buf_ptr(&zig_stderr));
                     exit(1);
                 }
             }
             return; // success
         } else {
             printf("\nCompile failed with return code 0 (Expected failure):\n");
-            print_compiler_invocation(test_case, &zig_stderr);
+            print_compiler_invocation(test_case);
+            printf("%s\n", buf_ptr(&zig_stderr));
             exit(1);
         }
     }
 
     if (return_code != 0) {
         printf("\nCompile failed with return code %d:\n", return_code);
-        print_compiler_invocation(test_case, &zig_stderr);
+        print_compiler_invocation(test_case);
+        printf("%s\n", buf_ptr(&zig_stderr));
         exit(1);
     }
 
@@ -740,6 +742,7 @@ static void run_test(TestCase *test_case) {
 
     if (return_code != 0) {
         printf("\nProgram exited with return code %d:\n", return_code);
+        print_compiler_invocation(test_case);
         printf("%s", tmp_exe_path);
         for (int i = 0; i < test_case->program_args.length; i += 1) {
             printf(" %s", test_case->program_args.at(i));
@@ -750,6 +753,12 @@ static void run_test(TestCase *test_case) {
     }
 
     if (!buf_eql_str(&program_stdout, test_case->output)) {
+        printf("\n");
+        print_compiler_invocation(test_case);
+        printf("%s", tmp_exe_path);
+        for (int i = 0; i < test_case->program_args.length; i += 1) {
+            printf(" %s", test_case->program_args.at(i));
+        }
         printf("\n");
         printf("==== Test failed. Expected output: ====\n");
         printf("%s\n", test_case->output);