Commit b0f608a6a7

Andrew Kelley <superjoe30@gmail.com>
2016-01-15 12:45:25
merge constant expression evaluator with analyzer
1 parent 0c9afed
Changed files (4)
doc/vim/syntax/zig.vim
@@ -8,14 +8,14 @@ if exists("b:current_syntax")
 endif
 
 syn keyword zigStorage const var extern volatile export pub noalias
-syn keyword zigStructure struct enum type
+syn keyword zigStructure struct enum
 syn keyword zigStatement goto break return continue asm
 syn keyword zigConditional if else switch
 syn keyword zigRepeat while for
 
 syn keyword zigConstant null
-syn keyword zigKeyword fn use
-syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string void unreachable
+syn keyword zigKeyword fn import
+syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 string void unreachable type
 
 syn keyword zigBoolean true false
 
src/all_types.hpp
@@ -57,6 +57,21 @@ struct Cast {
     AstNode *source_node;
 };
 
+struct ConstExprValue {
+    bool ok; // true if constant expression evalution worked
+    bool depends_on_compile_var;
+
+    union {
+        uint64_t x_uint;
+        int64_t x_int;
+        double x_float;
+        bool x_bool;
+        FnTableEntry *x_fn;
+        TypeTableEntry *x_type;
+        ConstExprValue *x_maybe;
+    } data;
+};
+
 struct Expr {
     TypeTableEntry *type_entry;
     // the context in which this expression is evaluated.
@@ -66,6 +81,8 @@ struct Expr {
     // may be null for no cast
     Cast implicit_cast; // happens first
     Cast implicit_maybe_cast; // happens second
+
+    ConstExprValue const_val;
 };
 
 struct NumLitCodeGen {
@@ -490,7 +507,6 @@ struct AstNodeNumberLiteral {
 
     union {
         uint64_t x_uint;
-        int64_t x_int;
         double x_float;
     } data;
 
@@ -534,8 +550,6 @@ struct AstNodeSymbolExpr {
     // populated by semantic analyzer
     Expr resolved_expr;
     VariableTableEntry *variable;
-    TypeTableEntry *meta_type;
-    FnTableEntry *fn_entry;
 };
 
 struct AstNodeBoolLiteral {
@@ -670,10 +684,6 @@ struct TypeTableEntryMaybe {
     TypeTableEntry *child_type;
 };
 
-struct TypeTableEntryMetaType {
-    TypeTableEntry *child_type;
-};
-
 struct TypeTableEntryEnum {
     AstNode *decl_node;
     uint32_t field_count;
@@ -731,7 +741,6 @@ struct TypeTableEntry {
         TypeTableEntryNumLit num_lit;
         TypeTableEntryMaybe maybe;
         TypeTableEntryEnum enumeration;
-        TypeTableEntryMetaType meta_type;
         TypeTableEntryFn fn;
     } data;
 
@@ -740,7 +749,6 @@ struct TypeTableEntry {
     TypeTableEntry *unknown_size_array_parent[2];
     HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size;
     TypeTableEntry *maybe_parent;
-    TypeTableEntry *meta_parent;
 };
 
 struct ImporterInfo {
@@ -852,6 +860,7 @@ struct CodeGen {
         TypeTableEntry *entry_c_string_literal;
         TypeTableEntry *entry_void;
         TypeTableEntry *entry_unreachable;
+        TypeTableEntry *entry_type;
         TypeTableEntry *entry_invalid;
     } builtin_types;
 
@@ -923,22 +932,4 @@ struct BlockContext {
     LLVMZigDIScope *di_scope;
 };
 
-struct ConstExprValue {
-    bool ok; // true if constant expression evalution worked
-    bool depends_on_compile_var;
-
-    union {
-        uint64_t x_uint;
-        int64_t x_int;
-        double x_float;
-        bool x_bool;
-        FnTableEntry *x_fn;
-        TypeTableEntry *x_type;
-        struct {
-            bool is_null;
-            ConstExprValue *child_val;
-        } x_maybe;
-    } data;
-};
-
 #endif
src/analyze.cpp
@@ -13,8 +13,6 @@
 
 static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node);
-static void eval_const_expr(CodeGen *g, BlockContext *context,
-        AstNode *node, ConstExprValue *out_val);
 static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import,
         BlockContext *context, TypeTableEntry *expected_type, AstNode *node);
 static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type);
@@ -158,20 +156,6 @@ 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_meta_type(CodeGen *g, TypeTableEntry *child_type) {
-    if (child_type->meta_parent) {
-        return child_type->meta_parent;
-    } else {
-        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMetaType);
-        buf_resize(&entry->name, 0);
-        buf_appendf(&entry->name, "(%s declaration)", buf_ptr(&child_type->name));
-
-        entry->data.meta_type.child_type = child_type;
-        child_type->meta_parent = entry;
-        return entry;
-    }
-}
-
 TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const) {
     assert(child_type->id != TypeTableEntryIdInvalid);
     TypeTableEntry **parent_pointer = &child_type->pointer_parent[(is_const ? 1 : 0)];
@@ -327,301 +311,36 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, ImportTableEntry
     }
 }
 
-// like analyze expression, but expects a type. creates an error if resulting type is
-// not a meta type. unwraps it if it is.
-static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        AstNode *node)
-{
-    TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, node);
-    if (type_entry->id == TypeTableEntryIdInvalid) {
-        return type_entry;
-    } else if (type_entry->id == TypeTableEntryIdMetaType) {
-        return type_entry->data.meta_type.child_type;
+// If the node does not have a constant expression value with a metatype, generates an error
+// and returns invalid type. Otherwise, returns the type of the constant expression value.
+// Must be called after analyze_expression on the same node.
+static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
+    Expr *expr = get_resolved_expr(node);
+    assert(expr->type_entry);
+    if (expr->type_entry->id == TypeTableEntryIdInvalid) {
+        return g->builtin_types.entry_invalid;
+    } else if (expr->type_entry->id == TypeTableEntryIdMetaType) {
+        // OK
     } else {
-        add_node_error(g, first_executing_node(node),
-            buf_sprintf("expected type, found expression"));
-        get_resolved_expr(node)->type_entry = g->builtin_types.entry_invalid;
+        add_node_error(g, node, buf_sprintf("expected type, found expression"));
         return g->builtin_types.entry_invalid;
     }
-}
-
-static void eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
-        AstNode *node, ConstExprValue *out_val)
-{
-    AstNode *op1_node = node->data.bin_op_expr.op1;
-    AstNode *op2_node = node->data.bin_op_expr.op2;
-    ConstExprValue op1_val = {0};
-    ConstExprValue op2_val = {0};
-    eval_const_expr(g, context, op1_node, &op1_val);
-    eval_const_expr(g, context, op2_node, &op2_val);
-
-    if (!op1_val.ok || !op2_val.ok) {
-        return;
-    }
 
-    TypeTableEntry *op1_type = get_resolved_expr(op1_node)->type_entry;
-    TypeTableEntry *op2_type = get_resolved_expr(op2_node)->type_entry;
-
-    // TODO complete more of this function instead of returning invalid
-    // returning invalid makes the "unable to evaluate constant expression" error
-
-    switch (node->data.bin_op_expr.bin_op) {
-        case BinOpTypeCmpNotEq:
-            {
-                if (op1_type->id == TypeTableEntryIdInt &&
-                    op2_type->id == TypeTableEntryIdInt)
-                {
-                    out_val->data.x_bool = (op1_val.data.x_uint == op2_val.data.x_uint);
-                    out_val->ok = true;
-                }
-                break;
-            }
-        case BinOpTypeCmpLessThan:
-            {
-                if (op1_type->id == TypeTableEntryIdInt &&
-                    op2_type->id == TypeTableEntryIdInt)
-                {
-                    if (op1_type->data.integral.is_signed &&
-                        op2_type->data.integral.is_signed)
-                    {
-                        out_val->data.x_bool = (op1_val.data.x_int < op2_val.data.x_int);
-                        out_val->ok = true;
-                    } else if (!op1_type->data.integral.is_signed &&
-                               !op2_type->data.integral.is_signed)
-                    {
-                        out_val->data.x_bool = (op1_val.data.x_uint < op2_val.data.x_uint);
-                        out_val->ok = true;
-                    }
-                }
-                break;
-            }
-        case BinOpTypeMod:
-            {
-                if (op1_type->id == TypeTableEntryIdInt &&
-                    op2_type->id == TypeTableEntryIdInt)
-                {
-                    if (op1_type->data.integral.is_signed &&
-                        op2_type->data.integral.is_signed)
-                    {
-                        out_val->data.x_int = op1_val.data.x_int % op2_val.data.x_int;
-                        out_val->ok = true;
-                    } else if (!op1_type->data.integral.is_signed &&
-                               !op2_type->data.integral.is_signed)
-                    {
-                        out_val->data.x_uint = op1_val.data.x_uint % op2_val.data.x_uint;
-                        out_val->ok = true;
-                    }
-                }
-                break;
-            }
-        case BinOpTypeBoolOr:
-        case BinOpTypeBoolAnd:
-        case BinOpTypeCmpEq:
-        case BinOpTypeCmpGreaterThan:
-        case BinOpTypeCmpLessOrEq:
-        case BinOpTypeCmpGreaterOrEq:
-        case BinOpTypeBinOr:
-        case BinOpTypeBinXor:
-        case BinOpTypeBinAnd:
-        case BinOpTypeBitShiftLeft:
-        case BinOpTypeBitShiftRight:
-        case BinOpTypeAdd:
-        case BinOpTypeSub:
-        case BinOpTypeMult:
-        case BinOpTypeDiv:
-        case BinOpTypeUnwrapMaybe:
-            break;
-        case BinOpTypeInvalid:
-        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:
-            zig_unreachable();
-    }
-}
-
-static void eval_const_expr_builtin(CodeGen *g, BlockContext *context, AstNode *node, ConstExprValue *out_val) {
-    switch (node->data.fn_call_expr.builtin_fn->id) {
-        case BuiltinFnIdInvalid:
-            zig_unreachable();
-        case BuiltinFnIdAddWithOverflow:
-        case BuiltinFnIdSubWithOverflow:
-        case BuiltinFnIdMulWithOverflow:
-        case BuiltinFnIdMemcpy:
-        case BuiltinFnIdMemset:
-            break;
-        case BuiltinFnIdSizeof:
-            {
-                AstNode *type_node = node->data.fn_call_expr.params.at(0);
-                TypeTableEntry *target_type = unwrapped_node_type(type_node);
-                out_val->data.x_uint = target_type->size_in_bits / 8;
-                out_val->ok = true;
-                break;
-            }
-        case BuiltinFnIdMaxValue:
-        case BuiltinFnIdMinValue:
-            zig_panic("TODO eval_const_expr_fn_call max/min value");
-        case BuiltinFnIdValueCount:
-            zig_panic("TODO eval_const_expr_fn_call value_count");
-        case BuiltinFnIdTypeof:
-            // TODO
-            out_val->ok = true;
-            break;
-    }
-}
-
-static void eval_const_expr_fn_call_known(CodeGen *g, BlockContext *context,
-        AstNode *node, ConstExprValue *out_val)
-{
-    // currently no functions can be constant expression evaluated,
-    // so we do nothing
-}
-
-static void eval_const_expr_fn_call_cast(CodeGen *g, BlockContext *context,
-        AstNode *node, ConstExprValue *out_val)
-{
-    assert(node->type == NodeTypeFnCallExpr);
-    AstNode *expr_node = node->data.fn_call_expr.params.at(0);
-    Cast *cast = &node->data.fn_call_expr.cast;
-    switch (cast->op) {
-        case CastOpNothing:
-        case CastOpPtrToInt:
-        case CastOpPointerReinterpret:
-        case CastOpIntWidenOrShorten:
-            {
-                eval_const_expr(g, context, expr_node, out_val);
-                break;
-            }
-        case CastOpMaybeWrap:
-            {
-                ConstExprValue *child_val = allocate<ConstExprValue>(1);
-                eval_const_expr(g, context, expr_node, child_val);
-                if (!child_val->ok) {
-                    return;
-                }
-                out_val->data.x_maybe.child_val = child_val;
-                out_val->data.x_maybe.is_null = false;
-                out_val->ok = true;
-                break;
-            }
-        case CastOpToUnknownSizeArray:
-            zig_panic("TODO eval_const_expr CastOpToUnknownSizeArray");
+    ConstExprValue *const_val = &expr->const_val;
+    if (!const_val->ok) {
+        add_node_error(g, node, buf_sprintf("unable to resolve constant expression"));
+        return g->builtin_types.entry_invalid;
     }
-}
 
-static void eval_const_expr_fn_call(CodeGen *g, BlockContext *context,
-        AstNode *node, ConstExprValue *out_val)
-{
-    if (node->data.fn_call_expr.is_builtin) {
-        return eval_const_expr_builtin(g, context, node, out_val);
-    }
-    if (node->data.fn_call_expr.fn_entry) {
-        return eval_const_expr_fn_call_known(g, context, node, out_val);
-    }
-    return eval_const_expr_fn_call_cast(g, context, node, out_val);
+    return const_val->data.x_type;
 }
 
-static void eval_const_expr_prefix_op_expr(CodeGen *g, BlockContext *context, AstNode *node,
-        ConstExprValue *out_val)
+// Calls analyze_expression on node, and then resolve_type.
+static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        AstNode *node)
 {
-    AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
-    switch (node->data.prefix_op_expr.prefix_op) {
-        case PrefixOpInvalid:
-            zig_unreachable();
-        case PrefixOpBoolNot:
-            {
-                eval_const_expr(g, context, expr_node, out_val);
-                if (out_val->ok) {
-                    out_val->data.x_bool = !out_val->data.x_bool;
-                }
-                break;
-            }
-        case PrefixOpBinNot:
-            break;
-        case PrefixOpNegation:
-            break;
-        case PrefixOpAddressOf:
-            {
-                if (get_resolved_expr(node)->type_entry->id == TypeTableEntryIdMetaType) {
-                    eval_const_expr(g, context, expr_node, out_val);
-                }
-                break;
-            }
-        case PrefixOpConstAddressOf:
-            break;
-        case PrefixOpDereference:
-            break;
-        case PrefixOpMaybe:
-            break;
-    }
-}
-
-static void eval_const_expr(CodeGen *g, BlockContext *context, AstNode *node, ConstExprValue *out_val) {
-    switch (node->type) {
-        case NodeTypeNumberLiteral:
-            {
-                if (is_num_lit_unsigned(node->data.number_literal.kind)) {
-                    out_val->data.x_uint = node->data.number_literal.data.x_uint;
-                    out_val->ok = true;
-                } else if (is_num_lit_float(node->data.number_literal.kind)) {
-                    out_val->data.x_uint = node->data.number_literal.data.x_float;
-                    out_val->ok = true;
-                }
-                break;
-            }
-        case NodeTypeBoolLiteral:
-            out_val->data.x_uint = node->data.bool_literal.value ? 1 : 0;
-            out_val->ok = true;
-            break;
-        case NodeTypeNullLiteral:
-            out_val->data.x_maybe.is_null = true;
-            out_val->ok = true;
-            break;
-        case NodeTypeBinOpExpr:
-            eval_const_expr_bin_op(g, context, node, out_val);
-            break;
-        case NodeTypeSymbol:
-            {
-                VariableTableEntry *var = node->data.symbol_expr.variable;
-                if (var) {
-                    if (var->is_const) {
-                        AstNode *decl_node = var->decl_node;
-                        if (decl_node->type == NodeTypeVariableDeclaration) {
-                            AstNode *expr_node = decl_node->data.variable_declaration.expr;
-                            if (expr_node) {
-                                BlockContext *next_context = get_resolved_expr(expr_node)->block_context;
-                                eval_const_expr(g, next_context, expr_node, out_val);
-                            }
-                        }
-                    }
-                } else if (node->data.symbol_expr.meta_type) {
-                    out_val->ok = true;
-                } else if (node->data.symbol_expr.fn_entry) {
-                    out_val->ok = true;
-                    out_val->data.x_fn = node->data.symbol_expr.fn_entry;
-                } else {
-                    zig_unreachable();
-                }
-                break;
-            }
-        case NodeTypeFnCallExpr:
-            eval_const_expr_fn_call(g, context, node, out_val);
-            break;
-        case NodeTypePrefixOpExpr:
-            eval_const_expr_prefix_op_expr(g, context, node, out_val);
-            break;
-        default:
-            break;
-    }
+    analyze_expression(g, import, context, nullptr, node);
+    return resolve_type(g, node);
 }
 
 static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry,
@@ -662,9 +381,8 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
 
     fn_table_entry->type_entry = fn_type;
 
-    Buf *name = &node->data.fn_proto.name;
     buf_resize(&fn_type->name, 0);
-    buf_appendf(&fn_type->name, "fn %s(", buf_ptr(name));
+    buf_appendf(&fn_type->name, "fn(");
     for (int i = 0; i < param_count; i += 1) {
         AstNode *child = node->data.fn_proto.params.at(i);
         assert(child->type == NodeTypeParamDecl);
@@ -672,8 +390,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
                 child->data.param_decl.type);
         fn_table_entry->type_entry->data.fn.param_types[i] = type_entry;
 
-        buf_appendf(&fn_type->name, "%s : %s",
-                buf_ptr(&child->data.param_decl.name), buf_ptr(&type_entry->name));
+        buf_appendf(&fn_type->name, "%s", buf_ptr(&type_entry->name));
 
         if (i + 1 < param_count) {
             buf_appendf(&fn_type->name, ", ");
@@ -1212,13 +929,14 @@ static FnTableEntry *get_context_fn_entry(BlockContext *context) {
 }
 
 static TypeTableEntry *unwrapped_node_type(AstNode *node) {
-    TypeTableEntry *meta_type_entry = get_resolved_expr(node)->type_entry;
-    if (meta_type_entry->id == TypeTableEntryIdInvalid) {
-        return meta_type_entry;
-    } else {
-        assert(meta_type_entry->id == TypeTableEntryIdMetaType);
-        return meta_type_entry->data.meta_type.child_type;
+    Expr *expr = get_resolved_expr(node);
+    if (expr->type_entry->id == TypeTableEntryIdInvalid) {
+        return expr->type_entry;
     }
+    assert(expr->type_entry->id == TypeTableEntryIdMetaType);
+    ConstExprValue *const_val = &expr->const_val;
+    assert(const_val->ok);
+    return const_val->data.x_type;
 }
 
 static TypeTableEntry *get_return_type(BlockContext *context) {
@@ -1704,8 +1422,6 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
     AstNode *struct_expr_node = node->data.field_access_expr.struct_expr;
     TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, struct_expr_node);
 
-    TypeTableEntry *return_type;
-
     if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer &&
          struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))
     {
@@ -1716,40 +1432,45 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
 
         node->data.field_access_expr.type_struct_field = get_struct_field(bare_struct_type, field_name);
         if (node->data.field_access_expr.type_struct_field) {
-            return_type = node->data.field_access_expr.type_struct_field->type_entry;
+            return node->data.field_access_expr.type_struct_field->type_entry;
         } else {
             add_node_error(g, node,
                 buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name)));
-            return_type = g->builtin_types.entry_invalid;
+            return g->builtin_types.entry_invalid;
         }
     } else if (struct_type->id == TypeTableEntryIdArray) {
         Buf *name = &node->data.field_access_expr.field_name;
         if (buf_eql_str(name, "len")) {
-            return_type = g->builtin_types.entry_usize;
+            return g->builtin_types.entry_usize;
         } else if (buf_eql_str(name, "ptr")) {
             // TODO determine whether the pointer should be const
-            return_type = get_pointer_to_type(g, struct_type->data.array.child_type, false);
+            return get_pointer_to_type(g, struct_type->data.array.child_type, false);
         } else {
             add_node_error(g, node,
                 buf_sprintf("no member named '%s' in '%s'", buf_ptr(name),
                     buf_ptr(&struct_type->name)));
-            return_type = g->builtin_types.entry_invalid;
+            return g->builtin_types.entry_invalid;
+        }
+    } else if (struct_type->id == TypeTableEntryIdMetaType) {
+        TypeTableEntry *enum_type = resolve_type(g, struct_expr_node);
+
+        if (enum_type->id == TypeTableEntryIdInvalid) {
+            return g->builtin_types.entry_invalid;
+        } else if (enum_type->id == TypeTableEntryIdEnum) {
+            Buf *field_name = &node->data.field_access_expr.field_name;
+            return analyze_enum_value_expr(g, import, context, node, nullptr, enum_type, field_name);
+        } else {
+            add_node_error(g, node,
+                buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name)));
+            return g->builtin_types.entry_invalid;
         }
-    } else if (struct_type->id == TypeTableEntryIdMetaType &&
-               struct_type->data.meta_type.child_type->id == TypeTableEntryIdEnum)
-    {
-        TypeTableEntry *enum_type = struct_type->data.meta_type.child_type;
-        Buf *field_name = &node->data.field_access_expr.field_name;
-        return_type = analyze_enum_value_expr(g, import, context, node, nullptr, enum_type, field_name);
     } else {
         if (struct_type->id != TypeTableEntryIdInvalid) {
             add_node_error(g, node,
                 buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name)));
         }
-        return_type = g->builtin_types.entry_invalid;
+        return g->builtin_types.entry_invalid;
     }
-
-    return return_type;
 }
 
 static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
@@ -1826,6 +1547,52 @@ static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *i
     return return_type;
 }
 
+static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node, TypeTableEntry *type) {
+    Expr *expr = get_resolved_expr(node);
+    expr->const_val.ok = true;
+    expr->const_val.data.x_type = type;
+    return g->builtin_types.entry_type;
+}
+
+static TypeTableEntry *resolve_expr_const_val_as_other_expr(CodeGen *g, AstNode *node, AstNode *other) {
+    Expr *expr = get_resolved_expr(node);
+    Expr *other_expr = get_resolved_expr(other);
+    expr->const_val = other_expr->const_val;
+    return other_expr->type_entry;
+}
+
+static TypeTableEntry *resolve_expr_const_val_as_fn(CodeGen *g, AstNode *node, FnTableEntry *fn) {
+    Expr *expr = get_resolved_expr(node);
+    expr->const_val.ok = true;
+    expr->const_val.data.x_fn = fn;
+    return fn->type_entry;
+}
+
+static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value) {
+    Expr *expr = get_resolved_expr(node);
+    expr->const_val.ok = true;
+    expr->const_val.data.x_bool = value;
+    return g->builtin_types.entry_bool;
+}
+
+static TypeTableEntry *resolve_expr_const_val_as_null(CodeGen *g, AstNode *node, TypeTableEntry *type) {
+    Expr *expr = get_resolved_expr(node);
+    expr->const_val.ok = true;
+    expr->const_val.data.x_maybe = nullptr;
+    return type;
+}
+
+static TypeTableEntry *resolve_expr_const_val_as_unsigned_num_lit(CodeGen *g, AstNode *node,
+        TypeTableEntry *expected_type, uint64_t x)
+{
+    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;
+}
+
 static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
@@ -1833,28 +1600,33 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import,
 
     auto primitive_table_entry = g->primitive_type_table.maybe_get(variable_name);
     if (primitive_table_entry) {
-        TypeTableEntry *meta_type = get_meta_type(g, primitive_table_entry->value);
-        node->data.symbol_expr.meta_type = meta_type;
-        return meta_type;
+        return resolve_expr_const_val_as_type(g, node, primitive_table_entry->value);
     }
 
     VariableTableEntry *var = find_variable(context, variable_name);
     if (var) {
         node->data.symbol_expr.variable = var;
+        if (var->is_const) {
+            AstNode *decl_node = var->decl_node;
+            if (decl_node->type == NodeTypeVariableDeclaration) {
+                AstNode *expr_node = decl_node->data.variable_declaration.expr;
+                ConstExprValue *const_val = &get_resolved_expr(expr_node)->const_val;
+                if (const_val->ok) {
+                    return resolve_expr_const_val_as_other_expr(g, node, expr_node);
+                }
+            }
+        }
         return var->type;
     }
 
     TypeTableEntry *container_type = find_container(context, variable_name);
     if (container_type) {
-        TypeTableEntry *meta_type = get_meta_type(g, container_type);
-        node->data.symbol_expr.meta_type = meta_type;
-        return meta_type;
+        return resolve_expr_const_val_as_type(g, node, container_type);
     }
 
     auto fn_table_entry = import->fn_table.maybe_get(variable_name);
     if (fn_table_entry) {
-        node->data.symbol_expr.fn_entry = fn_table_entry->value;
-        return node->data.symbol_expr.fn_entry->type_entry;
+        return resolve_expr_const_val_as_fn(g, node, fn_table_entry->value);
     }
 
     add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
@@ -1990,10 +1762,148 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc
     return expected_rhs_type;
 }
 
+static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) {
+    if (bin_op == BinOpTypeBoolOr) {
+        return a || b;
+    } else if (bin_op == BinOpTypeBoolAnd) {
+        return a && b;
+    } else {
+        zig_unreachable();
+    }
+}
+
+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)
+{
+    assert(node->type == NodeTypeBinOpExpr);
+    BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
+
+    AstNode *op1 = node->data.bin_op_expr.op1;
+    AstNode *op2 = node->data.bin_op_expr.op2;
+    TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, op1);
+    TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, op2);
+
+    TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, context, node,
+            op1, op2, op1_type, op2_type);
+
+    if (resolved_type->id == TypeTableEntryIdInvalid) {
+        return g->builtin_types.entry_invalid;
+    }
+
+    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 g->builtin_types.entry_bool;
+    }
+
+    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);
+        } 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);
+    } else {
+        zig_unreachable();
+    }
+
+    return resolve_expr_const_val_as_bool(g, node, answer);
+}
+
+static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        AstNode *node)
+{
+    assert(node->type == NodeTypeBinOpExpr);
+    BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
+
+    AstNode *op1 = node->data.bin_op_expr.op1;
+    AstNode *op2 = node->data.bin_op_expr.op2;
+    TypeTableEntry *op1_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, op1);
+    TypeTableEntry *op2_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, op2);
+
+    if (op1_type->id == TypeTableEntryIdInvalid ||
+        op2_type->id == TypeTableEntryIdInvalid)
+    {
+        return g->builtin_types.entry_invalid;
+    }
+
+    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 g->builtin_types.entry_bool;
+    }
+
+    bool answer = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op_type, op2_val->data.x_bool);
+    return resolve_expr_const_val_as_bool(g, node, answer);
+}
+
 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) {
+    BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
+    switch (bin_op_type) {
         case BinOpTypeAssign:
         case BinOpTypeAssignTimes:
         case BinOpTypeAssignDiv:
@@ -2025,25 +1935,14 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
             }
         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;
+            return analyze_logic_bin_op_expr(g, import, context, node);
         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);
-
-                resolve_peer_type_compatibility(g, context, node, op1, op2, lhs_type, rhs_type);
-
-                return g->builtin_types.entry_bool;
-            }
+            return analyze_bool_bin_op_expr(g, import, context, node);
         case BinOpTypeBinOr:
         case BinOpTypeBinXor:
         case BinOpTypeBinAnd:
@@ -2178,30 +2077,41 @@ static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *i
 {
     assert(node->type == NodeTypeNullLiteral);
 
-    if (expected_type) {
-        assert(expected_type->id == TypeTableEntryIdMaybe);
-
-        node->data.null_literal.resolved_struct_val_expr.type_entry = expected_type;
-        node->data.null_literal.resolved_struct_val_expr.source_node = node;
-        block_context->struct_val_expr_alloca_list.append(&node->data.null_literal.resolved_struct_val_expr);
-
-        return expected_type;
-    } else {
+    if (!expected_type) {
         add_node_error(g, node,
                 buf_sprintf("unable to determine null type"));
         return g->builtin_types.entry_invalid;
     }
+
+    assert(expected_type->id == TypeTableEntryIdMaybe);
+
+    node->data.null_literal.resolved_struct_val_expr.type_entry = expected_type;
+    node->data.null_literal.resolved_struct_val_expr.source_node = node;
+    block_context->struct_val_expr_alloca_list.append(&node->data.null_literal.resolved_struct_val_expr);
+
+    return resolve_expr_const_val_as_null(g, node, expected_type);
 }
 
 static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import,
         BlockContext *block_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"));
+        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) {
+    }
+
+    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;
+    } 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 =
@@ -2235,18 +2145,17 @@ static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import,
             return g->builtin_types.entry_invalid;
         }
 
-        ConstExprValue const_val = {0};
-        eval_const_expr(g, context, size_node, &const_val);
-
-        if (const_val.ok) {
-            return get_meta_type(g, get_array_type(g, import, child_type, const_val.data.x_uint));
+        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, import, child_type, const_val->data.x_uint));
         } else {
-            add_node_error(g, size_node,
-                buf_create_from_str("unable to resolve constant expression"));
+            add_node_error(g, size_node, buf_create_from_str("unable to resolve constant expression"));
             return g->builtin_types.entry_invalid;
         }
     } else {
-        return get_meta_type(g, get_unknown_size_array_type(g, import, child_type, node->data.array_type.is_const));
+        return resolve_expr_const_val_as_type(g, node,
+                get_unknown_size_array_type(g, import, child_type, node->data.array_type.is_const));
     }
 }
 
@@ -2271,11 +2180,9 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import,
     } else {
         // if the condition is a simple constant expression and there are no break statements
         // then the return type is unreachable
-        ConstExprValue const_val = {0};
-        eval_const_expr(g, context, condition_node, &const_val);
-
-        if (const_val.ok) {
-            if (const_val.data.x_bool) {
+        ConstExprValue *const_val = &get_resolved_expr(condition_node)->const_val;
+        if (const_val->ok) {
+            if (const_val->data.x_bool) {
                 node->data.while_expr.condition_always_true = true;
                 if (!node->data.while_expr.contains_break) {
                     expr_return_type = g->builtin_types.entry_unreachable;
@@ -2360,19 +2267,23 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import,
 }
 
 static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        AstNode *node, const char *err_format)
+        AstNode *node, const char *err_format, bool is_max)
 {
     assert(node->type == NodeTypeFnCallExpr);
     assert(node->data.fn_call_expr.params.length == 1);
 
     AstNode *type_node = node->data.fn_call_expr.params.at(0);
     TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
-    if (type_entry->id == TypeTableEntryIdInt ||
-        type_entry->id == TypeTableEntryIdFloat ||
-        type_entry->id == TypeTableEntryIdBool ||
-        type_entry->id == TypeTableEntryIdInvalid)
-    {
+    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
         return type_entry;
+    } else if (type_entry->id == TypeTableEntryIdFloat) {
+        // TODO const expr eval for min/max float
+        return type_entry;
+    } else if (type_entry->id == TypeTableEntryIdBool) {
+        return resolve_expr_const_val_as_bool(g, node, is_max);
     } else {
         add_node_error(g, node,
                 buf_sprintf(err_format, buf_ptr(&type_entry->name)));
@@ -2380,8 +2291,46 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
     }
 }
 
+static void eval_const_expr_implicit_cast(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        AstNode *node, Cast *cast, AstNode *expr_node)
+{
+    switch (cast->op) {
+        case CastOpNothing:
+        case CastOpPtrToInt:
+        case CastOpIntWidenOrShorten:
+        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;
+                }
+                break;
+            }
+        case CastOpToUnknownSizeArray:
+            // TODO eval const expr
+            break;
+        case CastOpMaybeWrap:
+            {
+                ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val;
+                ConstExprValue *const_val = &get_resolved_expr(node)->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;
+                }
+
+                const_val->data.x_maybe = other_val;
+                const_val->ok = true;
+                break;
+            }
+    }
+}
+
 static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        AstNode *node, TypeTableEntry *invoke_type_entry)
+        AstNode *node)
 {
     assert(node->type == NodeTypeFnCallExpr);
 
@@ -2394,7 +2343,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
     }
 
     AstNode *expr_node = node->data.fn_call_expr.params.at(0);
-    TypeTableEntry *wanted_type = invoke_type_entry->data.meta_type.child_type;
+    TypeTableEntry *wanted_type = resolve_type(g, fn_ref_expr);
     TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr, expr_node);
 
     if (wanted_type->id == TypeTableEntryIdInvalid ||
@@ -2411,11 +2360,13 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
         actual_type->id == TypeTableEntryIdPointer)
     {
         cast->op = CastOpPtrToInt;
+        eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
         return wanted_type;
     } else if (wanted_type->id == TypeTableEntryIdInt &&
                 actual_type->id == TypeTableEntryIdInt)
     {
         cast->op = CastOpIntWidenOrShorten;
+        eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
         return wanted_type;
     } else if (wanted_type->id == TypeTableEntryIdStruct &&
                wanted_type->data.structure.is_unknown_size_array &&
@@ -2424,6 +2375,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
     {
         cast->op = CastOpToUnknownSizeArray;
         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))
@@ -2432,11 +2384,13 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
         assert(!codegen_num_lit->resolved_type);
         codegen_num_lit->resolved_type = wanted_type;
         cast->op = CastOpNothing;
+        eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
         return wanted_type;
     } else if (actual_type->id == TypeTableEntryIdPointer &&
                wanted_type->id == TypeTableEntryIdPointer)
     {
         cast->op = CastOpPointerReinterpret;
+        eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
         return wanted_type;
     } else {
         add_node_error(g, node,
@@ -2457,165 +2411,160 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
 
     auto entry = g->builtin_fn_table.maybe_get(name);
 
-    if (entry) {
-        BuiltinFnEntry *builtin_fn = entry->value;
-        int actual_param_count = node->data.fn_call_expr.params.length;
+    if (!entry) {
+        add_node_error(g, node,
+                buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
+        return g->builtin_types.entry_invalid;
+    }
 
-        node->data.fn_call_expr.builtin_fn = builtin_fn;
+    BuiltinFnEntry *builtin_fn = entry->value;
+    int actual_param_count = node->data.fn_call_expr.params.length;
 
-        if (builtin_fn->param_count != actual_param_count) {
-            add_node_error(g, node,
-                    buf_sprintf("expected %d arguments, got %d",
-                        builtin_fn->param_count, actual_param_count));
-            return g->builtin_types.entry_invalid;
-        }
+    node->data.fn_call_expr.builtin_fn = builtin_fn;
 
-        switch (builtin_fn->id) {
-            case BuiltinFnIdInvalid:
-                zig_unreachable();
-            case BuiltinFnIdAddWithOverflow:
-            case BuiltinFnIdSubWithOverflow:
-            case BuiltinFnIdMulWithOverflow:
-                {
-                    AstNode *type_node = node->data.fn_call_expr.params.at(0);
-                    TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node);
-                    if (int_type->id == TypeTableEntryIdInvalid) {
-                        return g->builtin_types.entry_bool;
-                    } else if (int_type->id == TypeTableEntryIdInt) {
-                        AstNode *op1_node = node->data.fn_call_expr.params.at(1);
-                        AstNode *op2_node = node->data.fn_call_expr.params.at(2);
-                        AstNode *result_node = node->data.fn_call_expr.params.at(3);
-
-                        analyze_expression(g, import, context, int_type, op1_node);
-                        analyze_expression(g, import, context, int_type, op2_node);
-                        analyze_expression(g, import, context, get_pointer_to_type(g, int_type, false),
-                                result_node);
-                    } else {
-                        add_node_error(g, type_node,
-                            buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name)));
-                    }
+    if (builtin_fn->param_count != actual_param_count) {
+        add_node_error(g, node,
+                buf_sprintf("expected %d arguments, got %d",
+                    builtin_fn->param_count, actual_param_count));
+        return g->builtin_types.entry_invalid;
+    }
 
+    switch (builtin_fn->id) {
+        case BuiltinFnIdInvalid:
+            zig_unreachable();
+        case BuiltinFnIdAddWithOverflow:
+        case BuiltinFnIdSubWithOverflow:
+        case BuiltinFnIdMulWithOverflow:
+            {
+                AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                TypeTableEntry *int_type = analyze_type_expr(g, import, context, type_node);
+                if (int_type->id == TypeTableEntryIdInvalid) {
                     return g->builtin_types.entry_bool;
+                } else if (int_type->id == TypeTableEntryIdInt) {
+                    AstNode *op1_node = node->data.fn_call_expr.params.at(1);
+                    AstNode *op2_node = node->data.fn_call_expr.params.at(2);
+                    AstNode *result_node = node->data.fn_call_expr.params.at(3);
+
+                    analyze_expression(g, import, context, int_type, op1_node);
+                    analyze_expression(g, import, context, int_type, op2_node);
+                    analyze_expression(g, import, context, get_pointer_to_type(g, int_type, false),
+                            result_node);
+                } else {
+                    add_node_error(g, type_node,
+                        buf_sprintf("expected integer type, got '%s'", buf_ptr(&int_type->name)));
                 }
-            case BuiltinFnIdMemcpy:
-                {
-                    AstNode *dest_node = node->data.fn_call_expr.params.at(0);
-                    AstNode *src_node = node->data.fn_call_expr.params.at(1);
-                    AstNode *len_node = node->data.fn_call_expr.params.at(2);
-                    TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
-                    TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, src_node);
-                    analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
-
-                    if (dest_type->id != TypeTableEntryIdInvalid &&
-                        dest_type->id != TypeTableEntryIdPointer)
-                    {
-                        add_node_error(g, dest_node,
-                                buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name)));
-                    }
-
-                    if (src_type->id != TypeTableEntryIdInvalid &&
-                        src_type->id != TypeTableEntryIdPointer)
-                    {
-                        add_node_error(g, src_node,
-                                buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&src_type->name)));
-                    }
 
-                    if (dest_type->id == TypeTableEntryIdPointer &&
-                        src_type->id == TypeTableEntryIdPointer)
-                    {
-                        uint64_t dest_align_bits = dest_type->data.pointer.child_type->align_in_bits;
-                        uint64_t src_align_bits = src_type->data.pointer.child_type->align_in_bits;
-                        if (dest_align_bits != src_align_bits) {
-                            add_node_error(g, dest_node, buf_sprintf(
-                                "misaligned memcpy, '%s' has alignment '%" PRIu64 ", '%s' has alignment %" PRIu64,
-                                        buf_ptr(&dest_type->name), dest_align_bits / 8,
-                                        buf_ptr(&src_type->name), src_align_bits / 8));
-                        }
-                    }
+                // TODO constant expression evaluation
 
-                    return builtin_fn->return_type;
-                }
-            case BuiltinFnIdMemset:
+                return g->builtin_types.entry_bool;
+            }
+        case BuiltinFnIdMemcpy:
+            {
+                AstNode *dest_node = node->data.fn_call_expr.params.at(0);
+                AstNode *src_node = node->data.fn_call_expr.params.at(1);
+                AstNode *len_node = node->data.fn_call_expr.params.at(2);
+                TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
+                TypeTableEntry *src_type = analyze_expression(g, import, context, nullptr, src_node);
+                analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
+
+                if (dest_type->id != TypeTableEntryIdInvalid &&
+                    dest_type->id != TypeTableEntryIdPointer)
                 {
-                    AstNode *dest_node = node->data.fn_call_expr.params.at(0);
-                    AstNode *char_node = node->data.fn_call_expr.params.at(1);
-                    AstNode *len_node = node->data.fn_call_expr.params.at(2);
-                    TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
-                    analyze_expression(g, import, context, builtin_fn->param_types[1], char_node);
-                    analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
-
-                    if (dest_type->id != TypeTableEntryIdInvalid &&
-                        dest_type->id != TypeTableEntryIdPointer)
-                    {
-                        add_node_error(g, dest_node,
-                                buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name)));
-                    }
-
-                    return builtin_fn->return_type;
+                    add_node_error(g, dest_node,
+                            buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name)));
                 }
-            case BuiltinFnIdSizeof:
+
+                if (src_type->id != TypeTableEntryIdInvalid &&
+                    src_type->id != TypeTableEntryIdPointer)
                 {
-                    AstNode *type_node = node->data.fn_call_expr.params.at(0);
-                    TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
-                    if (type_entry->id == TypeTableEntryIdInvalid) {
-                        return g->builtin_types.entry_invalid;
-                    } else if (type_entry->id == TypeTableEntryIdUnreachable) {
-                        add_node_error(g, first_executing_node(type_node),
-                                buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
-                        return g->builtin_types.entry_invalid;
-                    } else {
-                        uint64_t size_in_bytes = type_entry->size_in_bits / 8;
+                    add_node_error(g, src_node,
+                            buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&src_type->name)));
+                }
 
-                        TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, size_in_bytes);
-                        TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type);
-                        return resolved_type ? resolved_type : num_lit_type;
+                if (dest_type->id == TypeTableEntryIdPointer &&
+                    src_type->id == TypeTableEntryIdPointer)
+                {
+                    uint64_t dest_align_bits = dest_type->data.pointer.child_type->align_in_bits;
+                    uint64_t src_align_bits = src_type->data.pointer.child_type->align_in_bits;
+                    if (dest_align_bits != src_align_bits) {
+                        add_node_error(g, dest_node, buf_sprintf(
+                            "misaligned memcpy, '%s' has alignment '%" PRIu64 ", '%s' has alignment %" PRIu64,
+                                    buf_ptr(&dest_type->name), dest_align_bits / 8,
+                                    buf_ptr(&src_type->name), src_align_bits / 8));
                     }
                 }
-            case BuiltinFnIdMaxValue:
-                return analyze_min_max_value(g, import, context, node, "no max value available for type '%s'");
-            case BuiltinFnIdMinValue:
-                return analyze_min_max_value(g, import, context, node, "no min value available for type '%s'");
-            case BuiltinFnIdValueCount:
-                {
-                    AstNode *type_node = node->data.fn_call_expr.params.at(0);
-                    TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
 
-                    if (type_entry->id == TypeTableEntryIdInvalid) {
-                        return type_entry;
-                    } else if (type_entry->id == TypeTableEntryIdEnum) {
-                        uint64_t value_count = type_entry->data.enumeration.field_count;
+                return builtin_fn->return_type;
+            }
+        case BuiltinFnIdMemset:
+            {
+                AstNode *dest_node = node->data.fn_call_expr.params.at(0);
+                AstNode *char_node = node->data.fn_call_expr.params.at(1);
+                AstNode *len_node = node->data.fn_call_expr.params.at(2);
+                TypeTableEntry *dest_type = analyze_expression(g, import, context, nullptr, dest_node);
+                analyze_expression(g, import, context, builtin_fn->param_types[1], char_node);
+                analyze_expression(g, import, context, builtin_fn->param_types[2], len_node);
+
+                if (dest_type->id != TypeTableEntryIdInvalid &&
+                    dest_type->id != TypeTableEntryIdPointer)
+                {
+                    add_node_error(g, dest_node,
+                            buf_sprintf("expected pointer argument, got '%s'", buf_ptr(&dest_type->name)));
+                }
 
-                        TypeTableEntry *num_lit_type = get_number_literal_type_unsigned(g, value_count);
-                        TypeTableEntry *resolved_type = resolve_rhs_number_literal(g, nullptr, expected_type, node, num_lit_type);
-                        return resolved_type ? resolved_type : num_lit_type;
+                return builtin_fn->return_type;
+            }
+        case BuiltinFnIdSizeof:
+            {
+                AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
+                if (type_entry->id == TypeTableEntryIdInvalid) {
+                    return g->builtin_types.entry_invalid;
+                } else if (type_entry->id == TypeTableEntryIdUnreachable) {
+                    add_node_error(g, first_executing_node(type_node),
+                            buf_sprintf("no size available for type '%s'", buf_ptr(&type_entry->name)));
+                    return g->builtin_types.entry_invalid;
+                } else {
+                    uint64_t size_in_bytes = type_entry->size_in_bits / 8;
+                    return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, size_in_bytes);
+                }
+            }
+        case BuiltinFnIdMaxValue:
+            return analyze_min_max_value(g, import, context, node,
+                    "no max value available for type '%s'", true);
+        case BuiltinFnIdMinValue:
+            return analyze_min_max_value(g, import, context, node,
+                    "no min value available for type '%s'", false);
+        case BuiltinFnIdValueCount:
+            {
+                AstNode *type_node = node->data.fn_call_expr.params.at(0);
+                TypeTableEntry *type_entry = analyze_type_expr(g, import, context, type_node);
 
-                    } else {
-                        add_node_error(g, node,
-                                buf_sprintf("no value count available for type '%s'", buf_ptr(&type_entry->name)));
-                        return g->builtin_types.entry_invalid;
-                    }
+                if (type_entry->id == TypeTableEntryIdInvalid) {
+                    return type_entry;
+                } else if (type_entry->id == TypeTableEntryIdEnum) {
+                    uint64_t value_count = type_entry->data.enumeration.field_count;
+                    return resolve_expr_const_val_as_unsigned_num_lit(g, node, expected_type, value_count);
+                } else {
+                    add_node_error(g, node,
+                            buf_sprintf("no value count available for type '%s'", buf_ptr(&type_entry->name)));
+                    return g->builtin_types.entry_invalid;
                 }
-            case BuiltinFnIdTypeof:
-                {
-                    AstNode *expr_node = node->data.fn_call_expr.params.at(0);
-                    TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
+            }
+        case BuiltinFnIdTypeof:
+            {
+                AstNode *expr_node = node->data.fn_call_expr.params.at(0);
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
 
-                    if (type_entry->id == TypeTableEntryIdInvalid) {
-                        return g->builtin_types.entry_invalid;
-                    } else if (type_entry->id == TypeTableEntryIdMetaType) {
-                        add_node_error(g, node, buf_sprintf("expected expression, got type"));
-                    } else {
-                        return get_meta_type(g, type_entry);
-                    }
+                if (type_entry->id == TypeTableEntryIdInvalid) {
+                    return g->builtin_types.entry_invalid;
+                } else {
+                    return resolve_expr_const_val_as_type(g, node, type_entry);
                 }
+            }
 
-        }
-        zig_unreachable();
-    } else {
-        add_node_error(g, node,
-                buf_sprintf("invalid builtin function: '%s'", buf_ptr(name)));
-        return g->builtin_types.entry_invalid;
     }
+    zig_unreachable();
 }
 
 static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context,
@@ -2707,30 +2656,35 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
             }
         } else if (struct_type->id == TypeTableEntryIdInvalid) {
             return struct_type;
-        } else if (struct_type->id == TypeTableEntryIdMetaType &&
-                   struct_type->data.meta_type.child_type->id == TypeTableEntryIdEnum)
-        {
-            TypeTableEntry *enum_type = struct_type->data.meta_type.child_type;
-            Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name;
-            int param_count = node->data.fn_call_expr.params.length;
-            if (param_count > 1) {
-                add_node_error(g, first_executing_node(node->data.fn_call_expr.params.at(1)),
-                        buf_sprintf("enum values accept only one parameter"));
-                return enum_type;
-            } else {
-                AstNode *value_node;
-                if (param_count == 1) {
-                    value_node = node->data.fn_call_expr.params.at(0);
+        } else if (struct_type->id == TypeTableEntryIdMetaType) {
+            TypeTableEntry *enum_type = resolve_type(g, first_param_expr);
+
+            if (enum_type->id == TypeTableEntryIdInvalid) {
+                return g->builtin_types.entry_invalid;
+            } else if (enum_type->id == TypeTableEntryIdEnum) {
+                Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name;
+                int param_count = node->data.fn_call_expr.params.length;
+                if (param_count > 1) {
+                    add_node_error(g, first_executing_node(node->data.fn_call_expr.params.at(1)),
+                            buf_sprintf("enum values accept only one parameter"));
+                    return enum_type;
                 } else {
-                    value_node = nullptr;
-                }
+                    AstNode *value_node;
+                    if (param_count == 1) {
+                        value_node = node->data.fn_call_expr.params.at(0);
+                    } else {
+                        value_node = nullptr;
+                    }
 
-                return analyze_enum_value_expr(g, import, context, fn_ref_expr, value_node,
-                        enum_type, field_name);
+                    return analyze_enum_value_expr(g, import, context, fn_ref_expr, value_node,
+                            enum_type, field_name);
+                }
+            } else {
+                add_node_error(g, first_param_expr, buf_sprintf("member reference base type not struct or enum"));
+                return g->builtin_types.entry_invalid;
             }
         } else {
-            add_node_error(g, fn_ref_expr->data.field_access_expr.struct_expr,
-                    buf_sprintf("member reference base type not struct or enum"));
+            add_node_error(g, first_param_expr, buf_sprintf("member reference base type not struct or enum"));
             return g->builtin_types.entry_invalid;
         }
     }
@@ -2742,18 +2696,17 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
 
     // use constant expression evaluator to figure out the function at compile time.
     // otherwise we treat this as a function pointer.
-    ConstExprValue const_val = {0};
-    eval_const_expr(g, context, fn_ref_expr, &const_val);
+    ConstExprValue *const_val = &get_resolved_expr(fn_ref_expr)->const_val;
 
-    if (!const_val.ok) {
+    if (!const_val->ok) {
         add_node_error(g, node, buf_sprintf("function pointers not yet supported"));
         return g->builtin_types.entry_invalid;
     }
 
     if (invoke_type_entry->id == TypeTableEntryIdMetaType) {
-        return analyze_cast_expr(g, import, context, node, invoke_type_entry);
+        return analyze_cast_expr(g, import, context, node);
     } else if (invoke_type_entry->id == TypeTableEntryIdFn) {
-        return analyze_fn_call_raw(g, import, context, expected_type, node, const_val.data.x_fn, nullptr);
+        return analyze_fn_call_raw(g, import, context, expected_type, node, const_val->data.x_fn, nullptr);
     } else {
         add_node_error(g, fn_ref_expr,
             buf_sprintf("type '%s' not a function", buf_ptr(&invoke_type_entry->name)));
@@ -2764,18 +2717,31 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
 static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
-    switch (node->data.prefix_op_expr.prefix_op) {
+    PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op;
+    AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
+    switch (prefix_op) {
         case PrefixOpInvalid:
             zig_unreachable();
         case PrefixOpBoolNot:
-            analyze_expression(g, import, context, g->builtin_types.entry_bool,
-                    node->data.prefix_op_expr.primary_expr);
-            return g->builtin_types.entry_bool;
+            {
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, g->builtin_types.entry_bool,
+                        expr_node);
+                if (type_entry->id == TypeTableEntryIdInvalid) {
+                    return g->builtin_types.entry_bool;
+                }
+
+                ConstExprValue *target_const_val = &get_resolved_expr(expr_node)->const_val;
+                if (!target_const_val->ok) {
+                    return g->builtin_types.entry_bool;
+                }
+
+                bool answer = target_const_val->data.x_bool;
+                return resolve_expr_const_val_as_bool(g, node, answer);
+            }
         case PrefixOpBinNot:
             {
-                AstNode *operand_node = node->data.prefix_op_expr.primary_expr;
                 TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type,
-                        operand_node);
+                        expr_node);
                 if (expr_type->id == TypeTableEntryIdInvalid) {
                     return expr_type;
                 } else if (expr_type->id == TypeTableEntryIdInt ||
@@ -2784,16 +2750,15 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                 {
                     return expr_type;
                 } else {
-                    add_node_error(g, operand_node, buf_sprintf("invalid binary not type: '%s'",
+                    add_node_error(g, expr_node, buf_sprintf("invalid binary not type: '%s'",
                             buf_ptr(&expr_type->name)));
                     return g->builtin_types.entry_invalid;
                 }
             }
         case PrefixOpNegation:
             {
-                AstNode *operand_node = node->data.prefix_op_expr.primary_expr;
                 TypeTableEntry *expr_type = analyze_expression(g, import, context, expected_type,
-                        operand_node);
+                        expr_node);
                 if (expr_type->id == TypeTableEntryIdInvalid) {
                     return expr_type;
                 } else if (expr_type->id == TypeTableEntryIdInt &&
@@ -2805,7 +2770,6 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
                 } else if (expr_type->id == TypeTableEntryIdNumberLiteral) {
                     return expr_type;
                 } else {
-                    BREAKPOINT;
                     add_node_error(g, node, buf_sprintf("invalid negation type: '%s'",
                             buf_ptr(&expr_type->name)));
                     return g->builtin_types.entry_invalid;
@@ -2814,20 +2778,23 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
         case PrefixOpAddressOf:
         case PrefixOpConstAddressOf:
             {
-                bool is_const = (node->data.prefix_op_expr.prefix_op == PrefixOpConstAddressOf);
+                bool is_const = (prefix_op == PrefixOpConstAddressOf);
 
                 TypeTableEntry *child_type = analyze_lvalue(g, import, context,
-                        node->data.prefix_op_expr.primary_expr, LValPurposeAddressOf, is_const);
+                        expr_node, LValPurposeAddressOf, is_const);
 
                 if (child_type->id == TypeTableEntryIdInvalid) {
                     return g->builtin_types.entry_invalid;
                 } else if (child_type->id == TypeTableEntryIdMetaType) {
-                    TypeTableEntry *meta_child_type = child_type->data.meta_type.child_type;
-                    if (meta_child_type->id == TypeTableEntryIdUnreachable) {
-                        add_node_error(g, node,
-                                buf_create_from_str("pointer to unreachable not allowed"));
+                    TypeTableEntry *meta_type = analyze_type_expr(g, import, context, expr_node);
+                    if (meta_type->id == TypeTableEntryIdInvalid) {
+                        return g->builtin_types.entry_invalid;
+                    } else if (meta_type->id == TypeTableEntryIdUnreachable) {
+                        add_node_error(g, node, buf_create_from_str("pointer to unreachable not allowed"));
+                        return g->builtin_types.entry_invalid;
                     } else {
-                        return get_meta_type(g, get_pointer_to_type(g, meta_child_type, is_const));
+                        return resolve_expr_const_val_as_type(g, node,
+                                get_pointer_to_type(g, meta_type, is_const));
                     }
                 } else {
                     return get_pointer_to_type(g, child_type, is_const);
@@ -2835,14 +2802,13 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
             }
         case PrefixOpDereference:
             {
-                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr,
-                        node->data.prefix_op_expr.primary_expr);
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
                 if (type_entry->id == TypeTableEntryIdInvalid) {
                     return type_entry;
                 } else if (type_entry->id == TypeTableEntryIdPointer) {
                     return type_entry->data.pointer.child_type;
                 } else {
-                    add_node_error(g, node->data.prefix_op_expr.primary_expr,
+                    add_node_error(g, expr_node,
                         buf_sprintf("indirection requires pointer operand ('%s' invalid)",
                             buf_ptr(&type_entry->name)));
                     return g->builtin_types.entry_invalid;
@@ -2850,24 +2816,25 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
             }
         case PrefixOpMaybe:
             {
-                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr,
-                        node->data.prefix_op_expr.primary_expr);
+                TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node);
 
                 if (type_entry->id == TypeTableEntryIdInvalid) {
                     return type_entry;
                 } else if (type_entry->id == TypeTableEntryIdMetaType) {
-                    TypeTableEntry *child_type = type_entry->data.meta_type.child_type;
-                    if (child_type->id == TypeTableEntryIdUnreachable) {
+                    TypeTableEntry *meta_type = resolve_type(g, expr_node);
+                    if (meta_type->id == TypeTableEntryIdInvalid) {
+                        return g->builtin_types.entry_invalid;
+                    } else if (meta_type->id == TypeTableEntryIdUnreachable) {
                         add_node_error(g, node, buf_create_from_str("maybe unreachable type not allowed"));
                         return g->builtin_types.entry_invalid;
                     } else {
-                        return get_meta_type(g, get_maybe_type(g, child_type));
+                        return resolve_expr_const_val_as_type(g, node, get_maybe_type(g, meta_type));
                     }
                 } else if (type_entry->id == TypeTableEntryIdUnreachable) {
-                    add_node_error(g, node->data.prefix_op_expr.primary_expr,
-                        buf_sprintf("unable to wrap unreachable in maybe type"));
+                    add_node_error(g, expr_node, buf_sprintf("unable to wrap unreachable in maybe type"));
                     return g->builtin_types.entry_invalid;
                 } else {
+                    // TODO eval const expr
                     return get_maybe_type(g, type_entry);
                 }
             }
@@ -3023,7 +2990,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
             return_type = g->builtin_types.entry_u8;
             break;
         case NodeTypeBoolLiteral:
-            return_type = g->builtin_types.entry_bool;
+            return_type = resolve_expr_const_val_as_bool(g, node, node->data.bool_literal.value);
             break;
 
         case NodeTypeNullLiteral:
@@ -3066,10 +3033,29 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
     assert(return_type);
     resolve_type_compatibility(g, context, node, expected_type, return_type);
 
-    get_resolved_expr(node)->type_entry = return_type;
-    get_resolved_expr(node)->block_context = context;
+    Expr *expr = get_resolved_expr(node);
+    expr->type_entry = return_type;
+    expr->block_context = context;
 
-    return return_type;
+    if (expr->type_entry->id == TypeTableEntryIdUnreachable) {
+        return expr->type_entry;
+    }
+
+    /*
+    Cast *cast_node = &expr->implicit_cast;
+    if (cast_node->after_type) {
+        eval_const_expr_implicit_cast(g, import, context, node, cast_node, node);
+        expr->type_entry = cast_node->after_type;
+    }
+    */
+
+    Cast *cast_node = &expr->implicit_maybe_cast;
+    if (cast_node->after_type) {
+        eval_const_expr_implicit_cast(g, import, context, node, cast_node, node);
+        expr->type_entry = cast_node->after_type;
+    }
+
+    return expr->type_entry;
 }
 
 static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node) {
src/codegen.cpp
@@ -73,11 +73,13 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType b
         TypeTableEntry *op1_type, TypeTableEntry *op2_type);
 static LLVMValueRef gen_bare_cast(CodeGen *g, AstNode *node, LLVMValueRef expr_val,
         TypeTableEntry *actual_type, TypeTableEntry *wanted_type, Cast *cast_node);
-    
+
 static TypeTableEntry *get_type_for_type_node(AstNode *node) {
-    TypeTableEntry *meta_type_entry = get_resolved_expr(node)->type_entry;
-    assert(meta_type_entry->id == TypeTableEntryIdMetaType);
-    return meta_type_entry->data.meta_type.child_type;
+    Expr *expr = get_resolved_expr(node);
+    assert(expr->type_entry->id == TypeTableEntryIdMetaType);
+    ConstExprValue *const_val = &expr->const_val;
+    assert(const_val->ok);
+    return const_val->data.x_type;
 }
 
 static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_node) {
@@ -434,10 +436,8 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
         } else if (struct_type->id == TypeTableEntryIdPointer) {
             assert(struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct);
             fn_table_entry = node->data.fn_call_expr.fn_entry;
-        } else if (struct_type->id == TypeTableEntryIdMetaType &&
-                   struct_type->data.meta_type.child_type->id == TypeTableEntryIdEnum)
-        {
-            TypeTableEntry *enum_type = struct_type->data.meta_type.child_type;
+        } else if (struct_type->id == TypeTableEntryIdMetaType) {
+            TypeTableEntry *enum_type = get_type_for_type_node(first_param_expr);
             int param_count = node->data.fn_call_expr.params.length;
             AstNode *arg1_node;
             if (param_count == 1) {
@@ -681,7 +681,8 @@ static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node, bool is_lva
 static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue) {
     assert(node->type == NodeTypeFieldAccessExpr);
 
-    TypeTableEntry *struct_type = get_expr_type(node->data.field_access_expr.struct_expr);
+    AstNode *struct_expr = node->data.field_access_expr.struct_expr;
+    TypeTableEntry *struct_type = get_expr_type(struct_expr);
     Buf *name = &node->data.field_access_expr.field_name;
 
     if (struct_type->id == TypeTableEntryIdArray) {
@@ -710,14 +711,12 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lva
             add_debug_source_node(g, node);
             return LLVMBuildLoad(g->builder, ptr, "");
         }
-    } else if (struct_type->id == TypeTableEntryIdMetaType &&
-               struct_type->data.meta_type.child_type->id == TypeTableEntryIdEnum)
-    {
+    } else if (struct_type->id == TypeTableEntryIdMetaType) {
         assert(!is_lvalue);
-        TypeTableEntry *enum_type = struct_type->data.meta_type.child_type;
+        TypeTableEntry *enum_type = get_type_for_type_node(struct_expr);
         return gen_enum_value_expr(g, node, enum_type, nullptr);
     } else {
-        zig_panic("gen_field_access_expr bad struct type");
+        zig_unreachable();
     }
 }
 
@@ -2392,6 +2391,12 @@ static void define_builtin_types(CodeGen *g) {
         g->builtin_types.entry_unreachable = entry;
         g->primitive_type_table.put(&entry->name, entry);
     }
+    {
+        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMetaType);
+        buf_init_from_str(&entry->name, "type");
+        g->builtin_types.entry_type = entry;
+        g->primitive_type_table.put(&entry->name, entry);
+    }
 
     g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, get_int_type(g, false, 8), true);