Commit 5f9ecb8566

Andrew Kelley <superjoe30@gmail.com>
2016-01-14 10:52:33
instead of 'as' to cast, call type as function
1 parent d121ed9
doc/vim/syntax/zig.vim
@@ -7,7 +7,6 @@ if exists("b:current_syntax")
   finish
 endif
 
-syn keyword zigOperator as
 syn keyword zigStorage const var extern volatile export pub noalias
 syn keyword zigStructure struct enum type
 syn keyword zigStatement goto break return continue asm
doc/langref.md
@@ -126,12 +126,10 @@ AdditionExpression : MultiplyExpression AdditionOperator AdditionExpression | Mu
 
 AdditionOperator : token(Plus) | token(Minus)
 
-MultiplyExpression : CastExpression MultiplyOperator MultiplyExpression | CastExpression
+MultiplyExpression : PrefixOpExpression MultiplyOperator MultiplyExpression | PrefixOpExpression
 
 MultiplyOperator : token(Star) | token(Slash) | token(Percent)
 
-CastExpression : CastExpression token(as) PrimaryExpression | PrefixOpExpression
-
 PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression
 
 SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ContainerInitExpression)
example/guess_number/main.zig
@@ -7,7 +7,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     print_str("Welcome to the Guess Number Game in Zig.\n");
 
     var seed : u32;
-    const err = os_get_random_bytes(&seed as (&u8), @sizeof(u32));
+    const err = os_get_random_bytes((&u8)(&seed), @sizeof(u32));
     if (err != @sizeof(u32)) {
         // TODO full error message
         fprint_str(stderr_fileno, "unable to get random bytes\n");
src/all_types.hpp
@@ -106,7 +106,6 @@ enum NodeType {
     NodeTypeReturnExpr,
     NodeTypeVariableDeclaration,
     NodeTypeBinOpExpr,
-    NodeTypeCastExpr,
     NodeTypeNumberLiteral,
     NodeTypeStringLiteral,
     NodeTypeCharLiteral,
@@ -272,6 +271,8 @@ struct AstNodeFnCallExpr {
     BuiltinFnEntry *builtin_fn;
     Expr resolved_expr;
     NumLitCodeGen resolved_num_lit;
+    Cast cast;
+    FnTableEntry *fn_entry;
 };
 
 struct AstNodeArrayAccessExpr {
@@ -320,15 +321,6 @@ struct AstNodeRootExportDecl {
     ZigList<AstNode *> *directives;
 };
 
-struct AstNodeCastExpr {
-    AstNode *expr;
-    AstNode *type;
-
-    // populated by semantic analyzer
-    Cast cast;
-    Expr resolved_expr;
-};
-
 enum PrefixOp {
     PrefixOpInvalid,
     PrefixOpBoolNot,
@@ -541,6 +533,9 @@ struct AstNodeSymbolExpr {
 
     // populated by semantic analyzer
     Expr resolved_expr;
+    VariableTableEntry *variable;
+    TypeTableEntry *meta_type;
+    FnTableEntry *fn_entry;
 };
 
 struct AstNodeBoolLiteral {
@@ -588,7 +583,6 @@ struct AstNode {
         AstNodeBinOpExpr bin_op_expr;
         AstNodeExternBlock extern_block;
         AstNodeDirective directive;
-        AstNodeCastExpr cast_expr;
         AstNodePrefixOpExpr prefix_op_expr;
         AstNodeFnCallExpr fn_call_expr;
         AstNodeArrayAccessExpr array_access_expr;
@@ -693,6 +687,12 @@ struct TypeTableEntryEnum {
     bool reported_infinite_err;
 };
 
+struct TypeTableEntryFn {
+    TypeTableEntry *return_type;
+    TypeTableEntry **param_types;
+    int param_count;
+};
+
 enum TypeTableEntryId {
     TypeTableEntryIdInvalid,
     TypeTableEntryIdMetaType,
@@ -707,6 +707,7 @@ enum TypeTableEntryId {
     TypeTableEntryIdNumberLiteral,
     TypeTableEntryIdMaybe,
     TypeTableEntryIdEnum,
+    TypeTableEntryIdFn,
 };
 
 struct TypeTableEntry {
@@ -728,6 +729,7 @@ struct TypeTableEntry {
         TypeTableEntryMaybe maybe;
         TypeTableEntryEnum enumeration;
         TypeTableEntryMetaType meta_type;
+        TypeTableEntryFn fn;
     } data;
 
     // use these fields to make sure we don't duplicate type table entries for the same type
@@ -781,6 +783,7 @@ struct FnTableEntry {
     ZigList<BlockContext *> all_block_contexts;
     TypeTableEntry *member_of_struct;
     Buf symbol_name;
+    TypeTableEntry *type_entry; // function type
 
     // reminder: hash tables must be initialized before use
     HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table;
@@ -913,4 +916,22 @@ 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,8 @@
 
 static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node);
-static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
-        AstNode *node, AstNodeNumberLiteral *out_number_literal);
+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);
@@ -32,8 +32,6 @@ static AstNode *first_executing_node(AstNode *node) {
             return first_executing_node(node->data.slice_expr.array_ref_expr);
         case NodeTypeFieldAccessExpr:
             return first_executing_node(node->data.field_access_expr.struct_expr);
-        case NodeTypeCastExpr:
-            return first_executing_node(node->data.cast_expr.expr);
         case NodeTypeRoot:
         case NodeTypeRootExportDecl:
         case NodeTypeFnProto:
@@ -125,6 +123,7 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) {
         case TypeTableEntryIdArray:
         case TypeTableEntryIdNumberLiteral:
         case TypeTableEntryIdMaybe:
+        case TypeTableEntryIdFn:
             // nothing to init
             break;
         case TypeTableEntryIdStruct:
@@ -357,63 +356,74 @@ static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, B
     }
 }
 
-
-static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
-        AstNode *node, AstNodeNumberLiteral *out_number_literal)
+static void eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
+        AstNode *node, ConstExprValue *out_val)
 {
-    AstNodeNumberLiteral op1_lit;
-    AstNodeNumberLiteral op2_lit;
-    TypeTableEntry *op1_type = eval_const_expr(g, context, node->data.bin_op_expr.op1, &op1_lit);
-    TypeTableEntry *op2_type = eval_const_expr(g, context, node->data.bin_op_expr.op1, &op2_lit);
-
-    if (op1_type->id == TypeTableEntryIdInvalid ||
-        op2_type->id == TypeTableEntryIdInvalid)
-    {
-        return g->builtin_types.entry_invalid;
+    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 (is_num_lit_unsigned(op1_lit.kind) &&
-                    is_num_lit_unsigned(op2_lit.kind))
+                if (op1_type->id == TypeTableEntryIdInt &&
+                    op2_type->id == TypeTableEntryIdInt)
                 {
-                    out_number_literal->kind = NumLitU8;
-                    out_number_literal->overflow = false;
-                    out_number_literal->data.x_uint = (op1_lit.data.x_uint != op2_lit.data.x_uint);
-                    return get_resolved_expr(node)->type_entry;
-                } else {
-                    return g->builtin_types.entry_invalid;
+                    out_val->data.x_bool = (op1_val.data.x_uint == op2_val.data.x_uint);
+                    out_val->ok = true;
                 }
+                break;
             }
         case BinOpTypeCmpLessThan:
             {
-                if (is_num_lit_unsigned(op1_lit.kind) &&
-                    is_num_lit_unsigned(op2_lit.kind))
+                if (op1_type->id == TypeTableEntryIdInt &&
+                    op2_type->id == TypeTableEntryIdInt)
                 {
-                    out_number_literal->kind = NumLitU8;
-                    out_number_literal->overflow = false;
-                    out_number_literal->data.x_uint = (op1_lit.data.x_uint < op2_lit.data.x_uint);
-                    return get_resolved_expr(node)->type_entry;
-                } else {
-                    return g->builtin_types.entry_invalid;
+                    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 (is_num_lit_unsigned(op1_lit.kind) &&
-                    is_num_lit_unsigned(op2_lit.kind))
+                if (op1_type->id == TypeTableEntryIdInt &&
+                    op2_type->id == TypeTableEntryIdInt)
                 {
-                    out_number_literal->kind = NumLitU64;
-                    out_number_literal->overflow = false;
-                    out_number_literal->data.x_uint = (op1_lit.data.x_uint % op2_lit.data.x_uint);
-                    return get_resolved_expr(node)->type_entry;
-                } else {
-                    return g->builtin_types.entry_invalid;
+                    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:
@@ -431,7 +441,7 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
         case BinOpTypeMult:
         case BinOpTypeDiv:
         case BinOpTypeUnwrapMaybe:
-            return g->builtin_types.entry_invalid;
+            break;
         case BinOpTypeInvalid:
         case BinOpTypeAssign:
         case BinOpTypeAssignTimes:
@@ -448,31 +458,23 @@ static TypeTableEntry *eval_const_expr_bin_op(CodeGen *g, BlockContext *context,
         case BinOpTypeAssignBoolOr:
             zig_unreachable();
     }
-    zig_unreachable();
 }
 
-static TypeTableEntry *eval_const_expr_fn_call(CodeGen *g, BlockContext *context,
-        AstNode *node, AstNodeNumberLiteral *out_number_literal)
-{
-    if (!node->data.fn_call_expr.is_builtin) {
-        return g->builtin_types.entry_invalid;
-    }
-
+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 BuiltinFnIdArithmeticWithOverflow:
         case BuiltinFnIdMemcpy:
         case BuiltinFnIdMemset:
-            return g->builtin_types.entry_invalid;
+            break;
         case BuiltinFnIdSizeof:
             {
                 AstNode *type_node = node->data.fn_call_expr.params.at(0);
                 TypeTableEntry *target_type = unwrapped_node_type(type_node);
-                out_number_literal->overflow = false;
-                out_number_literal->data.x_uint = target_type->size_in_bits / 8;
-                out_number_literal->kind = get_number_literal_kind_unsigned(out_number_literal->data.x_uint);
-                return get_resolved_expr(node)->type_entry;
+                out_val->data.x_uint = target_type->size_in_bits / 8;
+                out_val->ok = true;
+                break;
             }
         case BuiltinFnIdMaxValue:
         case BuiltinFnIdMinValue:
@@ -480,43 +482,154 @@ static TypeTableEntry *eval_const_expr_fn_call(CodeGen *g, BlockContext *context
         case BuiltinFnIdValueCount:
             zig_panic("TODO eval_const_expr_fn_call value_count");
         case BuiltinFnIdTypeof:
-            return get_resolved_expr(node)->type_entry;
+            // TODO
+            out_val->ok = true;
+            break;
     }
-    zig_unreachable();
 }
 
-static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
-        AstNode *node, AstNodeNumberLiteral *out_number_literal)
+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");
+    }
+}
+
+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);
+}
+
+static void eval_const_expr_prefix_op_expr(CodeGen *g, BlockContext *context, AstNode *node,
+        ConstExprValue *out_val)
 {
+    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:
-            *out_number_literal = node->data.number_literal;
-            return get_resolved_expr(node)->type_entry;
+            {
+                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_number_literal->data.x_uint = node->data.bool_literal.value ? 1 : 0;
-            return get_resolved_expr(node)->type_entry;
+            out_val->data.x_uint = node->data.bool_literal.value ? 1 : 0;
+            out_val->ok = true;
+            break;
         case NodeTypeNullLiteral:
-            return get_resolved_expr(node)->type_entry;
+            out_val->data.x_maybe.is_null = true;
+            out_val->ok = true;
+            break;
         case NodeTypeBinOpExpr:
-            return eval_const_expr_bin_op(g, context, node, out_number_literal);
+            eval_const_expr_bin_op(g, context, node, out_val);
+            break;
         case NodeTypeSymbol:
             {
-                VariableTableEntry *var = find_variable(context, &node->data.symbol_expr.symbol);
-                assert(var);
-                AstNode *decl_node = var->decl_node;
-                AstNode *expr_node = decl_node->data.variable_declaration.expr;
-                if (expr_node) {
-                    BlockContext *next_context = get_resolved_expr(expr_node)->block_context;
-                    return eval_const_expr(g, next_context, expr_node, out_number_literal);
+                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 {
-                    // can't eval it
-                    return g->builtin_types.entry_invalid;
+                    zig_unreachable();
                 }
+                break;
             }
         case NodeTypeFnCallExpr:
-            return eval_const_expr_fn_call(g, context, node, out_number_literal);
+            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:
-            return g->builtin_types.entry_invalid;
+            break;
     }
 }
 
@@ -550,11 +663,30 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
         }
     }
 
-    for (int i = 0; i < node->data.fn_proto.params.length; i += 1) {
+    int param_count = node->data.fn_proto.params.length;
+
+    TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
+    fn_type->data.fn.param_count = param_count;
+    fn_type->data.fn.param_types = allocate<TypeTableEntry*>(param_count);
+
+    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));
+    for (int i = 0; i < param_count; i += 1) {
         AstNode *child = node->data.fn_proto.params.at(i);
         assert(child->type == NodeTypeParamDecl);
         TypeTableEntry *type_entry = analyze_type_expr(g, import, import->block_context,
                 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));
+
+        if (i + 1 < param_count) {
+            buf_appendf(&fn_type->name, ", ");
+        }
 
         if (type_entry->id == TypeTableEntryIdUnreachable) {
             add_node_error(g, child->data.param_decl.type,
@@ -567,7 +699,14 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
         }
     }
 
-    analyze_type_expr(g, import, import->block_context, node->data.fn_proto.return_type);
+    TypeTableEntry *return_type = analyze_type_expr(g, import, import->block_context,
+            node->data.fn_proto.return_type);
+    fn_table_entry->type_entry->data.fn.return_type = return_type;
+
+    buf_appendf(&fn_type->name, ")");
+    if (return_type->id != TypeTableEntryIdVoid) {
+        buf_appendf(&fn_type->name, " %s", buf_ptr(&return_type->name));
+    }
 }
 
 static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry) {
@@ -1058,7 +1197,6 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeSymbol:
-        case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
         case NodeTypeIfBoolExpr:
         case NodeTypeIfVarExpr:
@@ -1116,6 +1254,7 @@ static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type,
         case TypeTableEntryIdStruct:
         case TypeTableEntryIdEnum:
         case TypeTableEntryIdMetaType:
+        case TypeTableEntryIdFn:
             return false;
         case TypeTableEntryIdInt:
             if (is_num_lit_unsigned(num_lit)) {
@@ -1703,17 +1842,28 @@ 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) {
-        return get_meta_type(g, primitive_table_entry->value);
+        TypeTableEntry *meta_type = get_meta_type(g, primitive_table_entry->value);
+        node->data.symbol_expr.meta_type = meta_type;
+        return meta_type;
     }
 
     VariableTableEntry *var = find_variable(context, variable_name);
     if (var) {
+        node->data.symbol_expr.variable = var;
         return var->type;
     }
 
     TypeTableEntry *container_type = find_container(context, variable_name);
     if (container_type) {
-        return get_meta_type(g, container_type);
+        TypeTableEntry *meta_type = get_meta_type(g, container_type);
+        node->data.symbol_expr.meta_type = meta_type;
+        return meta_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;
     }
 
     add_node_error(g, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
@@ -1781,65 +1931,6 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) {
     zig_unreachable();
 }
 
-static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        TypeTableEntry *expected_type, AstNode *node)
-{
-    assert(node->type == NodeTypeCastExpr);
-
-    TypeTableEntry *wanted_type = analyze_type_expr(g, import, context, node->data.cast_expr.type);
-    TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr, node->data.cast_expr.expr);
-
-    if (wanted_type->id == TypeTableEntryIdInvalid ||
-        actual_type->id == TypeTableEntryIdInvalid)
-    {
-        return g->builtin_types.entry_invalid;
-    }
-
-    Cast *cast = &node->data.cast_expr.cast;
-    cast->source_node = node;
-    cast->after_type = wanted_type;
-
-    if ((wanted_type == g->builtin_types.entry_isize || wanted_type == g->builtin_types.entry_usize) &&
-        actual_type->id == TypeTableEntryIdPointer)
-    {
-        cast->op = CastOpPtrToInt;
-        return wanted_type;
-    } else if (wanted_type->id == TypeTableEntryIdInt &&
-                actual_type->id == TypeTableEntryIdInt)
-    {
-        cast->op = CastOpIntWidenOrShorten;
-        return wanted_type;
-    } else if (wanted_type->id == TypeTableEntryIdStruct &&
-               wanted_type->data.structure.is_unknown_size_array &&
-               actual_type->id == TypeTableEntryIdArray &&
-               actual_type->data.array.child_type == wanted_type->data.structure.fields[0].type_entry)
-    {
-        cast->op = CastOpToUnknownSizeArray;
-        context->cast_expr_alloca_list.append(cast);
-        return wanted_type;
-    } else if (actual_type->id == TypeTableEntryIdNumberLiteral &&
-               num_lit_fits_in_other_type(g, actual_type, wanted_type))
-    {
-        AstNode *literal_node = node->data.cast_expr.expr;
-        NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(literal_node);
-        assert(!codegen_num_lit->resolved_type);
-        codegen_num_lit->resolved_type = wanted_type;
-        cast->op = CastOpNothing;
-        return wanted_type;
-    } else if (actual_type->id == TypeTableEntryIdPointer &&
-               wanted_type->id == TypeTableEntryIdPointer)
-    {
-        cast->op = CastOpPointerReinterpret;
-        return wanted_type;
-    } else {
-        add_node_error(g, node,
-            buf_sprintf("invalid cast from type '%s' to '%s'",
-                buf_ptr(&actual_type->name),
-                buf_ptr(&wanted_type->name)));
-        return g->builtin_types.entry_invalid;
-    }
-}
-
 enum LValPurpose {
     LValPurposeAssign,
     LValPurposeAddressOf,
@@ -2153,17 +2244,11 @@ static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import,
             return g->builtin_types.entry_invalid;
         }
 
-        AstNodeNumberLiteral number_literal;
-        TypeTableEntry *resolved_type = eval_const_expr(g, context, size_node, &number_literal);
+        ConstExprValue const_val = {0};
+        eval_const_expr(g, context, size_node, &const_val);
 
-        if (resolved_type->id == TypeTableEntryIdInt) {
-            if (resolved_type->data.integral.is_signed) {
-                add_node_error(g, size_node,
-                    buf_create_from_str("array size must be unsigned integer"));
-                return g->builtin_types.entry_invalid;
-            } else {
-                return get_meta_type(g, get_array_type(g, import, child_type, number_literal.data.x_uint));
-            }
+        if (const_val.ok) {
+            return get_meta_type(g, 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"));
@@ -2195,12 +2280,11 @@ 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
-        AstNodeNumberLiteral number_literal;
-        TypeTableEntry *resolved_type = eval_const_expr(g, context, condition_node, &number_literal);
-        if (resolved_type->id != TypeTableEntryIdInvalid) {
-            assert(resolved_type->id == TypeTableEntryIdBool);
-            bool constant_cond_value = number_literal.data.x_uint;
-            if (constant_cond_value) {
+        ConstExprValue const_val = {0};
+        eval_const_expr(g, context, 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;
@@ -2305,6 +2389,73 @@ static TypeTableEntry *analyze_min_max_value(CodeGen *g, ImportTableEntry *impor
     }
 }
 
+static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        AstNode *node, TypeTableEntry *invoke_type_entry)
+{
+    assert(node->type == NodeTypeFnCallExpr);
+
+    AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
+    int actual_param_count = node->data.fn_call_expr.params.length;
+
+    if (actual_param_count != 1) {
+        add_node_error(g, fn_ref_expr, buf_sprintf("cast expression expects exactly one parameter"));
+        return g->builtin_types.entry_invalid;
+    }
+
+    AstNode *expr_node = node->data.fn_call_expr.params.at(0);
+    TypeTableEntry *wanted_type = invoke_type_entry->data.meta_type.child_type;
+    TypeTableEntry *actual_type = analyze_expression(g, import, context, nullptr, expr_node);
+
+    if (wanted_type->id == TypeTableEntryIdInvalid ||
+        actual_type->id == TypeTableEntryIdInvalid)
+    {
+        return g->builtin_types.entry_invalid;
+    }
+
+    Cast *cast = &node->data.fn_call_expr.cast;
+    cast->source_node = node;
+    cast->after_type = wanted_type;
+
+    if ((wanted_type == g->builtin_types.entry_isize || wanted_type == g->builtin_types.entry_usize) &&
+        actual_type->id == TypeTableEntryIdPointer)
+    {
+        cast->op = CastOpPtrToInt;
+        return wanted_type;
+    } else if (wanted_type->id == TypeTableEntryIdInt &&
+                actual_type->id == TypeTableEntryIdInt)
+    {
+        cast->op = CastOpIntWidenOrShorten;
+        return wanted_type;
+    } else if (wanted_type->id == TypeTableEntryIdStruct &&
+               wanted_type->data.structure.is_unknown_size_array &&
+               actual_type->id == TypeTableEntryIdArray &&
+               actual_type->data.array.child_type == wanted_type->data.structure.fields[0].type_entry)
+    {
+        cast->op = CastOpToUnknownSizeArray;
+        context->cast_expr_alloca_list.append(cast);
+        return wanted_type;
+    } else if (actual_type->id == TypeTableEntryIdNumberLiteral &&
+               num_lit_fits_in_other_type(g, actual_type, wanted_type))
+    {
+        NumLitCodeGen *codegen_num_lit = get_resolved_num_lit(expr_node);
+        assert(!codegen_num_lit->resolved_type);
+        codegen_num_lit->resolved_type = wanted_type;
+        cast->op = CastOpNothing;
+        return wanted_type;
+    } else if (actual_type->id == TypeTableEntryIdPointer &&
+               wanted_type->id == TypeTableEntryIdPointer)
+    {
+        cast->op = CastOpPointerReinterpret;
+        return wanted_type;
+    } else {
+        add_node_error(g, node,
+            buf_sprintf("invalid cast from type '%s' to '%s'",
+                buf_ptr(&actual_type->name),
+                buf_ptr(&wanted_type->name)));
+        return g->builtin_types.entry_invalid;
+    }
+}
+
 static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
@@ -2458,25 +2609,93 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
     }
 }
 
+static TypeTableEntry *analyze_fn_call_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node, FnTableEntry *fn_table_entry, TypeTableEntry *struct_type)
+{
+    assert(node->type == NodeTypeFnCallExpr);
+
+    node->data.fn_call_expr.fn_entry = fn_table_entry;
+    assert(fn_table_entry->proto_node->type == NodeTypeFnProto);
+    AstNodeFnProto *fn_proto = &fn_table_entry->proto_node->data.fn_proto;
+
+    // count parameters
+    int expected_param_count = fn_proto->params.length;
+    int actual_param_count = node->data.fn_call_expr.params.length;
+
+    if (struct_type) {
+        actual_param_count += 1;
+    }
+
+    if (fn_proto->is_var_args) {
+        if (actual_param_count < expected_param_count) {
+            add_node_error(g, node,
+                    buf_sprintf("expected at least %d arguments, got %d",
+                        expected_param_count, actual_param_count));
+        }
+    } else if (expected_param_count != actual_param_count) {
+        add_node_error(g, node,
+                buf_sprintf("expected %d arguments, got %d",
+                    expected_param_count, actual_param_count));
+    }
+
+    // analyze each parameter. in the case of a method, we already analyzed the
+    // first parameter in order to figure out which struct we were calling a method on.
+    for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
+        AstNode *child = node->data.fn_call_expr.params.at(i);
+        // determine the expected type for each parameter
+        TypeTableEntry *expected_param_type = nullptr;
+        int fn_proto_i = i + (struct_type ? 1 : 0);
+        if (fn_proto_i < fn_proto->params.length) {
+            AstNode *param_decl_node = fn_proto->params.at(fn_proto_i);
+            assert(param_decl_node->type == NodeTypeParamDecl);
+            AstNode *param_type_node = param_decl_node->data.param_decl.type;
+            TypeTableEntry *param_type_entry = get_resolved_expr(param_type_node)->type_entry;
+            if (param_type_entry) {
+                expected_param_type = unwrapped_node_type(param_type_node);
+            }
+        }
+        analyze_expression(g, import, context, expected_param_type, child);
+    }
+
+    return unwrapped_node_type(fn_proto->return_type);
+}
+
 static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
     AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-    TypeTableEntry *struct_type = nullptr;
-    HashMap<Buf *, FnTableEntry *, buf_hash, buf_eql_buf> *fn_table = &import->fn_table;
-    AstNode *first_param_expr = nullptr;
-    Buf *name;
+
+    if (node->data.fn_call_expr.is_builtin) {
+        return analyze_builtin_fn_call_expr(g, import, context, expected_type, node);
+    }
 
     if (fn_ref_expr->type == NodeTypeFieldAccessExpr) {
-        first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;
-        struct_type = analyze_expression(g, import, context, nullptr, first_param_expr);
-        name = &fn_ref_expr->data.field_access_expr.field_name;
-        if (struct_type->id == TypeTableEntryIdStruct) {
-            fn_table = &struct_type->data.structure.fn_table;
-        } else if (struct_type->id == TypeTableEntryIdPointer &&
-                   struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct)
+        AstNode *first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;
+        TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, first_param_expr);
+        Buf *name = &fn_ref_expr->data.field_access_expr.field_name;
+        if (struct_type->id == TypeTableEntryIdStruct ||
+            (struct_type->id == TypeTableEntryIdPointer &&
+            struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct))
         {
-            fn_table = &struct_type->data.pointer.child_type->data.structure.fn_table;
+            TypeTableEntry *bare_struct_type = (struct_type->id == TypeTableEntryIdStruct) ?
+                struct_type : struct_type->data.pointer.child_type;
+
+            auto table_entry = bare_struct_type->data.structure.fn_table.maybe_get(name);
+            if (table_entry) {
+                return analyze_fn_call_raw(g, import, context, expected_type, node,
+                        table_entry->value, bare_struct_type);
+            } else {
+                add_node_error(g, fn_ref_expr,
+                        buf_sprintf("no function named '%s' in '%s'",
+                            buf_ptr(name), buf_ptr(&bare_struct_type->name)));
+                // still analyze the parameters, even though we don't know what to expect
+                for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
+                    AstNode *child = node->data.fn_call_expr.params.at(i);
+                    analyze_expression(g, import, context, nullptr, child);
+                }
+
+                return g->builtin_types.entry_invalid;
+            }
         } else if (struct_type->id == TypeTableEntryIdInvalid) {
             return struct_type;
         } else if (struct_type->id == TypeTableEntryIdMetaType &&
@@ -2505,74 +2724,31 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
                     buf_sprintf("member reference base type not struct or enum"));
             return g->builtin_types.entry_invalid;
         }
-    } else if (fn_ref_expr->type == NodeTypeSymbol) {
-        if (node->data.fn_call_expr.is_builtin) {
-            return analyze_builtin_fn_call_expr(g, import, context, expected_type, node);
-        }
-        name = &fn_ref_expr->data.symbol_expr.symbol;
-    } else {
-        add_node_error(g, node,
-                buf_sprintf("function pointers not yet supported"));
-        return g->builtin_types.entry_invalid;
     }
 
-    auto entry = fn_table->maybe_get(name);
-
-    if (!entry) {
-        add_node_error(g, fn_ref_expr,
-                buf_sprintf("undefined function: '%s'", buf_ptr(name)));
-        // still analyze the parameters, even though we don't know what to expect
-        for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
-            AstNode *child = node->data.fn_call_expr.params.at(i);
-            analyze_expression(g, import, context, nullptr, child);
-        }
-
+    TypeTableEntry *invoke_type_entry = analyze_expression(g, import, context, nullptr, fn_ref_expr);
+    if (invoke_type_entry->id == TypeTableEntryIdInvalid) {
         return g->builtin_types.entry_invalid;
-    } else {
-        FnTableEntry *fn_table_entry = entry->value;
-        assert(fn_table_entry->proto_node->type == NodeTypeFnProto);
-        AstNodeFnProto *fn_proto = &fn_table_entry->proto_node->data.fn_proto;
-
-        // count parameters
-        int expected_param_count = fn_proto->params.length;
-        int actual_param_count = node->data.fn_call_expr.params.length;
-
-        if (struct_type) {
-            actual_param_count += 1;
-        }
+    }
 
-        if (fn_proto->is_var_args) {
-            if (actual_param_count < expected_param_count) {
-                add_node_error(g, node,
-                        buf_sprintf("expected at least %d arguments, got %d",
-                            expected_param_count, actual_param_count));
-            }
-        } else if (expected_param_count != actual_param_count) {
-            add_node_error(g, node,
-                    buf_sprintf("expected %d arguments, got %d",
-                        expected_param_count, actual_param_count));
-        }
+    // 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);
 
-        // analyze each parameter. in the case of a method, we already analyzed the
-        // first parameter in order to figure out which struct we were calling a method on.
-        for (int i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
-            AstNode *child = node->data.fn_call_expr.params.at(i);
-            // determine the expected type for each parameter
-            TypeTableEntry *expected_param_type = nullptr;
-            int fn_proto_i = i + (struct_type ? 1 : 0);
-            if (fn_proto_i < fn_proto->params.length) {
-                AstNode *param_decl_node = fn_proto->params.at(fn_proto_i);
-                assert(param_decl_node->type == NodeTypeParamDecl);
-                AstNode *param_type_node = param_decl_node->data.param_decl.type;
-                TypeTableEntry *param_type_entry = get_resolved_expr(param_type_node)->type_entry;
-                if (param_type_entry) {
-                    expected_param_type = unwrapped_node_type(param_type_node);
-                }
-            }
-            analyze_expression(g, import, context, expected_param_type, child);
-        }
+    if (!const_val.ok) {
+        add_node_error(g, node, buf_sprintf("function pointers not yet supported"));
+        return g->builtin_types.entry_invalid;
+    }
 
-        return unwrapped_node_type(fn_proto->return_type);
+    if (invoke_type_entry->id == TypeTableEntryIdMetaType) {
+        return analyze_cast_expr(g, import, context, node, invoke_type_entry);
+    } else if (invoke_type_entry->id == TypeTableEntryIdFn) {
+        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)));
+        return g->builtin_types.entry_invalid;
     }
 }
 
@@ -2847,9 +3023,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
         case NodeTypeSymbol:
             return_type = analyze_symbol_expr(g, import, context, expected_type, node);
             break;
-        case NodeTypeCastExpr:
-            return_type = analyze_cast_expr(g, import, context, expected_type, node);
-            break;
         case NodeTypePrefixOpExpr:
             return_type = analyze_prefix_op_expr(g, import, context, expected_type, node);
             break;
@@ -3008,7 +3181,6 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeSymbol:
-        case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
         case NodeTypeIfBoolExpr:
         case NodeTypeIfVarExpr:
@@ -3060,10 +3232,6 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode
         case NodeTypeReturnExpr:
             collect_expr_decl_deps(g, import, node->data.return_expr.expr, decl_node);
             break;
-        case NodeTypeCastExpr:
-            collect_expr_decl_deps(g, import, node->data.cast_expr.expr, decl_node);
-            collect_expr_decl_deps(g, import, node->data.cast_expr.type, decl_node);
-            break;
         case NodeTypePrefixOpExpr:
             collect_expr_decl_deps(g, import, node->data.prefix_op_expr.primary_expr, decl_node);
             break;
@@ -3331,7 +3499,6 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
         case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeSymbol:
-        case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
         case NodeTypeIfBoolExpr:
         case NodeTypeIfVarExpr:
@@ -3504,8 +3671,6 @@ Expr *get_resolved_expr(AstNode *node) {
             return &node->data.return_expr.resolved_expr;
         case NodeTypeBinOpExpr:
             return &node->data.bin_op_expr.resolved_expr;
-        case NodeTypeCastExpr:
-            return &node->data.cast_expr.resolved_expr;
         case NodeTypePrefixOpExpr:
             return &node->data.prefix_op_expr.resolved_expr;
         case NodeTypeFnCallExpr:
@@ -3577,7 +3742,6 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) {
             return &node->data.fn_call_expr.resolved_num_lit;
         case NodeTypeReturnExpr:
         case NodeTypeBinOpExpr:
-        case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
         case NodeTypeArrayAccessExpr:
         case NodeTypeSliceExpr:
@@ -3627,7 +3791,6 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) {
         case NodeTypeNumberLiteral:
         case NodeTypeReturnExpr:
         case NodeTypeBinOpExpr:
-        case NodeTypeCastExpr:
         case NodeTypePrefixOpExpr:
         case NodeTypeFnCallExpr:
         case NodeTypeArrayAccessExpr:
src/codegen.cpp
@@ -71,6 +71,8 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa
 static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op,
         LLVMValueRef target_ref, LLVMValueRef value,
         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;
@@ -381,23 +383,43 @@ static LLVMValueRef gen_enum_value_expr(CodeGen *g, AstNode *node, TypeTableEntr
     }
 }
 
+static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
+    assert(node->type == NodeTypeFnCallExpr);
+
+    AstNode *expr_node = node->data.fn_call_expr.params.at(0);
+
+    LLVMValueRef expr_val = gen_expr(g, expr_node);
+
+    TypeTableEntry *actual_type = get_expr_type(expr_node);
+    TypeTableEntry *wanted_type = get_expr_type(node);
+
+    Cast *cast_node = &node->data.fn_call_expr.cast;
+
+    return gen_bare_cast(g, node, expr_val, actual_type, wanted_type, cast_node);
+
+}
 
 static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeFnCallExpr);
 
-    FnTableEntry *fn_table_entry;
+    if (node->data.fn_call_expr.is_builtin) {
+        return gen_builtin_fn_call_expr(g, node);
+    } else if (node->data.fn_call_expr.cast.after_type) {
+        return gen_cast_expr(g, node);
+    }
+
+    FnTableEntry *fn_table_entry = node->data.fn_call_expr.fn_entry;
     AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
-    TypeTableEntry *struct_type;
-    AstNode *first_param_expr;
+    TypeTableEntry *struct_type = nullptr;
+    AstNode *first_param_expr = nullptr;
     if (fn_ref_expr->type == NodeTypeFieldAccessExpr) {
-        Buf *name = &fn_ref_expr->data.field_access_expr.field_name;
         first_param_expr = fn_ref_expr->data.field_access_expr.struct_expr;
         struct_type = get_expr_type(first_param_expr);
         if (struct_type->id == TypeTableEntryIdStruct) {
-            fn_table_entry = struct_type->data.structure.fn_table.get(name);
+            fn_table_entry = node->data.fn_call_expr.fn_entry;
         } else if (struct_type->id == TypeTableEntryIdPointer) {
             assert(struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct);
-            fn_table_entry = struct_type->data.pointer.child_type->data.structure.fn_table.get(name);
+            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)
         {
@@ -414,24 +436,8 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
         } else {
             zig_unreachable();
         }
-    } else if (fn_ref_expr->type == NodeTypeSymbol) {
-        if (node->data.fn_call_expr.is_builtin) {
-            return gen_builtin_fn_call_expr(g, node);
-        }
-
-        // Assume that the expression evaluates to a simple name and return the buf
-        // TODO after we support function pointers we can make this generic
-        assert(fn_ref_expr->type == NodeTypeSymbol);
-        Buf *name = &fn_ref_expr->data.symbol_expr.symbol;
-
-        struct_type = nullptr;
-        first_param_expr = nullptr;
-        fn_table_entry = g->cur_fn->import_entry->fn_table.get(name);
-    } else {
-        zig_unreachable();
     }
 
-
     assert(fn_table_entry->proto_node->type == NodeTypeFnProto);
     AstNodeFnProto *fn_proto_data = &fn_table_entry->proto_node->data.fn_proto;
 
@@ -863,20 +869,6 @@ static LLVMValueRef gen_bare_cast(CodeGen *g, AstNode *node, LLVMValueRef expr_v
     zig_unreachable();
 }
 
-static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
-    assert(node->type == NodeTypeCastExpr);
-
-    LLVMValueRef expr_val = gen_expr(g, node->data.cast_expr.expr);
-
-    TypeTableEntry *actual_type = get_expr_type(node->data.cast_expr.expr);
-    TypeTableEntry *wanted_type = get_expr_type(node);
-
-    Cast *cast_node = &node->data.cast_expr.cast;
-
-    return gen_bare_cast(g, node, expr_val, actual_type, wanted_type, cast_node);
-
-}
-
 static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
     LLVMValueRef val1, LLVMValueRef val2,
     TypeTableEntry *op1_type, TypeTableEntry *op2_type,
@@ -1810,8 +1802,6 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
             return gen_return_expr(g, node);
         case NodeTypeVariableDeclaration:
             return gen_var_decl_expr(g, node);
-        case NodeTypeCastExpr:
-            return gen_cast_expr(g, node);
         case NodeTypePrefixOpExpr:
             return gen_prefix_op_expr(g, node);
         case NodeTypeFnCallExpr:
src/parser.cpp
@@ -99,8 +99,6 @@ const char *node_type_str(NodeType node_type) {
             return "ReturnExpr";
         case NodeTypeVariableDeclaration:
             return "VariableDeclaration";
-        case NodeTypeCastExpr:
-            return "CastExpr";
         case NodeTypeNumberLiteral:
             return "NumberLiteral";
         case NodeTypeStringLiteral:
@@ -265,12 +263,6 @@ void ast_print(AstNode *node, int indent) {
         case NodeTypeDirective:
             fprintf(stderr, "%s\n", node_type_str(node->type));
             break;
-        case NodeTypeCastExpr:
-            fprintf(stderr, "%s\n", node_type_str(node->type));
-            ast_print(node->data.cast_expr.expr, indent + 2);
-            if (node->data.cast_expr.type)
-                ast_print(node->data.cast_expr.type, indent + 2);
-            break;
         case NodeTypePrefixOpExpr:
             fprintf(stderr, "%s %s\n", node_type_str(node->type),
                     prefix_op_str(node->data.prefix_op_expr.prefix_op));
@@ -1604,29 +1596,6 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo
 }
 
 
-/*
-CastExpression : CastExpression token(as) PrimaryExpression | PrefixOpExpression
-*/
-static AstNode *ast_parse_cast_expression(ParseContext *pc, int *token_index, bool mandatory) {
-    AstNode *operand_1 = ast_parse_prefix_op_expr(pc, token_index, mandatory);
-    if (!operand_1)
-        return nullptr;
-
-    while (true) {
-        Token *as_kw = &pc->tokens->at(*token_index);
-        if (as_kw->id != TokenIdKeywordAs)
-            return operand_1;
-        *token_index += 1;
-
-        AstNode *node = ast_create_node(pc, NodeTypeCastExpr, as_kw);
-        node->data.cast_expr.expr = operand_1;
-
-        node->data.cast_expr.type = ast_parse_primary_expr(pc, token_index, true);
-
-        operand_1 = node;
-    }
-}
-
 static BinOpType tok_to_mult_op(Token *token) {
     switch (token->id) {
         case TokenIdStar: return BinOpTypeMult;
@@ -1654,10 +1623,10 @@ static BinOpType ast_parse_mult_op(ParseContext *pc, int *token_index, bool mand
 }
 
 /*
-MultiplyExpression : CastExpression MultiplyOperator MultiplyExpression | CastExpression
+MultiplyExpression : PrefixOpExpression MultiplyOperator MultiplyExpression | PrefixOpExpression
 */
 static AstNode *ast_parse_mult_expr(ParseContext *pc, int *token_index, bool mandatory) {
-    AstNode *operand_1 = ast_parse_cast_expression(pc, token_index, mandatory);
+    AstNode *operand_1 = ast_parse_prefix_op_expr(pc, token_index, mandatory);
     if (!operand_1)
         return nullptr;
 
@@ -1667,7 +1636,7 @@ static AstNode *ast_parse_mult_expr(ParseContext *pc, int *token_index, bool man
         if (mult_op == BinOpTypeInvalid)
             return operand_1;
 
-        AstNode *operand_2 = ast_parse_cast_expression(pc, token_index, true);
+        AstNode *operand_2 = ast_parse_prefix_op_expr(pc, token_index, true);
 
         AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
         node->data.bin_op_expr.op1 = operand_1;
src/tokenizer.cpp
@@ -211,8 +211,6 @@ static void end_token(Tokenize *t) {
         t->cur_tok->id = TokenIdKeywordPub;
     } else if (mem_eql_str(token_mem, token_len, "export")) {
         t->cur_tok->id = TokenIdKeywordExport;
-    } else if (mem_eql_str(token_mem, token_len, "as")) {
-        t->cur_tok->id = TokenIdKeywordAs;
     } else if (mem_eql_str(token_mem, token_len, "use")) {
         t->cur_tok->id = TokenIdKeywordUse;
     } else if (mem_eql_str(token_mem, token_len, "true")) {
@@ -1019,7 +1017,6 @@ const char * token_name(TokenId id) {
         case TokenIdKeywordExtern: return "extern";
         case TokenIdKeywordPub: return "pub";
         case TokenIdKeywordExport: return "export";
-        case TokenIdKeywordAs: return "as";
         case TokenIdKeywordUse: return "use";
         case TokenIdKeywordTrue: return "true";
         case TokenIdKeywordFalse: return "false";
src/tokenizer.hpp
@@ -20,7 +20,6 @@ enum TokenId {
     TokenIdKeywordExtern,
     TokenIdKeywordPub,
     TokenIdKeywordExport,
-    TokenIdKeywordAs,
     TokenIdKeywordUse,
     TokenIdKeywordTrue,
     TokenIdKeywordFalse,
std/rand.zig
@@ -13,7 +13,7 @@ pub struct Rand {
         var i : @typeof(ARRAY_SIZE) = 1;
         while (i < ARRAY_SIZE) {
             const prev_value : u64 = r.array[i - 1];
-            r.array[i] = ((prev_value ^ (prev_value << 30)) * 0x6c078965 + i) as u32;
+            r.array[i] = u32((prev_value ^ (prev_value << 30)) * 0x6c078965 + i);
             i += 1;
         }
     }
@@ -41,7 +41,7 @@ pub struct Rand {
         var bytes_left = r.get_bytes_aligned(buf);
         if (bytes_left > 0) {
             var rand_val_array : [@sizeof(u32)]u8;
-            *(rand_val_array.ptr as (&u32)) = r.get_u32();
+            *((&u32)(rand_val_array.ptr)) = r.get_u32();
             while (bytes_left > 0) {
                 buf[buf.len - bytes_left] = rand_val_array[@sizeof(u32) - bytes_left];
                 bytes_left -= 1;
@@ -59,7 +59,7 @@ pub struct Rand {
 
         while (true) {
             r.get_bytes_aligned(rand_val_array);
-            const rand_val = *(rand_val_array.ptr as (&u64));
+            const rand_val = *((&u64)(rand_val_array.ptr));
             if (rand_val < upper_bound) {
                 return start + (rand_val % range);
             }
@@ -85,7 +85,7 @@ pub struct Rand {
     fn get_bytes_aligned(r: &Rand, buf: []u8) usize => {
         var bytes_left = buf.len;
         while (bytes_left >= 4) {
-            *(&buf[buf.len - bytes_left] as (&u32)) = r.get_u32();
+            *((&u32)(&buf[buf.len - bytes_left])) = r.get_u32();
             bytes_left -= @sizeof(u32);
         }
         return bytes_left;
std/std.zig
@@ -43,7 +43,7 @@ pub fn readline(buf: []u8, out_len: &usize) bool => {
     if (amt_read < 0) {
         return true;
     }
-    *out_len = amt_read as usize;
+    *out_len = usize(amt_read);
     return false;
 }
 
@@ -94,9 +94,9 @@ const max_u64_base10_digits: usize = 20;
 fn buf_print_i64(out_buf: []u8, x: i64) usize => {
     if (x < 0) {
         out_buf[0] = '-';
-        return 1 + buf_print_u64(out_buf[1...], ((-(x + 1)) as u64) + 1);
+        return 1 + buf_print_u64(out_buf[1...], u64(-(x + 1)) + 1);
     } else {
-        return buf_print_u64(out_buf, x as u64);
+        return buf_print_u64(out_buf, u64(x));
     }
 }
 
@@ -108,7 +108,7 @@ fn buf_print_u64(out_buf: []u8, x: u64) usize => {
     while (true) {
         const digit = a % 10;
         index -= 1;
-        buf[index] = '0' + (digit as u8);
+        buf[index] = '0' + u8(digit);
         a /= 10;
         if (a == 0)
             break;
std/syscall.zig
@@ -18,19 +18,18 @@ fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize => {
 }
 
 pub fn read(fd: isize, buf: &u8, count: usize) isize => {
-    syscall3(SYS_read, fd as usize, buf as usize, count) as isize
+    isize(syscall3(SYS_read, usize(fd), usize(buf), count))
 }
 
 pub fn write(fd: isize, buf: &const u8, count: usize) isize => {
-    syscall3(SYS_write, fd as usize, buf as usize, count) as isize
+    isize(syscall3(SYS_write, usize(fd), usize(buf), count))
 }
 
 pub fn exit(status: i32) unreachable => {
-    syscall1(SYS_exit, status as usize);
+    syscall1(SYS_exit, usize(status));
     unreachable{}
 }
 
 pub fn getrandom(buf: &u8, count: usize, flags: u32) isize => {
-    syscall3(SYS_getrandom, buf as usize, count, flags as usize) as isize
+    isize(syscall3(SYS_getrandom, usize(buf), count, usize(flags)))
 }
-
test/run_tests.cpp
@@ -272,7 +272,7 @@ use "std.zig";
 
 pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     const a : i32 = 1;
-    const b = 2 as i32;
+    const b = i32(2);
     if (a + b == 3) {
         print_str("OK\n");
     }
@@ -302,7 +302,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     }
 
     const c = {
-        const no_conflict = 10 as i32;
+        const no_conflict = i32(10);
         no_conflict
     };
     if (c == 10) { print_str("OK 2\n"); }
@@ -358,7 +358,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     var zero : i32 = 0;
     if (zero == 0) { print_str("zero\n"); }
 
-    var i = 0 as i32;
+    var i = i32(0);
 loop_start:
     if (i == 3) {
         goto done;
@@ -384,7 +384,7 @@ pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
     }
 
     i = 0;
-    var accumulator = 0 as u32;
+    var accumulator = u32(0);
     while (i < 5) {
         accumulator += array[i];
 
@@ -430,9 +430,9 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     if (90 >> 1 >> 2 != 90 >> 3) { print_str("BAD 7\n"); }
     if (100 - 1 + 1000 != 1099)  { print_str("BAD 8\n"); }
     if (5 * 4 / 2 % 3 != 1)      { print_str("BAD 9\n"); }
-    if (5 as i32 as i32 != 5)    { print_str("BAD 10\n"); }
+    if (i32(i32(5)) != 5)        { print_str("BAD 10\n"); }
     if (!!false)                 { print_str("BAD 11\n"); }
-    if (7 as i32 != --(7 as i32))                { print_str("BAD 12\n"); }
+    if (i32(7) != --(i32(7)))    { print_str("BAD 12\n"); }
 
     print_str("OK\n");
     return 0;
@@ -495,82 +495,82 @@ export fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
     printf(c"\n");
 
     printf(c"0: %llu\n",
-             0 as u64);
+             u64(0));
     printf(c"320402575052271: %llu\n",
-             320402575052271 as u64);
+         u64(320402575052271));
     printf(c"0x01236789abcdef: %llu\n",
-             0x01236789abcdef as u64);
+         u64(0x01236789abcdef));
     printf(c"0xffffffffffffffff: %llu\n",
-             0xffffffffffffffff as u64);
+         u64(0xffffffffffffffff));
     printf(c"0x000000ffffffffffffffff: %llu\n",
-             0x000000ffffffffffffffff as u64);
+         u64(0x000000ffffffffffffffff));
     printf(c"0o1777777777777777777777: %llu\n",
-             0o1777777777777777777777 as u64);
+         u64(0o1777777777777777777777));
     printf(c"0o0000001777777777777777777777: %llu\n",
-             0o0000001777777777777777777777 as u64);
+         u64(0o0000001777777777777777777777));
     printf(c"0b1111111111111111111111111111111111111111111111111111111111111111: %llu\n",
-             0b1111111111111111111111111111111111111111111111111111111111111111 as u64);
+         u64(0b1111111111111111111111111111111111111111111111111111111111111111));
     printf(c"0b0000001111111111111111111111111111111111111111111111111111111111111111: %llu\n",
-             0b0000001111111111111111111111111111111111111111111111111111111111111111 as u64);
+         u64(0b0000001111111111111111111111111111111111111111111111111111111111111111));
 
     printf(c"\n");
 
     printf(c"0.0: %a\n",
-             0.0 as f64);
+         f64(0.0));
     printf(c"0e0: %a\n",
-             0e0 as f64);
+         f64(0e0));
     printf(c"0.0e0: %a\n",
-             0.0e0 as f64);
+         f64(0.0e0));
     printf(c"000000000000000000000000000000000000000000000000000000000.0e0: %a\n",
-             000000000000000000000000000000000000000000000000000000000.0e0 as f64);
+         f64(000000000000000000000000000000000000000000000000000000000.0e0));
     printf(c"0.000000000000000000000000000000000000000000000000000000000e0: %a\n",
-             0.000000000000000000000000000000000000000000000000000000000e0 as f64);
+         f64(0.000000000000000000000000000000000000000000000000000000000e0));
     printf(c"0.0e000000000000000000000000000000000000000000000000000000000: %a\n",
-             0.0e000000000000000000000000000000000000000000000000000000000 as f64);
+         f64(0.0e000000000000000000000000000000000000000000000000000000000));
     printf(c"1.0: %a\n",
-             1.0 as f64);
+         f64(1.0));
     printf(c"10.0: %a\n",
-             10.0 as f64);
+         f64(10.0));
     printf(c"10.5: %a\n",
-             10.5 as f64);
+         f64(10.5));
     printf(c"10.5e5: %a\n",
-             10.5e5 as f64);
+         f64(10.5e5));
     printf(c"10.5e+5: %a\n",
-             10.5e+5 as f64);
+         f64(10.5e+5));
     printf(c"50.0e-2: %a\n",
-             50.0e-2 as f64);
+         f64(50.0e-2));
     printf(c"50e-2: %a\n",
-             50e-2 as f64);
+         f64(50e-2));
 
     printf(c"\n");
 
     printf(c"0x1.0: %a\n",
-             0x1.0 as f64);
+         f64(0x1.0));
     printf(c"0x10.0: %a\n",
-             0x10.0 as f64);
+         f64(0x10.0));
     printf(c"0x100.0: %a\n",
-             0x100.0 as f64);
+         f64(0x100.0));
     printf(c"0x103.0: %a\n",
-             0x103.0 as f64);
+         f64(0x103.0));
     printf(c"0x103.7: %a\n",
-             0x103.7 as f64);
+         f64(0x103.7));
     printf(c"0x103.70: %a\n",
-             0x103.70 as f64);
+         f64(0x103.70));
     printf(c"0x103.70p4: %a\n",
-             0x103.70p4 as f64);
+         f64(0x103.70p4));
     printf(c"0x103.70p5: %a\n",
-             0x103.70p5 as f64);
+         f64(0x103.70p5));
     printf(c"0x103.70p+5: %a\n",
-             0x103.70p+5 as f64);
+         f64(0x103.70p+5));
     printf(c"0x103.70p-5: %a\n",
-             0x103.70p-5 as f64);
+         f64(0x103.70p-5));
 
     printf(c"\n");
 
     printf(c"0b10100.00010e0: %a\n",
-             0b10100.00010e0 as f64);
+         f64(0b10100.00010e0));
     printf(c"0o10700.00010e0: %a\n",
-             0o10700.00010e0 as f64);
+         f64(0o10700.00010e0));
 
     return 0;
 }
@@ -818,7 +818,7 @@ pub fn main(argc : isize, argv : &&u8, env : &&u8) i32 => {
 use "std.zig";
 
 pub fn main(argc: isize, argv: &&u8, env: &&u8) i32 => {
-    var x = 3 as i32;
+    var x = i32(3);
     const y = &x;
 
     *y += 1;
@@ -1116,7 +1116,7 @@ fn a() i32 => {}
 fn a() => {
     b();
 }
-    )SOURCE", 1, ".tmp_source.zig:3:5: error: undefined function: 'b'");
+    )SOURCE", 1, ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'");
 
     add_compile_fail_case("wrong number of arguments", R"SOURCE(
 fn a() => {
@@ -1282,7 +1282,7 @@ fn f() => {
     add_compile_fail_case("missing else clause", R"SOURCE(
 fn f() => {
     const x : i32 = if (true) { 1 };
-    const y = if (true) { 1 as i32 };
+    const y = if (true) { i32(1) };
 }
     )SOURCE", 2, ".tmp_source.zig:3:21: error: expected type 'i32', got 'void'",
                  ".tmp_source.zig:4:15: error: incompatible types: 'i32' and 'void'");
@@ -1383,9 +1383,9 @@ fn f() => {
 
     add_compile_fail_case("cast unreachable", R"SOURCE(
 fn f() i32 => {
-    (return 1) as i32
+    i32(return 1)
 }
-    )SOURCE", 1, ".tmp_source.zig:3:16: error: invalid cast from type 'unreachable' to 'i32'");
+    )SOURCE", 1, ".tmp_source.zig:3:8: error: invalid cast from type 'unreachable' to 'i32'");
 
     add_compile_fail_case("invalid builtin fn", R"SOURCE(
 fn f() @bogus(foo) => {
README.md
@@ -41,6 +41,12 @@ compromises backward compatibility.
    provide a tag or sha1).
  * Include documentation generator.
  * Shebang line OK so language can be used for "scripting" as well.
+ * Have the compiler run continuously, watching the file system for source
+   changes and automatically perform multithreaded compilation to build projects
+   quickly.
+ * Hot code swapping. When integrated with the previous feature, you could
+   press "save" in your editor and see the change immediately in your running
+   software.
 
 ### Current Status