Commit 32e2196257

Andrew Kelley <superjoe30@gmail.com>
2016-01-21 11:02:25
number literal rework
1 parent 5e212db
src/all_types.hpp
@@ -13,6 +13,7 @@
 #include "zig_llvm.hpp"
 #include "hash_map.hpp"
 #include "errmsg.hpp"
+#include "bignum.hpp"
 
 struct AstNode;
 struct ImportTableEntry;
@@ -49,15 +50,6 @@ enum CastOp {
     CastOpPointerReinterpret,
 };
 
-struct Cast {
-    CastOp op;
-    // if op is CastOpArrayToString, this will be a pointer to
-    // the string struct on the stack
-    LLVMValueRef ptr;
-    TypeTableEntry *after_type;
-    AstNode *source_node;
-};
-
 struct ConstEnumValue {
     uint64_t tag;
     ConstExprValue *payload;
@@ -68,9 +60,7 @@ struct ConstExprValue {
     bool depends_on_compile_var;
 
     union {
-        uint64_t x_uint;
-        int64_t x_int;
-        double x_float;
+        BigNum x_bignum;
         bool x_bool;
         FnTableEntry *x_fn;
         TypeTableEntry *x_type;
@@ -79,8 +69,19 @@ struct ConstExprValue {
     } data;
 };
 
+struct Cast {
+    CastOp op;
+    // if op is CastOpArrayToString, this will be a pointer to
+    // the string struct on the stack
+    LLVMValueRef ptr;
+    TypeTableEntry *after_type;
+    AstNode *source_node;
+    ConstExprValue const_val;
+};
+
 struct Expr {
     TypeTableEntry *type_entry;
+    TypeTableEntry *resolved_type;
     // the context in which this expression is evaluated.
     // for blocks, this points to the containing scope, not the block's own scope for its children.
     BlockContext *block_context;
@@ -92,10 +93,6 @@ struct Expr {
     ConstExprValue const_val;
 };
 
-struct NumLitCodeGen {
-    TypeTableEntry *resolved_type;
-};
-
 struct StructValExprCodeGen {
     TypeTableEntry *type_entry;
     LLVMValueRef ptr;
@@ -315,7 +312,6 @@ struct AstNodeFnCallExpr {
     // populated by semantic analyzer:
     BuiltinFnEntry *builtin_fn;
     Expr resolved_expr;
-    NumLitCodeGen resolved_num_lit;
     Cast cast;
     FnTableEntry *fn_entry;
 };
@@ -550,26 +546,15 @@ struct AstNodeCharLiteral {
 };
 
 enum NumLit {
-    NumLitF32,
-    NumLitF64,
-    NumLitF128,
-    NumLitU8,
-    NumLitU16,
-    NumLitU32,
-    NumLitU64,
-    NumLitI8,
-    NumLitI16,
-    NumLitI32,
-    NumLitI64,
-
-    NumLitCount
+    NumLitFloat,
+    NumLitUInt,
 };
 
 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
+    // fit without losing data in a uint64_t or double
     bool overflow;
 
     union {
@@ -578,7 +563,6 @@ struct AstNodeNumberLiteral {
     } data;
 
     // populated by semantic analyzer
-    NumLitCodeGen codegen;
     Expr resolved_expr;
 };
 
@@ -586,7 +570,6 @@ struct AstNodeErrorLiteral {
     Buf symbol;
 
     // populated by semantic analyzer
-    NumLitCodeGen codegen;
     Expr resolved_expr;
 };
 
@@ -758,10 +741,6 @@ struct TypeTableEntryStruct {
     bool reported_infinite_err;
 };
 
-struct TypeTableEntryNumLit {
-    NumLit kind;
-};
-
 struct TypeTableEntryMaybe {
     TypeTableEntry *child_type;
 };
@@ -808,7 +787,8 @@ enum TypeTableEntryId {
     TypeTableEntryIdPointer,
     TypeTableEntryIdArray,
     TypeTableEntryIdStruct,
-    TypeTableEntryIdNumberLiteral,
+    TypeTableEntryIdNumLitFloat,
+    TypeTableEntryIdNumLitInt,
     TypeTableEntryIdMaybe,
     TypeTableEntryIdError,
     TypeTableEntryIdEnum,
@@ -830,7 +810,6 @@ struct TypeTableEntry {
         TypeTableEntryInt integral;
         TypeTableEntryArray array;
         TypeTableEntryStruct structure;
-        TypeTableEntryNumLit num_lit;
         TypeTableEntryMaybe maybe;
         TypeTableEntryError error;
         TypeTableEntryEnum enumeration;
@@ -951,10 +930,10 @@ struct CodeGen {
         TypeTableEntry *entry_unreachable;
         TypeTableEntry *entry_type;
         TypeTableEntry *entry_invalid;
+        TypeTableEntry *entry_num_lit_int;
+        TypeTableEntry *entry_num_lit_float;
     } builtin_types;
 
-    TypeTableEntry *num_lit_types[NumLitCount];
-
     LLVMTargetDataRef target_data_ref;
     unsigned pointer_size_bytes;
     bool is_static;
src/analyze.cpp
@@ -103,7 +103,8 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
         case TypeTableEntryIdFloat:
         case TypeTableEntryIdPointer:
         case TypeTableEntryIdArray:
-        case TypeTableEntryIdNumberLiteral:
+        case TypeTableEntryIdNumLitFloat:
+        case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdMaybe:
         case TypeTableEntryIdFn:
         case TypeTableEntryIdError:
@@ -121,24 +122,20 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
     return entry;
 }
 
-static NumLit get_number_literal_kind_unsigned(uint64_t x) {
+static int bits_needed_for_unsigned(uint64_t x) {
     if (x <= UINT8_MAX) {
-        return NumLitU8;
+        return 8;
     } else if (x <= UINT16_MAX) {
-        return NumLitU16;
+        return 16;
     } else if (x <= UINT32_MAX) {
-        return NumLitU32;
+        return 32;
     } else {
-        return NumLitU64;
+        return 64;
     }
 }
 
-static TypeTableEntry *get_number_literal_type_unsigned(CodeGen *g, uint64_t x) {
-    return g->num_lit_types[get_number_literal_kind_unsigned(x)];
-}
-
-static TypeTableEntry *get_int_type_unsigned(CodeGen *g, uint64_t x) {
-    return get_int_type(g, false, num_lit_bit_count(get_number_literal_kind_unsigned(x)));
+static TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
+    return get_int_type(g, false, bits_needed_for_unsigned(x));
 }
 
 TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) {
@@ -660,10 +657,9 @@ static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEnt
     if (!enum_type->data.enumeration.is_invalid) {
         enum_type->data.enumeration.gen_field_count = gen_field_index;
 
-        uint64_t tag_size_in_bits = num_lit_bit_count(get_number_literal_kind_unsigned(field_count));
-        enum_type->align_in_bits = tag_size_in_bits;
-        enum_type->size_in_bits = tag_size_in_bits + biggest_union_member_size_in_bits;
-        TypeTableEntry *tag_type_entry = get_int_type_unsigned(g, field_count);
+        TypeTableEntry *tag_type_entry = get_smallest_unsigned_int_type(g, field_count);
+        enum_type->align_in_bits = tag_type_entry->size_in_bits;
+        enum_type->size_in_bits = tag_type_entry->size_in_bits + biggest_union_member_size_in_bits;
         enum_type->data.enumeration.tag_type = tag_type_entry;
 
         if (biggest_union_member) {
@@ -1048,136 +1044,105 @@ static TypeTableEntry *get_return_type(BlockContext *context) {
     return unwrapped_node_type(return_type_node);
 }
 
-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:
-        case TypeTableEntryIdEnum:
-        case TypeTableEntryIdMetaType:
-        case TypeTableEntryIdFn:
-        case TypeTableEntryIdError:
-            return false;
-        case TypeTableEntryIdInt:
-            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 if (other_type->size_in_bits == 32) {
-                return lit_size_in_bits < 24;
-            } else if (other_type->size_in_bits == 64) {
-                return lit_size_in_bits < 53;
-            } else {
-                return false;
-            }
-        case TypeTableEntryIdMaybe:
-            return false;
+static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTableEntry *other_type) {
+    Expr *expr = get_resolved_expr(literal_node);
+    ConstExprValue *const_val = &expr->const_val;
+    assert(const_val->ok);
+    if (other_type->id == TypeTableEntryIdFloat) {
+        expr->resolved_type = other_type;
+        return true;
+    } else if (other_type->id == TypeTableEntryIdInt &&
+               const_val->data.x_bignum.kind == BigNumKindInt)
+    {
+        if (bignum_fits_in_bits(&const_val->data.x_bignum, other_type->size_in_bits,
+                    other_type->data.integral.is_signed))
+        {
+            expr->resolved_type = other_type;
+            return true;
+        }
+    } else if (other_type->id == TypeTableEntryIdNumLitFloat ||
+               other_type->id == TypeTableEntryIdNumLitInt)
+    {
+        return true;
     }
-    zig_unreachable();
-}
-
-static TypeTableEntry *resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node,
-        TypeTableEntry *non_literal_type, AstNode *literal_node, TypeTableEntry *literal_type)
-{
-    NumLitCodeGen *num_lit_codegen = get_resolved_num_lit(literal_node);
 
-    if (non_literal_type && num_lit_fits_in_other_type(g, literal_type, non_literal_type)) {
-        assert(!num_lit_codegen->resolved_type);
-        num_lit_codegen->resolved_type = non_literal_type;
-        return non_literal_type;
-    } else {
-        return nullptr;
-    }
+    add_node_error(g, literal_node,
+        buf_sprintf("value %s cannot be represented in type '%s'",
+            buf_ptr(bignum_to_buf(&const_val->data.x_bignum)),
+            buf_ptr(&other_type->name)));
+    return false;
 }
 
-static TypeTableEntry * resolve_number_literals(CodeGen *g, AstNode *node1, AstNode *node2,
-        TypeTableEntry *type1, TypeTableEntry *type2)
+static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *parent_source_node,
+        AstNode **child_nodes, TypeTableEntry **child_types, int child_count)
 {
-    if (type1->id == TypeTableEntryIdNumberLiteral &&
-        type2->id == TypeTableEntryIdNumberLiteral)
-    {
-        NumLitCodeGen *codegen_num_lit_1 = get_resolved_num_lit(node1);
-        NumLitCodeGen *codegen_num_lit_2 = get_resolved_num_lit(node2);
-
-        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))
+    TypeTableEntry *prev_type = child_types[0];
+    AstNode *prev_node = child_nodes[0];
+    if (prev_type->id == TypeTableEntryIdInvalid) {
+        return prev_type;
+    }
+    for (int i = 1; i < child_count; i += 1) {
+        TypeTableEntry *cur_type = child_types[i];
+        AstNode *cur_node = child_nodes[i];
+        if (cur_type->id == TypeTableEntryIdInvalid) {
+            return cur_type;
+        } else if (prev_type->id == TypeTableEntryIdUnreachable) {
+            prev_type = cur_type;
+            prev_node = cur_node;
+        } else if (cur_type->id == TypeTableEntryIdUnreachable) {
+            continue;
+        } else if (prev_type->id == TypeTableEntryIdInt &&
+                   cur_type->id == TypeTableEntryIdInt &&
+                   prev_type->data.integral.is_signed == cur_type->data.integral.is_signed)
+        {
+            if (cur_type->size_in_bits > prev_type->size_in_bits) {
+                prev_type = cur_type;
+                prev_node = cur_node;
+            }
+        } else if (prev_type->id == TypeTableEntryIdFloat &&
+                   cur_type->id == TypeTableEntryIdFloat)
+        {
+            if (cur_type->size_in_bits > prev_type->size_in_bits) {
+                prev_type = cur_type;
+                prev_node = cur_node;
+            }
+        } else if (prev_type->id == TypeTableEntryIdNumLitFloat &&
+                   cur_type->id == TypeTableEntryIdNumLitFloat)
+        {
+            continue;
+        } else if (prev_type->id == TypeTableEntryIdNumLitInt &&
+                   cur_type->id == TypeTableEntryIdNumLitInt)
         {
-            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_unsigned(type1->data.num_lit.kind) &&
-                   is_num_lit_unsigned(type2->data.num_lit.kind))
+            continue;
+        } else if (prev_type->id == TypeTableEntryIdNumLitInt ||
+                    prev_type->id == TypeTableEntryIdNumLitFloat)
+        {
+            if (num_lit_fits_in_other_type(g, prev_node, cur_type)) {
+                prev_type = cur_type;
+                prev_node = cur_node;
+                continue;
+            } else {
+                return g->builtin_types.entry_invalid;
+            }
+        } else if (cur_type->id == TypeTableEntryIdNumLitInt ||
+                   cur_type->id == TypeTableEntryIdNumLitFloat)
         {
-            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;
+            if (num_lit_fits_in_other_type(g, cur_node, prev_type)) {
+                continue;
+            } else {
+                return g->builtin_types.entry_invalid;
+            }
+        } else if (prev_type == cur_type) {
+            continue;
         } 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);
-    }
-}
+            add_node_error(g, parent_source_node,
+                buf_sprintf("incompatible types: '%s' and '%s'",
+                    buf_ptr(&prev_type->name), buf_ptr(&cur_type->name)));
 
-static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *node,
-        TypeTableEntry *type1, TypeTableEntry *type2, AstNode *node1, AstNode *node2)
-{
-    if (type1->id == TypeTableEntryIdInvalid ||
-        type2->id == TypeTableEntryIdInvalid)
-    {
-        return type1;
-    } else if (type1->id == TypeTableEntryIdUnreachable) {
-        return type2;
-    } else if (type2->id == TypeTableEntryIdUnreachable) {
-        return type1;
-    } else if (type1->id == TypeTableEntryIdInt &&
-        type2->id == TypeTableEntryIdInt &&
-        type1->data.integral.is_signed == type2->data.integral.is_signed)
-    {
-        return (type1->size_in_bits > type2->size_in_bits) ? type1 : type2;
-    } else if (type1->id == TypeTableEntryIdFloat &&
-               type2->id == TypeTableEntryIdFloat)
-    {
-        return (type1->size_in_bits > type2->size_in_bits) ? type1 : type2;
-    } else if (type1->id == TypeTableEntryIdArray &&
-               type2->id == TypeTableEntryIdArray &&
-               type1 == type2)
-    {
-        return type1;
-    } else if (type1->id == TypeTableEntryIdNumberLiteral ||
-               type2->id == TypeTableEntryIdNumberLiteral)
-    {
-        TypeTableEntry *resolved_type = resolve_number_literals(g, node1, node2, type1, type2);
-        if (resolved_type)
-            return resolved_type;
-    } else if (type1 == type2) {
-        return type1;
+            return g->builtin_types.entry_invalid;
+        }
     }
-
-    add_node_error(g, node,
-        buf_sprintf("incompatible types: '%s' and '%s'",
-            buf_ptr(&type1->name), buf_ptr(&type2->name)));
-
-    return g->builtin_types.entry_invalid;
+    return prev_type;
 }
 
 static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *context, AstNode *node,
@@ -1192,16 +1157,6 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
     if (actual_type->id == TypeTableEntryIdUnreachable)
         return actual_type; // sorry toots; gotta run. good luck with that expected type.
 
-    if (actual_type->id == TypeTableEntryIdNumberLiteral &&
-        num_lit_fits_in_other_type(g, actual_type, expected_type))
-    {
-        NumLitCodeGen *num_lit_code_gen = get_resolved_num_lit(node);
-        assert(!num_lit_code_gen->resolved_type ||
-                num_lit_code_gen->resolved_type == expected_type);
-        num_lit_code_gen->resolved_type = expected_type;
-        return expected_type;
-    }
-
     if (expected_type->id == TypeTableEntryIdMaybe &&
         actual_type->id == TypeTableEntryIdMaybe)
     {
@@ -1283,6 +1238,16 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
         return expected_type;
     }
 
+    if ((actual_type->id == TypeTableEntryIdNumLitFloat ||
+         actual_type->id == TypeTableEntryIdNumLitInt))
+    {
+        if (num_lit_fits_in_other_type(g, node, expected_type)) {
+            return expected_type;
+        } else {
+            return g->builtin_types.entry_invalid;
+        }
+    }
+
     add_node_error(g, first_executing_node(node),
         buf_sprintf("expected type '%s', got '%s'",
             buf_ptr(&expected_type->name),
@@ -1292,23 +1257,23 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
 }
 
 static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, BlockContext *block_context,
-        AstNode *parent_node,
-        AstNode *child1, AstNode *child2,
-        TypeTableEntry *type1, TypeTableEntry *type2)
+        AstNode *parent_source_node,
+        AstNode **child_nodes, TypeTableEntry **child_types, int child_count)
 {
-    assert(type1);
-    assert(type2);
+    assert(child_count > 0);
 
-    TypeTableEntry *parent_type = determine_peer_type_compatibility(g, parent_node, type1, type2, child1, child2);
+    TypeTableEntry *expected_type = determine_peer_type_compatibility(g, parent_source_node,
+            child_nodes, child_types, child_count);
 
-    if (parent_type->id == TypeTableEntryIdInvalid) {
-        return parent_type;
+    if (expected_type->id == TypeTableEntryIdInvalid) {
+        return expected_type;
     }
 
-    resolve_type_compatibility(g, block_context, child1, parent_type, type1);
-    resolve_type_compatibility(g, block_context, child2, parent_type, type2);
+    for (int i = 0; i < child_count; i += 1) {
+        resolve_type_compatibility(g, block_context, child_nodes[i], expected_type, child_types[i]);
+    }
 
-    return parent_type;
+    return expected_type;
 }
 
 BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
@@ -1732,12 +1697,58 @@ static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, As
 {
     Expr *expr = get_resolved_expr(node);
     expr->const_val.ok = true;
-    expr->const_val.data.x_uint = x;
-    TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, x);
-    TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type);
-    return resolved_type ? resolved_type : num_lit_type;
+
+    bignum_init_unsigned(&expr->const_val.data.x_bignum, x);
+
+    if (expected_type) {
+        if (expected_type->id == TypeTableEntryIdMaybe) {
+            return g->builtin_types.entry_num_lit_int;
+        } else {
+            num_lit_fits_in_other_type(g, node, expected_type);
+            return expected_type;
+        }
+    } else {
+        return g->builtin_types.entry_num_lit_int;
+    }
+}
+
+static TypeTableEntry *resolve_expr_const_val_as_float_num_lit(CodeGen *g, AstNode *node,
+        TypeTableEntry *expected_type, double x)
+{
+    Expr *expr = get_resolved_expr(node);
+    expr->const_val.ok = true;
+
+    bignum_init_float(&expr->const_val.data.x_bignum, x);
+
+    if (expected_type) {
+        num_lit_fits_in_other_type(g, node, expected_type);
+        return expected_type;
+    } else {
+        return g->builtin_types.entry_num_lit_float;
+    }
+}
+
+static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode *node, 
+        bool (*bignum_fn)(BigNum *, BigNum *, BigNum *), AstNode *op1, AstNode *op2,
+        TypeTableEntry *resolved_type)
+{
+    ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+    ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val;
+    ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val;
+
+    const_val->ok = true;
+
+    if (bignum_fn(&const_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) {
+        add_node_error(g, node,
+            buf_sprintf("value cannot be represented in any integer type"));
+    } else {
+        num_lit_fits_in_other_type(g, node, resolved_type);
+    }
+
+    return resolved_type;
 }
 
+
 static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
@@ -1918,60 +1929,6 @@ static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) {
     }
 }
 
-static bool eval_bool_bin_op_signed(int64_t a, BinOpType bin_op, int64_t b) {
-    if (bin_op == BinOpTypeCmpEq) {
-        return a == b;
-    } else if (bin_op == BinOpTypeCmpNotEq) {
-        return a != b;
-    } else if (bin_op == BinOpTypeCmpLessThan) {
-        return a < b;
-    } else if (bin_op == BinOpTypeCmpGreaterThan) {
-        return a > b;
-    } else if (bin_op == BinOpTypeCmpLessOrEq) {
-        return a <= b;
-    } else if (bin_op == BinOpTypeCmpGreaterOrEq) {
-        return a >= b;
-    } else {
-        zig_unreachable();
-    }
-}
-
-static bool eval_bool_bin_op_unsigned(uint64_t a, BinOpType bin_op, uint64_t b) {
-    if (bin_op == BinOpTypeCmpEq) {
-        return a == b;
-    } else if (bin_op == BinOpTypeCmpNotEq) {
-        return a != b;
-    } else if (bin_op == BinOpTypeCmpLessThan) {
-        return a < b;
-    } else if (bin_op == BinOpTypeCmpGreaterThan) {
-        return a > b;
-    } else if (bin_op == BinOpTypeCmpLessOrEq) {
-        return a <= b;
-    } else if (bin_op == BinOpTypeCmpGreaterOrEq) {
-        return a >= b;
-    } else {
-        zig_unreachable();
-    }
-}
-
-static bool eval_bool_bin_op_float(double a, BinOpType bin_op, double b) {
-    if (bin_op == BinOpTypeCmpEq) {
-        return a == b;
-    } else if (bin_op == BinOpTypeCmpNotEq) {
-        return a != b;
-    } else if (bin_op == BinOpTypeCmpLessThan) {
-        return a < b;
-    } else if (bin_op == BinOpTypeCmpGreaterThan) {
-        return a > b;
-    } else if (bin_op == BinOpTypeCmpLessOrEq) {
-        return a <= b;
-    } else if (bin_op == BinOpTypeCmpGreaterOrEq) {
-        return a >= b;
-    } else {
-        zig_unreachable();
-    }
-}
-
 static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         AstNode *node)
 {
@@ -1983,8 +1940,11 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
     TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, op1);
     TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, op2);
 
+    AstNode *op_nodes[] = {op1, op2};
+    TypeTableEntry *op_types[] = {op1_type, op2_type};
+
     TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, context, node,
-            op1, op2, op1_type, op2_type);
+            op_nodes, op_types, 2);
 
     if (resolved_type->id == TypeTableEntryIdInvalid) {
         return g->builtin_types.entry_invalid;
@@ -1997,20 +1957,30 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
     }
 
     bool answer;
-    if (resolved_type->id == TypeTableEntryIdInt) {
-        if (op1_type->data.integral.is_signed &&
-            op2_type->data.integral.is_signed)
-        {
-            answer = eval_bool_bin_op_signed(op1_val->data.x_int, bin_op_type, op2_val->data.x_int);
-        } else if (!op1_type->data.integral.is_signed &&
-                   !op2_type->data.integral.is_signed)
-        {
-            answer = eval_bool_bin_op_unsigned(op1_val->data.x_uint, bin_op_type, op2_val->data.x_uint);
+    if (resolved_type->id == TypeTableEntryIdNumLitFloat ||
+        resolved_type->id == TypeTableEntryIdNumLitInt ||
+        resolved_type->id == TypeTableEntryIdFloat ||
+        resolved_type->id == TypeTableEntryIdInt)
+    {
+        bool (*bignum_cmp)(BigNum *, BigNum *);
+        if (bin_op_type == BinOpTypeCmpEq) {
+            bignum_cmp = bignum_cmp_eq;
+        } else if (bin_op_type == BinOpTypeCmpNotEq) {
+            bignum_cmp = bignum_cmp_neq;
+        } else if (bin_op_type == BinOpTypeCmpLessThan) {
+            bignum_cmp = bignum_cmp_lt;
+        } else if (bin_op_type == BinOpTypeCmpGreaterThan) {
+            bignum_cmp = bignum_cmp_gt;
+        } else if (bin_op_type == BinOpTypeCmpLessOrEq) {
+            bignum_cmp = bignum_cmp_lte;
+        } else if (bin_op_type == BinOpTypeCmpGreaterOrEq) {
+            bignum_cmp = bignum_cmp_gte;
         } else {
             zig_unreachable();
         }
-    } else if (resolved_type->id == TypeTableEntryIdFloat) {
-        answer = eval_bool_bin_op_float(op1_val->data.x_float, bin_op_type, op2_val->data.x_float);
+
+        answer = bignum_cmp(&op1_val->data.x_bignum, &op2_val->data.x_bignum);
+
     } else if (resolved_type->id == TypeTableEntryIdEnum) {
         ConstEnumValue *enum1 = &op1_val->data.x_enum;
         ConstEnumValue *enum2 = &op2_val->data.x_enum;
@@ -2124,7 +2094,45 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
                 TypeTableEntry *lhs_type = analyze_expression(g, import, context, expected_type, op1);
                 TypeTableEntry *rhs_type = analyze_expression(g, import, context, expected_type, op2);
 
-                return resolve_peer_type_compatibility(g, context, node, op1, op2, lhs_type, rhs_type);
+                AstNode *op_nodes[] = {op1, op2};
+                TypeTableEntry *op_types[] = {lhs_type, rhs_type};
+
+                TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, context, node,
+                        op_nodes, op_types, 2);
+
+                if (resolved_type->id == TypeTableEntryIdInvalid) {
+                    return resolved_type;
+                }
+
+                ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val;
+                ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val;
+                if (!op1_val->ok || !op2_val->ok) {
+                    return resolved_type;
+                }
+
+                if (bin_op_type == BinOpTypeAdd) {
+                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_add, op1, op2, resolved_type);
+                } else if (bin_op_type == BinOpTypeSub) {
+                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_sub, op1, op2, resolved_type);
+                } else if (bin_op_type == BinOpTypeMult) {
+                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_mul, op1, op2, resolved_type);
+                } else if (bin_op_type == BinOpTypeDiv) {
+                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_div, op1, op2, resolved_type);
+                } else if (bin_op_type == BinOpTypeMod) {
+                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_mod, op1, op2, resolved_type);
+                } else if (bin_op_type == BinOpTypeBinOr) {
+                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_or, op1, op2, resolved_type);
+                } else if (bin_op_type == BinOpTypeBinAnd) {
+                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_and, op1, op2, resolved_type);
+                } else if (bin_op_type == BinOpTypeBinXor) {
+                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_xor, op1, op2, resolved_type);
+                } else if (bin_op_type == BinOpTypeBitShiftLeft) {
+                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_shl, op1, op2, resolved_type);
+                } else if (bin_op_type == BinOpTypeBitShiftRight) {
+                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_shr, op1, op2, resolved_type);
+                } else {
+                    zig_unreachable();
+                }
             }
         case BinOpTypeUnwrapMaybe:
             {
@@ -2197,6 +2205,8 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
         AstNodeVariableDeclaration *variable_declaration,
         bool expr_is_maybe)
 {
+    bool is_const = variable_declaration->is_const;
+
     TypeTableEntry *explicit_type = nullptr;
     if (variable_declaration->type != nullptr) {
         explicit_type = analyze_type_expr(g, import, context, variable_declaration->type);
@@ -2223,19 +2233,19 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
             add_node_error(g, source_node,
                 buf_sprintf("variable initialization is unreachable"));
             implicit_type = g->builtin_types.entry_invalid;
-        } else if (implicit_type->id == TypeTableEntryIdNumberLiteral) {
-            add_node_error(g, source_node,
-                buf_sprintf("unable to infer variable type"));
-            implicit_type = g->builtin_types.entry_invalid;
-        } else if (implicit_type->id == TypeTableEntryIdMetaType &&
-                   !variable_declaration->is_const)
+        } else if (!is_const &&
+                (implicit_type->id == TypeTableEntryIdNumLitFloat ||
+                 implicit_type->id == TypeTableEntryIdNumLitInt))
         {
+            add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
+            implicit_type = g->builtin_types.entry_invalid;
+        } else if (implicit_type->id == TypeTableEntryIdMetaType && !is_const) {
             add_node_error(g, source_node, buf_sprintf("variable of type 'type' must be constant"));
             implicit_type = g->builtin_types.entry_invalid;
         }
     }
 
-    if (implicit_type == nullptr && variable_declaration->is_const) {
+    if (implicit_type == nullptr && is_const) {
         add_node_error(g, source_node, buf_sprintf("const variable missing initialization"));
         implicit_type = g->builtin_types.entry_invalid;
     }
@@ -2244,7 +2254,7 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa
     assert(type != nullptr); // should have been caught by the parser
 
     VariableTableEntry *var = add_local_var(g, source_node, context,
-            &variable_declaration->symbol, type, variable_declaration->is_const);
+            &variable_declaration->symbol, type, is_const);
 
 
     bool is_pub = (variable_declaration->visib_mod != VisibModPrivate);
@@ -2300,28 +2310,15 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry
         return g->builtin_types.entry_invalid;
     }
 
-    ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
-    const_val->ok = true;
-    if (is_num_lit_unsigned(node->data.number_literal.kind)) {
-        const_val->data.x_uint = node->data.number_literal.data.x_uint;
-    } else if (is_num_lit_float(node->data.number_literal.kind)) {
-        const_val->data.x_float = node->data.number_literal.data.x_float;
+    if (node->data.number_literal.kind == NumLitUInt) {
+        return resolve_expr_const_val_as_unsigned_num_lit(g, node,
+                expected_type, node->data.number_literal.data.x_uint);
+    } else if (node->data.number_literal.kind == NumLitFloat) {
+        return resolve_expr_const_val_as_float_num_lit(g, node,
+                expected_type, node->data.number_literal.data.x_float);
     } else {
         zig_unreachable();
     }
-
-    TypeTableEntry *num_lit_type = g->num_lit_types[node->data.number_literal.kind];
-    if (expected_type) {
-        NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(node);
-        assert(!codegen_num_lit->resolved_type);
-        TypeTableEntry *after_implicit_cast_resolved_type =
-            resolve_type_compatibility(g, block_context, node, expected_type, num_lit_type);
-        assert(codegen_num_lit->resolved_type ||
-                after_implicit_cast_resolved_type->id == TypeTableEntryIdInvalid);
-        return after_implicit_cast_resolved_type;
-    } else {
-        return num_lit_type;
-    }
 }
 
 static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
@@ -2353,8 +2350,15 @@ static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import,
 
         ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
         if (const_val->ok) {
-            return resolve_expr_const_val_as_type(g, node,
-                    get_array_type(g, child_type, const_val->data.x_uint));
+            if (const_val->data.x_bignum.is_negative) {
+                add_node_error(g, size_node,
+                    buf_sprintf("array size %s is negative",
+                        buf_ptr(bignum_to_buf(&const_val->data.x_bignum))));
+                return g->builtin_types.entry_invalid;
+            } else {
+                return resolve_expr_const_val_as_type(g, node,
+                        get_array_type(g, child_type, const_val->data.x_bignum.data.x_uint));
+            }
         } else {
             return resolve_expr_const_val_as_type(g, node,
                     get_unknown_size_array_type(g, child_type, node->data.array_type.is_const));
@@ -2493,9 +2497,9 @@ static TypeTableEntry *analyze_if_then_else(CodeGen *g, ImportTableEntry *import
     if (expected_type) {
         return (then_type->id == TypeTableEntryIdUnreachable) ? else_type : then_type;
     } else {
-        return resolve_peer_type_compatibility(g, context, parent_node,
-                then_block, else_node,
-                then_type, else_type);
+        AstNode *op_nodes[] = {then_block, else_node};
+        TypeTableEntry *op_types[] = {then_type, else_type};
+        return resolve_peer_type_compatibility(g, context, parent_node, op_nodes, op_types, 2);
     }
 }
 
@@ -2535,10 +2539,60 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
     if (type_entry->id == TypeTableEntryIdInvalid) {
         return g->builtin_types.entry_invalid;
     } else if (type_entry->id == TypeTableEntryIdInt) {
-        // TODO const expr eval for min/max int
+        ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+        const_val->ok = true;
+        if (is_max) {
+            if (type_entry->data.integral.is_signed) {
+                int64_t val;
+                if (type_entry->size_in_bits == 64) {
+                    val = INT64_MAX;
+                } else if (type_entry->size_in_bits == 32) {
+                    val = INT32_MAX;
+                } else if (type_entry->size_in_bits == 16) {
+                    val = INT16_MAX;
+                } else if (type_entry->size_in_bits == 8) {
+                    val = INT8_MAX;
+                } else {
+                    zig_unreachable();
+                }
+                bignum_init_signed(&const_val->data.x_bignum, val);
+            } else {
+                uint64_t val;
+                if (type_entry->size_in_bits == 64) {
+                    val = UINT64_MAX;
+                } else if (type_entry->size_in_bits == 32) {
+                    val = UINT32_MAX;
+                } else if (type_entry->size_in_bits == 16) {
+                    val = UINT16_MAX;
+                } else if (type_entry->size_in_bits == 8) {
+                    val = UINT8_MAX;
+                } else {
+                    zig_unreachable();
+                }
+                bignum_init_unsigned(&const_val->data.x_bignum, val);
+            }
+        } else {
+            if (type_entry->data.integral.is_signed) {
+                int64_t val;
+                if (type_entry->size_in_bits == 64) {
+                    val = INT64_MIN;
+                } else if (type_entry->size_in_bits == 32) {
+                    val = INT32_MIN;
+                } else if (type_entry->size_in_bits == 16) {
+                    val = INT16_MIN;
+                } else if (type_entry->size_in_bits == 8) {
+                    val = INT8_MIN;
+                } else {
+                    zig_unreachable();
+                }
+                bignum_init_signed(&const_val->data.x_bignum, val);
+            } else {
+                bignum_init_unsigned(&const_val->data.x_bignum, 0);
+            }
+        }
         return type_entry;
     } else if (type_entry->id == TypeTableEntryIdFloat) {
-        // TODO const expr eval for min/max float
+        zig_panic("TODO analyze_min_max_value float");
         return type_entry;
     } else if (type_entry->id == TypeTableEntryIdBool) {
         return resolve_expr_const_val_as_bool(g, node, is_max);
@@ -2559,10 +2613,9 @@ static void eval_const_expr_implicit_cast(CodeGen *g, ImportTableEntry *import,
         case CastOpPointerReinterpret:
             {
                 ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val;
-                ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
-                if (other_val != const_val) {
-                    *const_val = *other_val;
-                }
+                ConstExprValue *const_val = &cast->const_val;
+                assert(const_val != other_val);
+                *const_val = *other_val;
                 break;
             }
         case CastOpToUnknownSizeArray:
@@ -2571,14 +2624,11 @@ static void eval_const_expr_implicit_cast(CodeGen *g, ImportTableEntry *import,
         case CastOpMaybeWrap:
             {
                 ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val;
-                ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+                ConstExprValue *const_val = &cast->const_val;
                 if (!other_val->ok) {
                     break;
-                } else if (const_val == other_val) {
-                    ConstExprValue *new_val = allocate<ConstExprValue>(1);
-                    memcpy(new_val, other_val, sizeof(ConstExprValue));
-                    other_val = new_val;
                 }
+                assert(const_val != other_val);
 
                 const_val->data.x_maybe = other_val;
                 const_val->ok = true;
@@ -2635,12 +2685,10 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
         context->cast_expr_alloca_list.append(cast);
         eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
         return wanted_type;
-    } else if (actual_type->id == TypeTableEntryIdNumberLiteral &&
-               num_lit_fits_in_other_type(g, actual_type, wanted_type))
+    } else if (actual_type->id == TypeTableEntryIdNumLitFloat ||
+                actual_type->id == TypeTableEntryIdNumLitInt)
     {
-        NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(expr_node);
-        assert(!codegen_num_lit->resolved_type);
-        codegen_num_lit->resolved_type = wanted_type;
+        num_lit_fits_in_other_type(g, expr_node, wanted_type);
         cast->op = CastOpNothing;
         eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
         return wanted_type;
@@ -2998,7 +3046,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                     return g->builtin_types.entry_bool;
                 }
 
-                bool answer = target_const_val->data.x_bool;
+                bool answer = !target_const_val->data.x_bool;
                 return resolve_expr_const_val_as_bool(g, node, answer);
             }
         case PrefixOpBinNot:
@@ -3008,8 +3056,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                 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)))
+                           expr_type->id == TypeTableEntryIdNumLitInt)
                 {
                     return expr_type;
                 } else {
@@ -3017,6 +3064,7 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                             buf_ptr(&expr_type->name)));
                     return g->builtin_types.entry_invalid;
                 }
+                // TODO const expr eval
             }
         case PrefixOpNegation:
             {
@@ -3030,13 +3078,16 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                     return expr_type;
                 } else if (expr_type->id == TypeTableEntryIdFloat) {
                     return expr_type;
-                } else if (expr_type->id == TypeTableEntryIdNumberLiteral) {
+                } else if (expr_type->id == TypeTableEntryIdNumLitInt) {
+                    return expr_type;
+                } else if (expr_type->id == TypeTableEntryIdNumLitFloat) {
                     return expr_type;
                 } else {
                     add_node_error(g, node, buf_sprintf("invalid negation type: '%s'",
                             buf_ptr(&expr_type->name)));
                     return g->builtin_types.entry_invalid;
                 }
+                // TODO const expr eval
             }
         case PrefixOpAddressOf:
         case PrefixOpConstAddressOf:
@@ -4129,59 +4180,6 @@ Expr *get_resolved_expr(AstNode *node) {
     zig_unreachable();
 }
 
-NumLitCodeGen *get_resolved_num_lit(AstNode *node) {
-    switch (node->type) {
-        case NodeTypeNumberLiteral:
-            return &node->data.number_literal.codegen;
-        case NodeTypeErrorLiteral:
-            return &node->data.error_literal.codegen;
-        case NodeTypeFnCallExpr:
-            return &node->data.fn_call_expr.resolved_num_lit;
-        case NodeTypeReturnExpr:
-        case NodeTypeBinOpExpr:
-        case NodeTypePrefixOpExpr:
-        case NodeTypeArrayAccessExpr:
-        case NodeTypeSliceExpr:
-        case NodeTypeFieldAccessExpr:
-        case NodeTypeIfBoolExpr:
-        case NodeTypeIfVarExpr:
-        case NodeTypeWhileExpr:
-        case NodeTypeForExpr:
-        case NodeTypeSwitchExpr:
-        case NodeTypeSwitchProng:
-        case NodeTypeSwitchRange:
-        case NodeTypeAsmExpr:
-        case NodeTypeContainerInitExpr:
-        case NodeTypeRoot:
-        case NodeTypeRootExportDecl:
-        case NodeTypeFnProto:
-        case NodeTypeFnDef:
-        case NodeTypeFnDecl:
-        case NodeTypeParamDecl:
-        case NodeTypeBlock:
-        case NodeTypeExternBlock:
-        case NodeTypeDirective:
-        case NodeTypeVariableDeclaration:
-        case NodeTypeStringLiteral:
-        case NodeTypeCharLiteral:
-        case NodeTypeSymbol:
-        case NodeTypeUse:
-        case NodeTypeBoolLiteral:
-        case NodeTypeNullLiteral:
-        case NodeTypeLabel:
-        case NodeTypeGoto:
-        case NodeTypeBreak:
-        case NodeTypeContinue:
-        case NodeTypeStructDecl:
-        case NodeTypeStructField:
-        case NodeTypeStructValueField:
-        case NodeTypeArrayType:
-        case NodeTypeErrorValueDecl:
-            zig_unreachable();
-    }
-    zig_unreachable();
-}
-
 TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
     switch (node->type) {
         case NodeTypeVariableDeclaration:
src/analyze.hpp
@@ -18,7 +18,6 @@ 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);
 TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits);
src/bignum.cpp
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2016 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include "bignum.hpp"
+
+#include <assert.h>
+#include <math.h>
+
+static void bignum_normalize(BigNum *bn) {
+    assert(bn->kind == BigNumKindInt);
+    if (bn->data.x_uint == 0) {
+        bn->is_negative = false;
+    }
+}
+
+void bignum_init_float(BigNum *dest, double x) {
+    dest->kind = BigNumKindFloat;
+    dest->is_negative = false;
+    dest->data.x_float = x;
+}
+
+void bignum_init_unsigned(BigNum *dest, uint64_t x) {
+    dest->kind = BigNumKindInt;
+    dest->is_negative = false;
+    dest->data.x_uint = x;
+}
+
+void bignum_init_signed(BigNum *dest, int64_t x) {
+    dest->kind = BigNumKindInt;
+    if (x < 0) {
+        dest->is_negative = true;
+        dest->data.x_uint = ((uint64_t)(-(x + 1))) + 1;
+    } else {
+        dest->is_negative = false;
+        dest->data.x_uint = x;
+    }
+}
+
+bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed) {
+    assert(bn->kind == BigNumKindInt);
+
+    if (is_signed) {
+        if (bn->data.x_uint <= ((uint64_t)(INT8_MAX)) + 1) {
+            return bit_count >= 8;
+        } else if (bn->data.x_uint <= ((uint64_t)(INT16_MAX)) + 1) {
+            return bit_count >= 16;
+        } else if (bn->data.x_uint <= ((uint64_t)(INT32_MAX)) + 1) {
+            return bit_count >= 32;
+        } else {
+            return bit_count >= 64;
+        }
+    } else {
+        if (bn->is_negative) {
+            return bn->data.x_uint == 0;
+        } else {
+            if (bn->data.x_uint <= UINT8_MAX) {
+                return bit_count >= 8;
+            } else if (bn->data.x_uint <= UINT16_MAX) {
+                return bit_count >= 16;
+            } else if (bn->data.x_uint <= UINT32_MAX) {
+                return bit_count >= 32;
+            } else {
+                return bit_count >= 64;
+            }
+        }
+    }
+}
+
+uint64_t bignum_to_twos_complement(BigNum *bn) {
+    assert(bn->kind == BigNumKindInt);
+
+    if (bn->is_negative) {
+        int64_t x = bn->data.x_uint;
+        return -x;
+    } else {
+        return bn->data.x_uint;
+    }
+}
+
+// returns true if overflow happened
+bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2) {
+    assert(op1->kind == op2->kind);
+    dest->kind = op1->kind;
+
+    if (dest->kind == BigNumKindFloat) {
+        dest->data.x_float = op1->data.x_float + op2->data.x_float;
+        return false;
+    }
+
+    if (op1->is_negative == op2->is_negative) {
+        return __builtin_uaddll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint);
+    } else if (!op1->is_negative && op2->is_negative) {
+        if (__builtin_usubll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint)) {
+            dest->data.x_uint = (UINT64_MAX - dest->data.x_uint) + 1;
+            dest->is_negative = true;
+            bignum_normalize(dest);
+            return false;
+        } else {
+            bignum_normalize(dest);
+            return false;
+        }
+    } else {
+        return bignum_add(dest, op2, op1);
+    }
+}
+
+void bignum_negate(BigNum *dest, BigNum *op) {
+    dest->kind = op->kind;
+
+    if (dest->kind == BigNumKindFloat) {
+        dest->data.x_float = -dest->data.x_float;
+    } else {
+        dest->data.x_uint = op->data.x_uint;
+        dest->is_negative = !op->is_negative;
+        bignum_normalize(dest);
+    }
+}
+
+bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2) {
+    BigNum op2_negated;
+    bignum_negate(&op2_negated, op2);
+    return bignum_add(dest, op1, &op2_negated);
+}
+
+bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2) {
+    assert(op1->kind == op2->kind);
+    dest->kind = op1->kind;
+
+    if (dest->kind == BigNumKindFloat) {
+        dest->data.x_float = op1->data.x_float * op2->data.x_float;
+        bignum_normalize(dest);
+        return false;
+    }
+
+    if (__builtin_umulll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint)) {
+        return true;
+    }
+
+    dest->is_negative = op1->is_negative != op2->is_negative;
+    bignum_normalize(dest);
+    return false;
+}
+
+bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) {
+    assert(op1->kind == op2->kind);
+    dest->kind = op1->kind;
+
+    if (dest->kind == BigNumKindFloat) {
+        dest->data.x_float = op1->data.x_float / op2->data.x_float;
+    } else {
+        dest->data.x_uint = op1->data.x_uint / op2->data.x_uint;
+        dest->is_negative = op1->is_negative != op2->is_negative;
+        bignum_normalize(dest);
+    }
+    return false;
+}
+
+bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) {
+    assert(op1->kind == op2->kind);
+    dest->kind = op1->kind;
+
+    if (dest->kind == BigNumKindFloat) {
+        dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);
+    } else {
+        if (op1->is_negative || op2->is_negative) {
+            zig_panic("TODO handle mod with negative numbers");
+        }
+        dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;
+        bignum_normalize(dest);
+    }
+    return false;
+}
+
+bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2) {
+    assert(op1->kind == BigNumKindInt);
+    assert(op2->kind == BigNumKindInt);
+
+    assert(!op1->is_negative);
+    assert(!op2->is_negative);
+
+    dest->kind = BigNumKindInt;
+    dest->data.x_uint = op1->data.x_uint | op2->data.x_uint;
+    return false;
+}
+
+bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2) {
+    assert(op1->kind == BigNumKindInt);
+    assert(op2->kind == BigNumKindInt);
+
+    assert(!op1->is_negative);
+    assert(!op2->is_negative);
+
+    dest->kind = BigNumKindInt;
+    dest->data.x_uint = op1->data.x_uint & op2->data.x_uint;
+    return false;
+}
+
+bool bignum_xor(BigNum *dest, BigNum *op1, BigNum *op2) {
+    assert(op1->kind == BigNumKindInt);
+    assert(op2->kind == BigNumKindInt);
+
+    assert(!op1->is_negative);
+    assert(!op2->is_negative);
+
+    dest->kind = BigNumKindInt;
+    dest->data.x_uint = op1->data.x_uint ^ op2->data.x_uint;
+    return false;
+}
+
+bool bignum_shl(BigNum *dest, BigNum *op1, BigNum *op2) {
+    assert(op1->kind == BigNumKindInt);
+    assert(op2->kind == BigNumKindInt);
+
+    assert(!op1->is_negative);
+    assert(!op2->is_negative);
+
+    dest->kind = BigNumKindInt;
+    dest->data.x_uint = op1->data.x_uint << op2->data.x_uint;
+    return false;
+}
+
+bool bignum_shr(BigNum *dest, BigNum *op1, BigNum *op2) {
+    assert(op1->kind == BigNumKindInt);
+    assert(op2->kind == BigNumKindInt);
+
+    assert(!op1->is_negative);
+    assert(!op2->is_negative);
+
+    dest->kind = BigNumKindInt;
+    dest->data.x_uint = op1->data.x_uint >> op2->data.x_uint;
+    return false;
+}
+
+
+Buf *bignum_to_buf(BigNum *bn) {
+    if (bn->kind == BigNumKindFloat) {
+        return buf_sprintf("%f", bn->data.x_float);
+    } else {
+        const char *neg = bn->is_negative ? "-" : "";
+        return buf_sprintf("%s%llu", neg, bn->data.x_uint);
+    }
+}
+
+bool bignum_cmp_eq(BigNum *op1, BigNum *op2) {
+    assert(op1->kind == op2->kind);
+    if (op1->kind == BigNumKindFloat) {
+        return op1->data.x_float == op2->data.x_float;
+    } else {
+        return op1->data.x_uint == op2->data.x_uint &&
+            (op1->is_negative == op2->is_negative || op1->data.x_uint == 0);
+    }
+}
+
+bool bignum_cmp_neq(BigNum *op1, BigNum *op2) {
+    return !bignum_cmp_eq(op1, op2);
+}
+
+bool bignum_cmp_lt(BigNum *op1, BigNum *op2) {
+    return !bignum_cmp_gte(op1, op2);
+}
+
+bool bignum_cmp_gt(BigNum *op1, BigNum *op2) {
+    return !bignum_cmp_lte(op1, op2);
+}
+
+bool bignum_cmp_lte(BigNum *op1, BigNum *op2) {
+    assert(op1->kind == op2->kind);
+    if (op1->kind == BigNumKindFloat) {
+        return (op1->data.x_float <= op2->data.x_float);
+    }
+
+    // assume normalized is_negative
+    if (!op1->is_negative && !op2->is_negative) {
+        return op1->data.x_uint <= op2->data.x_uint;
+    } else if (op1->is_negative && op2->is_negative) {
+        return op1->data.x_uint >= op2->data.x_uint;
+    } else if (op1->is_negative && !op2->is_negative) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool bignum_cmp_gte(BigNum *op1, BigNum *op2) {
+    assert(op1->kind == op2->kind);
+
+    if (op1->kind == BigNumKindFloat) {
+        return (op1->data.x_float >= op2->data.x_float);
+    }
+
+    // assume normalized is_negative
+    if (!op1->is_negative && !op2->is_negative) {
+        return op1->data.x_uint >= op2->data.x_uint;
+    } else if (op1->is_negative && op2->is_negative) {
+        return op1->data.x_uint <= op2->data.x_uint;
+    } else if (op1->is_negative && !op2->is_negative) {
+        return false;
+    } else {
+        return true;
+    }
+}
src/bignum.hpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2016 Andrew Kelley
+ *
+ * This file is part of zig, which is MIT licensed.
+ * See http://opensource.org/licenses/MIT
+ */
+
+#include "buffer.hpp"
+
+#include <stdint.h>
+
+enum BigNumKind {
+    BigNumKindInt,
+    BigNumKindFloat,
+};
+
+struct BigNum {
+    BigNumKind kind;
+    bool is_negative;
+    union {
+        unsigned long long x_uint;
+        double x_float;
+    } data;
+};
+
+void bignum_init_float(BigNum *dest, double x);
+void bignum_init_unsigned(BigNum *dest, uint64_t x);
+void bignum_init_signed(BigNum *dest, int64_t x);
+
+bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed);
+uint64_t bignum_to_twos_complement(BigNum *bn);
+
+// returns true if overflow happened
+bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2);
+bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2);
+bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2);
+bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2);
+bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2);
+
+bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2);
+bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2);
+bool bignum_xor(BigNum *dest, BigNum *op1, BigNum *op2);
+bool bignum_shl(BigNum *dest, BigNum *op1, BigNum *op2);
+bool bignum_shr(BigNum *dest, BigNum *op1, BigNum *op2);
+
+void bignum_negate(BigNum *dest, BigNum *op);
+
+// returns the result of the comparison
+bool bignum_cmp_eq(BigNum *op1, BigNum *op2);
+bool bignum_cmp_neq(BigNum *op1, BigNum *op2);
+bool bignum_cmp_lt(BigNum *op1, BigNum *op2);
+bool bignum_cmp_gt(BigNum *op1, BigNum *op2);
+bool bignum_cmp_lte(BigNum *op1, BigNum *op2);
+bool bignum_cmp_gte(BigNum *op1, BigNum *op2);
+
+Buf *bignum_to_buf(BigNum *bn);
src/codegen.cpp
@@ -118,6 +118,9 @@ static TypeTableEntry *get_expr_type(AstNode *node) {
     if (expr->implicit_cast.after_type) {
         return expr->implicit_cast.after_type;
     }
+    if (expr->resolved_type) {
+        return expr->resolved_type;
+    }
     return expr->type_entry;
 }
 
@@ -131,29 +134,35 @@ static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_no
     }
 }
 
-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;
+static LLVMValueRef gen_number_literal(CodeGen *g, AstNode *expr_node) {
+    Expr *expr = get_resolved_expr(expr_node);
+    TypeTableEntry *type_entry = expr->resolved_type;
+    if (!type_entry) {
+        type_entry = expr->type_entry;
+    }
     assert(type_entry);
 
-    // override the expression type for number literals
-    get_resolved_expr(source_node)->type_entry = type_entry;
+    ConstExprValue *const_val = &expr->const_val;
 
-    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
+    assert(const_val->ok);
 
+    if (type_entry->id == TypeTableEntryIdInt) {
+        assert(const_val->data.x_bignum.kind == BigNumKindInt);
         return LLVMConstInt(type_entry->type_ref,
-                num_lit_node->data.x_uint,
-                type_entry->data.integral.is_signed);
+            bignum_to_twos_complement(&const_val->data.x_bignum),
+            type_entry->data.integral.is_signed);
     } else if (type_entry->id == TypeTableEntryIdFloat) {
-
-        return LLVMConstReal(type_entry->type_ref,
-                num_lit_node->data.x_float);
+        if (const_val->data.x_bignum.kind == BigNumKindFloat) {
+            return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float);
+        } else {
+            int64_t x = const_val->data.x_bignum.data.x_uint;
+            if (const_val->data.x_bignum.is_negative) {
+                x = -x;
+            }
+            return LLVMConstReal(type_entry->type_ref, x);
+        }
     } else {
-        zig_panic("bad number literal type");
+        zig_unreachable();
     }
 }
 
@@ -265,73 +274,10 @@ static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
                 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 BuiltinFnIdMemberCount:
-            {
-                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();
-                }
-            }
+            return gen_number_literal(g, node);
     }
     zig_unreachable();
 }
@@ -1407,11 +1353,22 @@ static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) {
     assert(node->data.if_bool_expr.condition);
     assert(node->data.if_bool_expr.then_block);
 
-    LLVMValueRef cond_value = gen_expr(g, node->data.if_bool_expr.condition);
+    ConstExprValue *const_val = &get_resolved_expr(node->data.if_bool_expr.condition)->const_val;
+    if (const_val->ok) {
+        if (const_val->data.x_bool) {
+            return gen_expr(g, node->data.if_bool_expr.then_block);
+        } else if (node->data.if_bool_expr.else_node) {
+            return gen_expr(g, node->data.if_bool_expr.else_node);
+        } else {
+            return nullptr;
+        }
+    } else {
+        LLVMValueRef cond_value = gen_expr(g, node->data.if_bool_expr.condition);
 
-    return gen_if_bool_expr_raw(g, node, cond_value,
-            node->data.if_bool_expr.then_block,
-            node->data.if_bool_expr.else_node);
+        return gen_if_bool_expr_raw(g, node, cond_value,
+                node->data.if_bool_expr.then_block,
+                node->data.if_bool_expr.else_node);
+    }
 }
 
 static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) {
@@ -1932,15 +1889,6 @@ static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
             get_resolved_expr(node)->block_context, false, &init_val);
 }
 
-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_error_literal(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeErrorLiteral);
 
@@ -2383,20 +2331,6 @@ static void add_int_overflow_fns(CodeGen *g, TypeTableEntry *type_entry) {
     type_entry->data.integral.mul_with_overflow_fn = get_arithmetic_overflow_fn(g, type_entry, "smul", "umul");
 }
 
-static const NumLit num_lit_kinds[] = {
-    NumLitF32,
-    NumLitF64,
-    NumLitF128,
-    NumLitU8,
-    NumLitU16,
-    NumLitU32,
-    NumLitU64,
-    NumLitI8,
-    NumLitI16,
-    NumLitI32,
-    NumLitI64,
-};
-
 static const int int_sizes_in_bits[] = {
     8,
     16,
@@ -2411,18 +2345,15 @@ 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;
-        entry->size_in_bits = num_lit_bit_count(num_lit_kind);
-        g->num_lit_types[i] = entry;
+    {
+        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitFloat);
+        buf_init_from_str(&entry->name, "(float literal)");
+        g->builtin_types.entry_num_lit_float = entry;
+    }
+    {
+        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumLitInt);
+        buf_init_from_str(&entry->name, "(integer literal)");
+        g->builtin_types.entry_num_lit_int = entry;
     }
 
     for (int i = 0; i < array_length(int_sizes_in_bits); i += 1) {
src/parser.cpp
@@ -301,13 +301,12 @@ void ast_print(AstNode *node, int indent) {
             break;
         case NodeTypeNumberLiteral:
             {
-                NumLit num_lit = node->data.number_literal.kind;
+                NumLit kind = 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_unsigned(num_lit)) {
-                    fprintf(stderr, "%s %s %" PRIu64 "\n", name, kind_str, node->data.number_literal.data.x_uint);
+                if (kind == NumLitUInt) {
+                    fprintf(stderr, "%s uint %" PRIu64 "\n", name, node->data.number_literal.data.x_uint);
                 } else {
-                    fprintf(stderr, "%s %s %f\n", name, kind_str, node->data.number_literal.data.x_float);
+                    fprintf(stderr, "%s float %f\n", name, node->data.number_literal.data.x_float);
                 }
                 break;
             }
@@ -808,16 +807,7 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
         if (num_lit->overflow) return;
 
         num_lit->data.x_uint = whole_number;
-
-        if (whole_number <= UINT8_MAX) {
-            num_lit->kind = NumLitU8;
-        } else if (whole_number <= UINT16_MAX) {
-            num_lit->kind = NumLitU16;
-        } else if (whole_number <= UINT32_MAX) {
-            num_lit->kind = NumLitU32;
-        } else {
-            num_lit->kind = NumLitU64;
-        }
+        num_lit->kind = NumLitUInt;
     } else {
         // float
 
@@ -834,7 +824,7 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
             }
             assert(str_end == buf_ptr(pc->buf) + token->end_pos);
             num_lit->data.x_float = x;
-            num_lit->kind = NumLitF64;
+            num_lit->kind = NumLitFloat;
             return;
         }
 
@@ -954,8 +944,7 @@ static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLi
         double x = *(double *)&double_bits;
 
         num_lit->data.x_float = x;
-        // TODO: see if we can store it in f32
-        num_lit->kind = NumLitF64;
+        num_lit->kind = NumLitFloat;
     }
 }
 
@@ -3053,99 +3042,3 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner,
     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 NumLitU8:
-            return "u8";
-        case NumLitU16:
-            return "u16";
-        case NumLitU32:
-            return "u32";
-        case NumLitU64:
-            return "u64";
-        case NumLitI8:
-            return "i8";
-        case NumLitI16:
-            return "i16";
-        case NumLitI32:
-            return "i32";
-        case NumLitI64:
-            return "i64";
-        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 NumLitU8:
-        case NumLitU16:
-        case NumLitU32:
-        case NumLitU64:
-        case NumLitI8:
-        case NumLitI16:
-        case NumLitI32:
-        case NumLitI64:
-            return false;
-        case NumLitCount:
-            zig_unreachable();
-    }
-    zig_unreachable();
-}
-
-uint64_t num_lit_bit_count(NumLit num_lit) {
-    switch (num_lit) {
-        case NumLitU8:
-        case NumLitI8:
-            return 8;
-        case NumLitU16:
-        case NumLitI16:
-            return 16;
-        case NumLitU32:
-        case NumLitI32:
-        case NumLitF32:
-            return 32;
-        case NumLitU64:
-        case NumLitI64:
-        case NumLitF64:
-            return 64;
-        case NumLitF128:
-            return 128;
-        case NumLitCount:
-            zig_unreachable();
-    }
-    zig_unreachable();
-}
src/parser.hpp
@@ -24,10 +24,4 @@ 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_unsigned(NumLit num_lit);
-bool is_num_lit_float(NumLit num_lit);
-uint64_t num_lit_bit_count(NumLit num_lit);
-
-
 #endif
std/std.zig
@@ -34,7 +34,7 @@ pub %.BadPerm;
 pub %.PipeFail;
 */
 
-const buffer_size: u16 = 4 * 1024;
+//const buffer_size: u16 = 4 * 1024;
 const max_u64_base10_digits: isize = 20;
 
 /*
test/run_tests.cpp
@@ -1323,7 +1323,7 @@ fn f() i32 => {
 fn f() => {
     if (0) {}
 }
-    )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got '(u8 literal)'");
+    )SOURCE", 1, ".tmp_source.zig:3:9: error: value 0 cannot be represented in type 'bool'");
 
     add_compile_fail_case("assign unreachable", R"SOURCE(
 fn f() => {
CMakeLists.txt
@@ -25,6 +25,7 @@ include_directories(
 )
 
 set(ZIG_SOURCES
+    "${CMAKE_SOURCE_DIR}/src/bignum.cpp"
     "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
     "${CMAKE_SOURCE_DIR}/src/parser.cpp"
     "${CMAKE_SOURCE_DIR}/src/analyze.cpp"