Commit 272fe1c54c

Andrew Kelley <superjoe30@gmail.com>
2016-01-22 23:31:35
implicit casts rewrite the AST
1 parent b09a0cd
src/all_types.hpp
@@ -22,7 +22,6 @@ struct FnTableEntry;
 struct BlockContext;
 struct TypeTableEntry;
 struct VariableTableEntry;
-struct Cast;
 struct BuiltinFnEntry;
 struct LabelTableEntry;
 struct TypeStructField;
@@ -41,15 +40,6 @@ enum CodeGenBuildType {
     CodeGenBuildTypeRelease,
 };
 
-enum CastOp {
-    CastOpNothing,
-    CastOpPtrToInt,
-    CastOpIntWidenOrShorten,
-    CastOpToUnknownSizeArray,
-    CastOpMaybeWrap,
-    CastOpPointerReinterpret,
-};
-
 struct ConstEnumValue {
     uint64_t tag;
     ConstExprValue *payload;
@@ -69,29 +59,15 @@ struct ConstExprValue {
     } data;
 };
 
-struct Cast {
-    CastOp op;
-    // if op is CastOpArrayToString, this will be a pointer to
-    // the string struct on the stack
-    LLVMValueRef ptr;
-    TypeTableEntry *after_type;
-    AstNode *source_node;
-    ConstExprValue const_val;
-};
-
 struct Expr {
     TypeTableEntry *type_entry;
-    TypeTableEntry *resolved_type;
     // the context in which this expression is evaluated.
     // for blocks, this points to the containing scope, not the block's own scope for its children.
     BlockContext *block_context;
 
-    // may be null for no cast
-    Cast implicit_cast; // happens first
-    Cast implicit_maybe_cast; // happens second
-
     LLVMValueRef const_llvm_val;
     ConstExprValue const_val;
+    bool has_global_const;
 };
 
 struct StructValExprCodeGen {
@@ -305,6 +281,16 @@ struct AstNodeBinOpExpr {
     Expr resolved_expr;
 };
 
+enum CastOp {
+    CastOpNoCast, // signifies the function call expression is not a cast
+    CastOpNoop, // fn call expr is a cast, but does nothing
+    CastOpPtrToInt,
+    CastOpIntWidenOrShorten,
+    CastOpToUnknownSizeArray,
+    CastOpMaybeWrap,
+    CastOpPointerReinterpret,
+};
+
 struct AstNodeFnCallExpr {
     AstNode *fn_ref_expr;
     ZigList<AstNode *> params;
@@ -313,8 +299,11 @@ struct AstNodeFnCallExpr {
     // populated by semantic analyzer:
     BuiltinFnEntry *builtin_fn;
     Expr resolved_expr;
-    Cast cast;
     FnTableEntry *fn_entry;
+    CastOp cast_op;
+    // if cast_op is CastOpArrayToString, this will be a pointer to
+    // the string struct on the stack
+    LLVMValueRef tmp_ptr;
 };
 
 struct AstNodeArrayAccessExpr {
@@ -610,6 +599,8 @@ struct AstNodeSymbolExpr {
     Expr resolved_expr;
     VariableTableEntry *variable;
     FnTableEntry *fn_entry;
+    // set this to instead of analyzing the node, pretend it's a type entry and it's this one.
+    TypeTableEntry *override_type_entry;
 };
 
 struct AstNodeBoolLiteral {
@@ -644,6 +635,7 @@ struct AstNode {
     int column;
     uint32_t create_index; // for determinism purposes
     ImportTableEntry *owner;
+    AstNode **parent_field; // for AST rewriting
     union {
         AstNodeRoot root;
         AstNodeRootExportDecl root_export_decl;
@@ -997,7 +989,7 @@ struct BlockContext {
     BlockContext *parent; // null when this is the root
     HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
     HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table;
-    ZigList<Cast *> cast_expr_alloca_list;
+    ZigList<AstNode *> cast_alloca_list;
     ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
     ZigList<VariableTableEntry *> variable_list;
     AstNode *parent_loop_node;
src/analyze.cpp
@@ -17,6 +17,8 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE
         BlockContext *context, TypeTableEntry *expected_type, AstNode *node);
 static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *struct_type);
 static TypeTableEntry *unwrapped_node_type(AstNode *node);
+static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        AstNode *node);
 
 static AstNode *first_executing_node(AstNode *node) {
     switch (node->type) {
@@ -369,6 +371,9 @@ static TypeTableEntry *get_unknown_size_array_type(CodeGen *g, TypeTableEntry *c
 // 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) {
+    if (node->type == NodeTypeSymbol && node->data.symbol_expr.override_type_entry) {
+        return node->data.symbol_expr.override_type_entry;
+    }
     Expr *expr = get_resolved_expr(node);
     assert(expr->type_entry);
     if (expr->type_entry->id == TypeTableEntryIdInvalid) {
@@ -393,8 +398,9 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
 static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         AstNode *node)
 {
-    analyze_expression(g, import, context, nullptr, node);
-    return resolve_type(g, node);
+    AstNode **node_ptr = node->parent_field;
+    analyze_expression(g, import, context, nullptr, *node_ptr);
+    return resolve_type(g, *node_ptr);
 }
 
 static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry,
@@ -1044,12 +1050,43 @@ static TypeTableEntry *get_return_type(BlockContext *context) {
     return unwrapped_node_type(return_type_node);
 }
 
+static bool type_has_codegen_value(TypeTableEntryId id) {
+    switch (id) {
+        case TypeTableEntryIdInvalid:
+        case TypeTableEntryIdMetaType:
+        case TypeTableEntryIdVoid:
+        case TypeTableEntryIdUnreachable:
+        case TypeTableEntryIdNumLitFloat:
+        case TypeTableEntryIdNumLitInt:
+            return false;
+
+        case TypeTableEntryIdBool:
+        case TypeTableEntryIdInt:
+        case TypeTableEntryIdFloat:
+        case TypeTableEntryIdPointer:
+        case TypeTableEntryIdArray:
+        case TypeTableEntryIdStruct:
+        case TypeTableEntryIdMaybe:
+        case TypeTableEntryIdError:
+        case TypeTableEntryIdEnum:
+        case TypeTableEntryIdFn:
+            return true;
+    }
+    zig_unreachable();
+}
+
+static void add_global_const_expr(CodeGen *g, Expr *expr) {
+    if (expr->const_val.ok && type_has_codegen_value(expr->type_entry->id) && !expr->has_global_const) {
+        g->global_const_list.append(expr);
+        expr->has_global_const = true;
+    }
+}
+
 static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTableEntry *other_type) {
     Expr *expr = get_resolved_expr(literal_node);
     ConstExprValue *const_val = &expr->const_val;
     assert(const_val->ok);
     if (other_type->id == TypeTableEntryIdFloat) {
-        expr->resolved_type = other_type;
         return true;
     } else if (other_type->id == TypeTableEntryIdInt &&
                const_val->data.x_bignum.kind == BigNumKindInt)
@@ -1057,7 +1094,6 @@ static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTa
         if (bignum_fits_in_bits(&const_val->data.x_bignum, other_type->size_in_bits,
                     other_type->data.integral.is_signed))
         {
-            expr->resolved_type = other_type;
             return true;
         }
     } else if (other_type->id == TypeTableEntryIdNumLitFloat ||
@@ -1145,39 +1181,74 @@ static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *pa
     return prev_type;
 }
 
-static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *context, AstNode *node,
-        TypeTableEntry *expected_type, TypeTableEntry *actual_type)
-{
-    if (expected_type == nullptr)
-        return actual_type; // anything will do
+static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) {
     if (expected_type == actual_type)
-        return expected_type; // match
-    if (expected_type->id == TypeTableEntryIdInvalid || actual_type->id == TypeTableEntryIdInvalid)
-        return expected_type; // already complained
-    if (actual_type->id == TypeTableEntryIdUnreachable)
-        return actual_type; // sorry toots; gotta run. good luck with that expected type.
+        return true;
+
+    // pointer const
+    if (expected_type->id == TypeTableEntryIdPointer &&
+        actual_type->id == TypeTableEntryIdPointer &&
+        (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const))
+    {
+        return types_match_const_cast_only(expected_type->data.pointer.child_type,
+                actual_type->data.pointer.child_type);
+    }
 
+    // unknown size array const
+    if (expected_type->id == TypeTableEntryIdStruct &&
+        actual_type->id == TypeTableEntryIdStruct &&
+        expected_type->data.structure.is_unknown_size_array &&
+        actual_type->data.structure.is_unknown_size_array &&
+        (!actual_type->data.structure.fields[0].type_entry->data.pointer.is_const ||
+          expected_type->data.structure.fields[0].type_entry->data.pointer.is_const))
+    {
+        return types_match_const_cast_only(
+                expected_type->data.structure.fields[0].type_entry->data.pointer.child_type,
+                actual_type->data.structure.fields[0].type_entry->data.pointer.child_type);
+    }
+
+    // maybe
     if (expected_type->id == TypeTableEntryIdMaybe &&
         actual_type->id == TypeTableEntryIdMaybe)
     {
-        TypeTableEntry *expected_child = expected_type->data.maybe.child_type;
-        TypeTableEntry *actual_child = actual_type->data.maybe.child_type;
-        return resolve_type_compatibility(g, context, node, expected_child, actual_child);
+        return types_match_const_cast_only(
+                expected_type->data.maybe.child_type,
+                actual_type->data.maybe.child_type);
+    }
+
+    // error
+    if (expected_type->id == TypeTableEntryIdError &&
+        actual_type->id == TypeTableEntryIdError)
+    {
+        return types_match_const_cast_only(
+                expected_type->data.error.child_type,
+                actual_type->data.error.child_type);
+    }
+
+    // fn
+    if (expected_type->id == TypeTableEntryIdFn &&
+        actual_type->id == TypeTableEntryIdFn)
+    {
+        zig_panic("TODO types_match_const_cast_only for fns");
+    }
+
+
+    return false;
+}
+
+static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_type,
+        TypeTableEntry *actual_type, AstNode *literal_node, bool *reported_err)
+{
+    if (types_match_const_cast_only(expected_type, actual_type)) {
+        return true;
     }
 
     // implicit conversion from non maybe type to maybe type
-    if (expected_type->id == TypeTableEntryIdMaybe) {
-        TypeTableEntry *resolved_type = resolve_type_compatibility(g, context, node,
-                expected_type->data.maybe.child_type, actual_type);
-        if (resolved_type->id == TypeTableEntryIdInvalid) {
-            return resolved_type;
-        }
-        Expr *expr = get_resolved_expr(node);
-        expr->implicit_maybe_cast.op = CastOpMaybeWrap;
-        expr->implicit_maybe_cast.after_type = expected_type;
-        expr->implicit_maybe_cast.source_node = node;
-        context->cast_expr_alloca_list.append(&expr->implicit_maybe_cast);
-        return expected_type;
+    if (expected_type->id == TypeTableEntryIdMaybe &&
+        types_match_with_implicit_cast(g, expected_type->data.maybe.child_type, actual_type,
+            literal_node, reported_err))
+    {
+        return true;
     }
 
     // implicit widening conversion
@@ -1186,78 +1257,94 @@ static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *cont
         expected_type->data.integral.is_signed == actual_type->data.integral.is_signed &&
         expected_type->size_in_bits >= actual_type->size_in_bits)
     {
-        Expr *expr = get_resolved_expr(node);
-        expr->implicit_cast.after_type = expected_type;
-        expr->implicit_cast.op = CastOpIntWidenOrShorten;
-        expr->implicit_cast.source_node = node;
-        return expected_type;
+        return true;
     }
 
     // implicit constant sized array to unknown size array conversion
     if (expected_type->id == TypeTableEntryIdStruct &&
         expected_type->data.structure.is_unknown_size_array &&
         actual_type->id == TypeTableEntryIdArray &&
-        actual_type->data.array.child_type == expected_type->data.structure.fields[0].type_entry->data.pointer.child_type)
+        types_match_const_cast_only(
+            expected_type->data.structure.fields[0].type_entry->data.pointer.child_type,
+            actual_type->data.array.child_type))
     {
-        Expr *expr = get_resolved_expr(node);
-        expr->implicit_cast.after_type = expected_type;
-        expr->implicit_cast.op = CastOpToUnknownSizeArray;
-        expr->implicit_cast.source_node = node;
-        context->cast_expr_alloca_list.append(&expr->implicit_cast);
-        return expected_type;
-    }
-
-    // implicit non-const to const for pointers
-    if (expected_type->id == TypeTableEntryIdPointer &&
-        actual_type->id == TypeTableEntryIdPointer &&
-        (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const))
-    {
-        TypeTableEntry *resolved_type = resolve_type_compatibility(g, context, node,
-                expected_type->data.pointer.child_type,
-                actual_type->data.pointer.child_type);
-        if (resolved_type->id == TypeTableEntryIdInvalid) {
-            return resolved_type;
-        }
-        return expected_type;
-    }
-
-    // implicit non-const to const for unknown size arrays
-    if (expected_type->id == TypeTableEntryIdStruct &&
-        actual_type->id == TypeTableEntryIdStruct &&
-        expected_type->data.structure.is_unknown_size_array &&
-        actual_type->data.structure.is_unknown_size_array &&
-        (!actual_type->data.structure.fields[0].type_entry->data.pointer.is_const ||
-          expected_type->data.structure.fields[0].type_entry->data.pointer.is_const))
-    {
-        TypeTableEntry *resolved_type = resolve_type_compatibility(g, context, node,
-                expected_type->data.structure.fields[0].type_entry->data.pointer.child_type,
-                actual_type->data.structure.fields[0].type_entry->data.pointer.child_type);
-        if (resolved_type->id == TypeTableEntryIdInvalid) {
-            return resolved_type;
-        }
-        return expected_type;
+        return true;
     }
 
+    // implicit number literal to typed number
     if ((actual_type->id == TypeTableEntryIdNumLitFloat ||
          actual_type->id == TypeTableEntryIdNumLitInt))
     {
-        if (num_lit_fits_in_other_type(g, node, expected_type)) {
-            return expected_type;
+        if (num_lit_fits_in_other_type(g, literal_node, expected_type)) {
+            return true;
         } else {
-            return g->builtin_types.entry_invalid;
+            *reported_err = true;
         }
     }
 
-    add_node_error(g, first_executing_node(node),
-        buf_sprintf("expected type '%s', got '%s'",
-            buf_ptr(&expected_type->name),
-            buf_ptr(&actual_type->name)));
+
+    return false;
+}
+
+static AstNode *create_ast_node(CodeGen *g, ImportTableEntry *import, NodeType kind) {
+    AstNode *node = allocate<AstNode>(1);
+    node->type = kind;
+    node->owner = import;
+    node->create_index = g->next_node_index;
+    g->next_node_index += 1;
+    return node;
+}
+
+static AstNode *create_ast_type_node(CodeGen *g, ImportTableEntry *import, TypeTableEntry *type_entry) {
+    AstNode *node = create_ast_node(g, import, NodeTypeSymbol);
+    node->data.symbol_expr.override_type_entry = type_entry;
+    return node;
+}
+
+static TypeTableEntry *create_and_analyze_cast_node(CodeGen *g, ImportTableEntry *import,
+        BlockContext *context, TypeTableEntry *cast_to_type, AstNode *node)
+{
+    AstNode *new_parent_node = create_ast_node(g, import, NodeTypeFnCallExpr);
+    *node->parent_field = new_parent_node;
+    new_parent_node->parent_field = node->parent_field;
+
+    new_parent_node->data.fn_call_expr.fn_ref_expr = create_ast_type_node(g, import, cast_to_type);
+    new_parent_node->data.fn_call_expr.params.append(node);
+    normalize_parent_ptrs(new_parent_node);
+
+    return analyze_expression(g, import, context, cast_to_type, new_parent_node);
+}
+
+static TypeTableEntry *resolve_type_compatibility(CodeGen *g, ImportTableEntry *import,
+        BlockContext *context, AstNode *node,
+        TypeTableEntry *expected_type, TypeTableEntry *actual_type)
+{
+    if (expected_type == nullptr)
+        return actual_type; // anything will do
+    if (expected_type == actual_type)
+        return expected_type; // match
+    if (expected_type->id == TypeTableEntryIdInvalid || actual_type->id == TypeTableEntryIdInvalid)
+        return g->builtin_types.entry_invalid;
+    if (actual_type->id == TypeTableEntryIdUnreachable)
+        return actual_type;
+
+    bool reported_err = false;
+    if (types_match_with_implicit_cast(g, expected_type, actual_type, node, &reported_err)) {
+        return create_and_analyze_cast_node(g, import, context, expected_type, node);
+    }
+
+    if (!reported_err) {
+        add_node_error(g, first_executing_node(node),
+            buf_sprintf("expected type '%s', got '%s'",
+                buf_ptr(&expected_type->name),
+                buf_ptr(&actual_type->name)));
+    }
 
     return g->builtin_types.entry_invalid;
 }
 
-static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, BlockContext *block_context,
-        AstNode *parent_source_node,
+static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *import,
+        BlockContext *block_context, AstNode *parent_source_node,
         AstNode **child_nodes, TypeTableEntry **child_types, int child_count)
 {
     assert(child_count > 0);
@@ -1270,7 +1357,14 @@ static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, BlockContext
     }
 
     for (int i = 0; i < child_count; i += 1) {
-        resolve_type_compatibility(g, block_context, child_nodes[i], expected_type, child_types[i]);
+        if (!child_nodes[i]) {
+            continue;
+        }
+        Expr *expr = get_resolved_expr(child_nodes[i]);
+        TypeTableEntry *resolved_type = resolve_type_compatibility(g, import, block_context,
+                child_nodes[i], expected_type, child_types[i]);
+        expr->type_entry = resolved_type;
+        add_global_const_expr(g, expr);
     }
 
     return expected_type;
@@ -1667,13 +1761,7 @@ static TypeTableEntry *resolve_expr_const_val_as_type(CodeGen *g, AstNode *node,
 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);
-    ConstExprValue *other_const_val;
-    if (other_expr->implicit_maybe_cast.after_type) {
-        other_const_val = &other_expr->implicit_maybe_cast.const_val;
-    } else {
-        other_const_val = &other_expr->const_val;
-    }
-    expr->const_val = *other_const_val;
+    expr->const_val = other_expr->const_val;
     return other_expr->type_entry;
 }
 
@@ -1758,6 +1846,10 @@ static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode *
 static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
+    if (node->data.symbol_expr.override_type_entry) {
+        return resolve_expr_const_val_as_type(g, node, node->data.symbol_expr.override_type_entry);
+    }
+
     Buf *variable_name = &node->data.symbol_expr.symbol;
 
     auto primitive_table_entry = g->primitive_type_table.maybe_get(variable_name);
@@ -1772,13 +1864,7 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import,
             AstNode *decl_node = var->decl_node;
             if (decl_node->type == NodeTypeVariableDeclaration) {
                 AstNode *expr_node = decl_node->data.variable_declaration.expr;
-                Expr *other_expr = get_resolved_expr(expr_node);
-                ConstExprValue *other_const_val;
-                if (other_expr->implicit_maybe_cast.after_type) {
-                    other_const_val = &other_expr->implicit_maybe_cast.const_val;
-                } else {
-                    other_const_val = &other_expr->const_val;
-                }
+                ConstExprValue *other_const_val = &get_resolved_expr(expr_node)->const_val;
                 if (other_const_val->ok) {
                     return resolve_expr_const_val_as_other_expr(g, node, expr_node);
                 }
@@ -1955,7 +2041,7 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
     AstNode *op_nodes[] = {op1, op2};
     TypeTableEntry *op_types[] = {op1_type, op2_type};
 
-    TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, context, node,
+    TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node,
             op_nodes, op_types, 2);
 
     if (resolved_type->id == TypeTableEntryIdInvalid) {
@@ -2109,7 +2195,7 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
                 AstNode *op_nodes[] = {op1, op2};
                 TypeTableEntry *op_types[] = {lhs_type, rhs_type};
 
-                TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, context, node,
+                TypeTableEntry *resolved_type = resolve_peer_type_compatibility(g, import, context, node,
                         op_nodes, op_types, 2);
 
                 if (resolved_type->id == TypeTableEntryIdInvalid) {
@@ -2162,6 +2248,7 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
                     add_node_error(g, op1,
                         buf_sprintf("expected maybe type, got '%s'",
                             buf_ptr(&lhs_type->name)));
+                    return g->builtin_types.entry_invalid;
                 }
             }
         case BinOpTypeInvalid:
@@ -2502,8 +2589,8 @@ static TypeTableEntry *analyze_if_then_else(CodeGen *g, ImportTableEntry *import
     if (else_node) {
         else_type = analyze_expression(g, import, context, expected_type, else_node);
     } else {
-        else_type = g->builtin_types.entry_void;
-        else_type = resolve_type_compatibility(g, context, parent_node, expected_type, else_type);
+        else_type = resolve_type_compatibility(g, import, context, parent_node, expected_type,
+                g->builtin_types.entry_void);
     }
 
 
@@ -2512,7 +2599,7 @@ static TypeTableEntry *analyze_if_then_else(CodeGen *g, ImportTableEntry *import
     } else {
         AstNode *op_nodes[] = {then_block, else_node};
         TypeTableEntry *op_types[] = {then_type, else_type};
-        return resolve_peer_type_compatibility(g, context, parent_node, op_nodes, op_types, 2);
+        return resolve_peer_type_compatibility(g, import, context, parent_node, op_nodes, op_types, 2);
     }
 }
 
@@ -2616,37 +2703,30 @@ 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:
+static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *expr_node) {
+    assert(node->type == NodeTypeFnCallExpr);
+    ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val;
+    ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+    if (!other_val->ok) {
+        return;
+    }
+    assert(other_val != const_val);
+    switch (node->data.fn_call_expr.cast_op) {
+        case CastOpNoCast:
+            zig_unreachable();
+        case CastOpNoop:
         case CastOpPtrToInt:
         case CastOpIntWidenOrShorten:
         case CastOpPointerReinterpret:
-            {
-                ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val;
-                ConstExprValue *const_val = &cast->const_val;
-                assert(const_val != other_val);
-                *const_val = *other_val;
-                break;
-            }
+            *const_val = *other_val;
+            break;
         case CastOpToUnknownSizeArray:
-            // TODO eval const expr
+            zig_panic("TODO CastOpToUnknownSizeArray");
             break;
         case CastOpMaybeWrap:
-            {
-                ConstExprValue *other_val = &get_resolved_expr(expr_node)->const_val;
-                ConstExprValue *const_val = &cast->const_val;
-                if (!other_val->ok) {
-                    break;
-                }
-                assert(const_val != other_val);
-
-                const_val->data.x_maybe = other_val;
-                const_val->ok = true;
-                break;
-            }
+            const_val->data.x_maybe = other_val;
+            const_val->ok = true;
+            break;
     }
 }
 
@@ -2673,51 +2753,91 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
         return g->builtin_types.entry_invalid;
     }
 
-    Cast *cast = &node->data.fn_call_expr.cast;
-    cast->source_node = node;
-    cast->after_type = wanted_type;
+    // explicit match or non-const to const
+    if (types_match_const_cast_only(wanted_type, actual_type)) {
+        node->data.fn_call_expr.cast_op = CastOpNoop;
+        eval_const_expr_implicit_cast(g, node, expr_node);
+        return wanted_type;
+    }
 
+    // explicit cast from pointer to isize or usize
     if ((wanted_type == g->builtin_types.entry_isize || wanted_type == g->builtin_types.entry_usize) &&
         actual_type->id == TypeTableEntryIdPointer)
     {
-        cast->op = CastOpPtrToInt;
-        eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
+        node->data.fn_call_expr.cast_op = CastOpPtrToInt;
+        eval_const_expr_implicit_cast(g, node, expr_node);
         return wanted_type;
-    } else if (wanted_type->id == TypeTableEntryIdInt &&
-                actual_type->id == TypeTableEntryIdInt)
+    }
+
+    // explicit cast from any int to any other int
+    if (wanted_type->id == TypeTableEntryIdInt &&
+        actual_type->id == TypeTableEntryIdInt)
     {
-        cast->op = CastOpIntWidenOrShorten;
-        eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
+        node->data.fn_call_expr.cast_op = CastOpIntWidenOrShorten;
+        eval_const_expr_implicit_cast(g, node, expr_node);
         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)
+    }
+
+    // explicit cast from fixed size array to unknown size array
+    if (wanted_type->id == TypeTableEntryIdStruct &&
+        wanted_type->data.structure.is_unknown_size_array &&
+        actual_type->id == TypeTableEntryIdArray &&
+        types_match_const_cast_only(
+            wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type,
+            actual_type->data.array.child_type))
     {
-        cast->op = CastOpToUnknownSizeArray;
-        context->cast_expr_alloca_list.append(cast);
-        eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
+        node->data.fn_call_expr.cast_op = CastOpToUnknownSizeArray;
+        context->cast_alloca_list.append(node);
+        eval_const_expr_implicit_cast(g, node, expr_node);
         return wanted_type;
-    } else if (actual_type->id == TypeTableEntryIdNumLitFloat ||
-                actual_type->id == TypeTableEntryIdNumLitInt)
+    }
+
+    // explicit cast from pointer to another pointer
+    if (actual_type->id == TypeTableEntryIdPointer &&
+        wanted_type->id == TypeTableEntryIdPointer)
     {
-        num_lit_fits_in_other_type(g, expr_node, wanted_type);
-        cast->op = CastOpNothing;
-        eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
+        node->data.fn_call_expr.cast_op = CastOpPointerReinterpret;
+        eval_const_expr_implicit_cast(g, node, expr_node);
         return wanted_type;
-    } else if (actual_type->id == TypeTableEntryIdPointer &&
-               wanted_type->id == TypeTableEntryIdPointer)
+    }
+
+    // explicit cast from child type of maybe type to maybe type
+    if (wanted_type->id == TypeTableEntryIdMaybe) {
+        if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) {
+            node->data.fn_call_expr.cast_op = CastOpMaybeWrap;
+            eval_const_expr_implicit_cast(g, node, expr_node);
+            return wanted_type;
+        } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
+                   actual_type->id == TypeTableEntryIdNumLitFloat)
+        {
+            if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.maybe.child_type)) {
+                node->data.fn_call_expr.cast_op = CastOpMaybeWrap;
+                eval_const_expr_implicit_cast(g, node, expr_node);
+                return wanted_type;
+            } else {
+                return g->builtin_types.entry_invalid;
+            }
+        }
+    }
+
+    // explicit cast from number literal to another type
+    if (actual_type->id == TypeTableEntryIdNumLitFloat ||
+        actual_type->id == TypeTableEntryIdNumLitInt)
     {
-        cast->op = CastOpPointerReinterpret;
-        eval_const_expr_implicit_cast(g, import, context, node, cast, expr_node);
-        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;
+        if (num_lit_fits_in_other_type(g, expr_node, wanted_type)) {
+            node->data.fn_call_expr.cast_op = CastOpNoop;
+            eval_const_expr_implicit_cast(g, node, expr_node);
+            return wanted_type;
+        } else {
+            return g->builtin_types.entry_invalid;
+        }
     }
+
+    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,
@@ -3281,39 +3401,14 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import,
         actual_return_type = g->builtin_types.entry_invalid;
     }
 
-    resolve_type_compatibility(g, context, node, expected_return_type, actual_return_type);
+    resolve_type_compatibility(g, import, context, node, expected_return_type, actual_return_type);
 
     return g->builtin_types.entry_unreachable;
 }
 
-static bool type_has_codegen_value(TypeTableEntryId id) {
-    switch (id) {
-        case TypeTableEntryIdInvalid:
-        case TypeTableEntryIdMetaType:
-        case TypeTableEntryIdVoid:
-        case TypeTableEntryIdUnreachable:
-            return false;
-
-        // TODO make num lits return false when we make implicit casts insert ast nodes
-        case TypeTableEntryIdNumLitFloat:
-        case TypeTableEntryIdNumLitInt:
-
-        case TypeTableEntryIdBool:
-        case TypeTableEntryIdInt:
-        case TypeTableEntryIdFloat:
-        case TypeTableEntryIdPointer:
-        case TypeTableEntryIdArray:
-        case TypeTableEntryIdStruct:
-        case TypeTableEntryIdMaybe:
-        case TypeTableEntryIdError:
-        case TypeTableEntryIdEnum:
-        case TypeTableEntryIdFn:
-            return true;
-    }
-    zig_unreachable();
-}
-
-static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+// When you call analyze_expression, the node you pass might no longer be the child node
+// you thought it was due to implicit casting rewriting the AST.
+static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
     TypeTableEntry *return_type = nullptr;
@@ -3494,39 +3589,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
             zig_unreachable();
     }
     assert(return_type);
-    resolve_type_compatibility(g, context, node, expected_type, return_type);
+    // resolve_type_compatibility might do implicit cast which means node is now a child
+    // of the actual node that we want to return the type of.
+    //AstNode **field = node->parent_field;
+    TypeTableEntry *resolved_type = resolve_type_compatibility(g, import, context, node,
+            expected_type, return_type);
 
     Expr *expr = get_resolved_expr(node);
-    if (!expr->resolved_type) {
-        expr->resolved_type = return_type;
-    }
-    expr->type_entry = expr->resolved_type;
+    expr->type_entry = return_type;
     expr->block_context = context;
 
-    if (expr->const_val.ok && type_has_codegen_value(expr->resolved_type->id)) {
-        g->global_const_list.append(expr);
-    }
+    add_global_const_expr(g, expr);
 
-
-    if (expr->type_entry->id == TypeTableEntryIdUnreachable) {
-        return expr->type_entry;
-    }
-
-    /* TODO delete this code when we make implicit casts insert ast nodes
-    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;
+    return resolved_type;
 }
 
 static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node) {
src/codegen.cpp
@@ -71,8 +71,6 @@ 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) {
     Expr *expr = get_resolved_expr(node);
@@ -111,17 +109,7 @@ static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str, bool c) {
 }
 
 static TypeTableEntry *get_expr_type(AstNode *node) {
-    Expr *expr = get_resolved_expr(node);
-    if (expr->implicit_maybe_cast.after_type) {
-        return expr->implicit_maybe_cast.after_type;
-    }
-    if (expr->implicit_cast.after_type) {
-        return expr->implicit_cast.after_type;
-    }
-    if (expr->resolved_type) {
-        return expr->resolved_type;
-    }
-    return expr->type_entry;
+    return get_resolved_expr(node)->type_entry;
 }
 
 static TypeTableEntry *fn_proto_type_from_type_node(CodeGen *g, AstNode *type_node) {
@@ -305,18 +293,84 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *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;
+    AstNodeFnCallExpr *cast_expr = &node->data.fn_call_expr;
+
+    switch (cast_expr->cast_op) {
+        case CastOpNoCast:
+            zig_unreachable();
+        case CastOpNoop:
+            return expr_val;
+        case CastOpMaybeWrap:
+            {
+                assert(cast_expr->tmp_ptr);
+                assert(wanted_type->id == TypeTableEntryIdMaybe);
+                assert(actual_type);
+
+                add_debug_source_node(g, node);
+                LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, "");
+                gen_assign_raw(g, node, BinOpTypeAssign,
+                        val_ptr, expr_val, wanted_type->data.maybe.child_type, actual_type);
+
+                add_debug_source_node(g, node);
+                LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, "");
+                LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr);
+
+                return cast_expr->tmp_ptr;
+            }
+        case CastOpPtrToInt:
+            add_debug_source_node(g, node);
+            return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
+        case CastOpPointerReinterpret:
+            add_debug_source_node(g, node);
+            return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, "");
+        case CastOpIntWidenOrShorten:
+            if (actual_type->size_in_bits == wanted_type->size_in_bits) {
+                return expr_val;
+            } else if (actual_type->size_in_bits < wanted_type->size_in_bits) {
+                if (actual_type->data.integral.is_signed) {
+                    add_debug_source_node(g, node);
+                    return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, "");
+                } else {
+                    add_debug_source_node(g, node);
+                    return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, "");
+                }
+            } else {
+                assert(actual_type->size_in_bits > wanted_type->size_in_bits);
+                add_debug_source_node(g, node);
+                return LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, "");
+            }
+        case CastOpToUnknownSizeArray:
+            {
+                assert(cast_expr->tmp_ptr);
+                assert(wanted_type->id == TypeTableEntryIdStruct);
+                assert(wanted_type->data.structure.is_unknown_size_array);
+
+                TypeTableEntry *pointer_type = wanted_type->data.structure.fields[0].type_entry;
+
+                add_debug_source_node(g, node);
 
-    return gen_bare_cast(g, node, expr_val, actual_type, wanted_type, cast_node);
+                LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, "");
+                LLVMValueRef expr_bitcast = LLVMBuildBitCast(g->builder, expr_val, pointer_type->type_ref, "");
+                LLVMBuildStore(g->builder, expr_bitcast, ptr_ptr);
 
+                LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, "");
+                LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref,
+                        actual_type->data.array.len, false);
+                LLVMBuildStore(g->builder, len_val, len_ptr);
+
+                return cast_expr->tmp_ptr;
+            }
+    }
+    zig_unreachable();
 }
 
+
 static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
     assert(node->type == NodeTypeFnCallExpr);
 
     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) {
+    } else if (node->data.fn_call_expr.cast_op != CastOpNoCast) {
         return gen_cast_expr(g, node);
     }
 
@@ -752,74 +806,6 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
     zig_unreachable();
 }
 
-static LLVMValueRef gen_bare_cast(CodeGen *g, AstNode *node, LLVMValueRef expr_val,
-        TypeTableEntry *actual_type, TypeTableEntry *wanted_type, Cast *cast_node)
-{
-    switch (cast_node->op) {
-        case CastOpNothing:
-            return expr_val;
-        case CastOpMaybeWrap:
-            {
-                assert(cast_node->ptr);
-                assert(wanted_type->id == TypeTableEntryIdMaybe);
-                assert(actual_type);
-
-                add_debug_source_node(g, node);
-                LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, cast_node->ptr, 0, "");
-                gen_assign_raw(g, node, BinOpTypeAssign,
-                        val_ptr, expr_val, wanted_type->data.maybe.child_type, actual_type);
-
-                add_debug_source_node(g, node);
-                LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, cast_node->ptr, 1, "");
-                LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr);
-
-                return cast_node->ptr;
-            }
-        case CastOpPtrToInt:
-            add_debug_source_node(g, node);
-            return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
-        case CastOpPointerReinterpret:
-            add_debug_source_node(g, node);
-            return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, "");
-        case CastOpIntWidenOrShorten:
-            if (actual_type->size_in_bits == wanted_type->size_in_bits) {
-                return expr_val;
-            } else if (actual_type->size_in_bits < wanted_type->size_in_bits) {
-                if (actual_type->data.integral.is_signed) {
-                    add_debug_source_node(g, node);
-                    return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, "");
-                } else {
-                    add_debug_source_node(g, node);
-                    return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, "");
-                }
-            } else {
-                assert(actual_type->size_in_bits > wanted_type->size_in_bits);
-                add_debug_source_node(g, node);
-                return LLVMBuildTrunc(g->builder, expr_val, wanted_type->type_ref, "");
-            }
-        case CastOpToUnknownSizeArray:
-            {
-                assert(cast_node->ptr);
-
-                TypeTableEntry *pointer_type = wanted_type->data.structure.fields[0].type_entry;
-
-                add_debug_source_node(g, node);
-
-                LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, cast_node->ptr, 0, "");
-                LLVMValueRef expr_bitcast = LLVMBuildBitCast(g->builder, expr_val, pointer_type->type_ref, "");
-                LLVMBuildStore(g->builder, expr_bitcast, ptr_ptr);
-
-                LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, cast_node->ptr, 1, "");
-                LLVMValueRef len_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref,
-                        actual_type->data.array.len, false);
-                LLVMBuildStore(g->builder, len_val, len_ptr);
-
-                return cast_node->ptr;
-            }
-    }
-    zig_unreachable();
-}
-
 static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
     LLVMValueRef val1, LLVMValueRef val2,
     TypeTableEntry *op1_type, TypeTableEntry *op2_type,
@@ -1970,7 +1956,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
     return phi;
 }
 
-static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
+static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
     Expr *expr = get_resolved_expr(node);
     if (expr->const_val.ok) {
         assert(expr->const_llvm_val);
@@ -2072,33 +2058,6 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
     zig_unreachable();
 }
 
-static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
-    LLVMValueRef val = gen_expr_no_cast(g, node);
-
-    if (is_node_void_expr(node)) {
-        return val;
-    }
-
-    Expr *expr = get_resolved_expr(node);
-
-    TypeTableEntry *before_type = expr->type_entry;
-    if (before_type && before_type->id == TypeTableEntryIdUnreachable) {
-        return val;
-    }
-    Cast *cast_node = &expr->implicit_cast;
-    if (cast_node->after_type) {
-        val = gen_bare_cast(g, node, val, before_type, cast_node->after_type, cast_node);
-        before_type = cast_node->after_type;
-    }
-
-    cast_node = &expr->implicit_maybe_cast;
-    if (cast_node->after_type) {
-        val = gen_bare_cast(g, node, val, before_type, cast_node->after_type, cast_node);
-    }
-
-    return val;
-}
-
 static void build_label_blocks(CodeGen *g, AstNode *block_node) {
     assert(block_node->type == NodeTypeBlock);
     for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
@@ -2180,14 +2139,7 @@ static void gen_const_globals(CodeGen *g) {
         Expr *expr = g->global_const_list.at(i);
         ConstExprValue *const_val = &expr->const_val;
         assert(const_val->ok);
-        TypeTableEntry *type_entry = expr->resolved_type;
-
-        // TODO delete this if when we make implicit casts insert ast nodes
-        if (type_entry->id == TypeTableEntryIdNumLitFloat ||
-            type_entry->id == TypeTableEntryIdNumLitInt)
-        {
-            continue;
-        }
+        TypeTableEntry *type_entry = expr->type_entry;
 
         if (handle_is_ptr(type_entry)) {
             LLVMValueRef global_value = LLVMAddGlobal(g->module, type_entry->type_ref, "");
@@ -2330,10 +2282,12 @@ static void do_code_gen(CodeGen *g) {
             }
 
             // allocate structs which are the result of casts
-            for (int cea_i = 0; cea_i < block_context->cast_expr_alloca_list.length; cea_i += 1) {
-                Cast *cast_node = block_context->cast_expr_alloca_list.at(cea_i);
-                add_debug_source_node(g, cast_node->source_node);
-                cast_node->ptr = LLVMBuildAlloca(g->builder, cast_node->after_type->type_ref, "");
+            for (int cea_i = 0; cea_i < block_context->cast_alloca_list.length; cea_i += 1) {
+                AstNode *fn_call_node = block_context->cast_alloca_list.at(cea_i);
+                add_debug_source_node(g, fn_call_node);
+                Expr *expr = &fn_call_node->data.fn_call_expr.resolved_expr;
+                fn_call_node->data.fn_call_expr.tmp_ptr = LLVMBuildAlloca(g->builder,
+                        expr->type_entry->type_ref, "");
             }
 
             // allocate structs which are struct value expressions
src/parser.cpp
@@ -173,6 +173,7 @@ void ast_print(AstNode *node, int indent) {
     for (int i = 0; i < indent; i += 1) {
         fprintf(stderr, " ");
     }
+    assert(node->type == NodeTypeRoot || *node->parent_field == node);
 
     switch (node->type) {
         case NodeTypeRoot:
@@ -1008,6 +1009,7 @@ static AstNode *ast_parse_directive(ParseContext *pc, int *token_index) {
     *token_index += 1;
     ast_expect_token(pc, r_paren, TokenIdRParen);
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -1059,6 +1061,7 @@ static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) {
 
     node->data.param_decl.type = ast_parse_prefix_op_expr(pc, token_index, true);
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -1175,6 +1178,7 @@ static AstNode *ast_parse_array_type_expr(ParseContext *pc, int *token_index, bo
 
     node->data.array_type.child_type = ast_parse_prefix_op_expr(pc, token_index, true);
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -1358,6 +1362,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand
     ast_expect_token(pc, rparen_tok, TokenIdRParen);
     *token_index += 1;
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -1416,6 +1421,8 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
         ast_eat_token(pc, token_index, TokenIdLParen);
         ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params);
         node->data.fn_call_expr.is_builtin = true;
+
+        normalize_parent_ptrs(node);
         return node;
     } else if (token->id == TokenIdSymbol) {
         *token_index += 1;
@@ -1499,6 +1506,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, int *token_index,
                         ast_buf_from_token(pc, field_name_tok, &field_node->data.struct_val_field.name);
                         field_node->data.struct_val_field.expr = ast_parse_expression(pc, token_index, true);
 
+                        normalize_parent_ptrs(field_node);
                         node->data.container_init_expr.entries.append(field_node);
 
                         Token *comma_tok = &pc->tokens->at(*token_index);
@@ -1545,6 +1553,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, int *token_index,
                 }
             }
 
+            normalize_parent_ptrs(node);
             prefix_op_expr = node;
         } else {
             return prefix_op_expr;
@@ -1575,6 +1584,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo
             node->data.fn_call_expr.fn_ref_expr = primary_expr;
             ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params);
 
+            normalize_parent_ptrs(node);
             primary_expr = node;
         } else if (first_token->id == TokenIdLBracket) {
             *token_index += 1;
@@ -1599,6 +1609,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo
                     node->data.slice_expr.is_const = true;
                 }
 
+                normalize_parent_ptrs(node);
                 primary_expr = node;
             } else if (ellipsis_or_r_bracket->id == TokenIdRBracket) {
                 *token_index += 1;
@@ -1607,6 +1618,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo
                 node->data.array_access_expr.array_ref_expr = primary_expr;
                 node->data.array_access_expr.subscript = expr_node;
 
+                normalize_parent_ptrs(node);
                 primary_expr = node;
             } else {
                 ast_invalid_token_error(pc, first_token);
@@ -1620,6 +1632,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, int *token_index, boo
             node->data.field_access_expr.struct_expr = primary_expr;
             ast_buf_from_token(pc, name_token, &node->data.field_access_expr.field_name);
 
+            normalize_parent_ptrs(node);
             primary_expr = node;
         } else {
             return primary_expr;
@@ -1677,6 +1690,8 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo
     node->data.prefix_op_expr.primary_expr = prefix_op_expr;
     node->data.prefix_op_expr.prefix_op = prefix_op;
 
+    normalize_parent_ptrs(node);
+    normalize_parent_ptrs(parent_node);
     return parent_node;
 }
 
@@ -1728,6 +1743,7 @@ static AstNode *ast_parse_mult_expr(ParseContext *pc, int *token_index, bool man
         node->data.bin_op_expr.bin_op = mult_op;
         node->data.bin_op_expr.op2 = operand_2;
 
+        normalize_parent_ptrs(node);
         operand_1 = node;
     }
 }
@@ -1778,6 +1794,7 @@ static AstNode *ast_parse_add_expr(ParseContext *pc, int *token_index, bool mand
         node->data.bin_op_expr.bin_op = add_op;
         node->data.bin_op_expr.op2 = operand_2;
 
+        normalize_parent_ptrs(node);
         operand_1 = node;
     }
 }
@@ -1828,6 +1845,7 @@ static AstNode *ast_parse_bit_shift_expr(ParseContext *pc, int *token_index, boo
         node->data.bin_op_expr.bin_op = bit_shift_op;
         node->data.bin_op_expr.op2 = operand_2;
 
+        normalize_parent_ptrs(node);
         operand_1 = node;
     }
 }
@@ -1854,6 +1872,7 @@ static AstNode *ast_parse_bin_and_expr(ParseContext *pc, int *token_index, bool
         node->data.bin_op_expr.bin_op = BinOpTypeBinAnd;
         node->data.bin_op_expr.op2 = operand_2;
 
+        normalize_parent_ptrs(node);
         operand_1 = node;
     }
 }
@@ -1879,6 +1898,7 @@ static AstNode *ast_parse_bin_xor_expr(ParseContext *pc, int *token_index, bool
         node->data.bin_op_expr.bin_op = BinOpTypeBinXor;
         node->data.bin_op_expr.op2 = operand_2;
 
+        normalize_parent_ptrs(node);
         operand_1 = node;
     }
 }
@@ -1904,6 +1924,7 @@ static AstNode *ast_parse_bin_or_expr(ParseContext *pc, int *token_index, bool m
         node->data.bin_op_expr.bin_op = BinOpTypeBinOr;
         node->data.bin_op_expr.op2 = operand_2;
 
+        normalize_parent_ptrs(node);
         operand_1 = node;
     }
 }
@@ -1954,6 +1975,7 @@ static AstNode *ast_parse_comparison_expr(ParseContext *pc, int *token_index, bo
     node->data.bin_op_expr.bin_op = cmp_op;
     node->data.bin_op_expr.op2 = operand_2;
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -1978,6 +2000,7 @@ static AstNode *ast_parse_bool_and_expr(ParseContext *pc, int *token_index, bool
         node->data.bin_op_expr.bin_op = BinOpTypeBoolAnd;
         node->data.bin_op_expr.op2 = operand_2;
 
+        normalize_parent_ptrs(node);
         operand_1 = node;
     }
 }
@@ -2043,6 +2066,8 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda
         ast_eat_token(pc, token_index, TokenIdRParen);
         node->data.if_var_expr.then_block = ast_parse_expression(pc, token_index, true);
         node->data.if_var_expr.else_node = ast_parse_else(pc, token_index, false);
+
+        normalize_parent_ptrs(node);
         return node;
     } else {
         AstNode *node = ast_create_node(pc, NodeTypeIfBoolExpr, if_tok);
@@ -2050,6 +2075,8 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda
         ast_eat_token(pc, token_index, TokenIdRParen);
         node->data.if_bool_expr.then_block = ast_parse_expression(pc, token_index, true);
         node->data.if_bool_expr.else_node = ast_parse_else(pc, token_index, false);
+
+        normalize_parent_ptrs(node);
         return node;
     }
 }
@@ -2094,6 +2121,8 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m
     AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, token);
     node->data.return_expr.kind = kind;
     node->data.return_expr.expr = ast_parse_expression(pc, token_index, false);
+
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2156,6 +2185,8 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token
     *token_index += 1;
     if (eq_or_colon->id == TokenIdEq) {
         node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true);
+
+        normalize_parent_ptrs(node);
         return node;
     } else if (eq_or_colon->id == TokenIdColon) {
         node->data.variable_declaration.type = ast_parse_prefix_op_expr(pc, token_index, true);
@@ -2165,6 +2196,8 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token
 
             node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true);
         }
+
+        normalize_parent_ptrs(node);
         return node;
     } else {
         ast_invalid_token_error(pc, eq_or_colon);
@@ -2192,6 +2225,7 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, int *token_index, bool
         node->data.bin_op_expr.bin_op = BinOpTypeBoolOr;
         node->data.bin_op_expr.op2 = operand_2;
 
+        normalize_parent_ptrs(node);
         operand_1 = node;
     }
 }
@@ -2220,7 +2254,7 @@ static AstNode *ast_parse_while_expr(ParseContext *pc, int *token_index, bool ma
     node->data.while_expr.body = ast_parse_expression(pc, token_index, true);
 
 
-
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2262,6 +2296,8 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, int *token_index, bool mand
     ast_eat_token(pc, token_index, TokenIdRParen);
 
     node->data.for_expr.body = ast_parse_expression(pc, token_index, true);
+
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2294,6 +2330,8 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m
 
         if (token->id == TokenIdRBrace) {
             *token_index += 1;
+
+            normalize_parent_ptrs(node);
             return node;
         }
 
@@ -2313,6 +2351,8 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m
 
                 range_node->data.switch_range.start = expr1;
                 range_node->data.switch_range.end = ast_parse_expression(pc, token_index, true);
+
+                normalize_parent_ptrs(range_node);
             } else {
                 prong_node->data.switch_prong.items.append(expr1);
             }
@@ -2335,6 +2375,8 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m
         ast_eat_token(pc, token_index, TokenIdFatArrow);
         prong_node->data.switch_prong.expr = ast_parse_expression(pc, token_index, true);
         ast_eat_token(pc, token_index, TokenIdComma);
+
+        normalize_parent_ptrs(prong_node);
     }
 }
 
@@ -2430,6 +2472,7 @@ static AstNode *ast_parse_unwrap_maybe_expr(ParseContext *pc, int *token_index,
     node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe;
     node->data.bin_op_expr.op2 = rhs;
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2453,6 +2496,7 @@ static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mand
     node->data.bin_op_expr.bin_op = ass_op;
     node->data.bin_op_expr.op2 = rhs;
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2530,6 +2574,7 @@ static AstNode *ast_create_void_expr(ParseContext *pc, Token *token) {
     node->data.container_init_expr.type = ast_create_node(pc, NodeTypeSymbol, token);
     node->data.container_init_expr.kind = ContainerInitKindArray;
     buf_init_from_str(&node->data.container_init_expr.type->data.symbol_expr.symbol, "void");
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2581,6 +2626,8 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato
         last_token = &pc->tokens->at(*token_index);
         if (last_token->id == TokenIdRBrace) {
             *token_index += 1;
+
+            normalize_parent_ptrs(node);
             return node;
         } else if (!semicolon_expected) {
             continue;
@@ -2651,6 +2698,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand
         node->data.fn_proto.return_type = ast_create_void_type_node(pc, next_token);
     }
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2667,6 +2715,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandat
     ast_eat_token(pc, token_index, TokenIdFatArrow);
     node->data.fn_def.body = ast_parse_block(pc, token_index, true);
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2683,6 +2732,7 @@ static AstNode *ast_parse_fn_decl(ParseContext *pc, int *token_index) {
     *token_index += 1;
     ast_expect_token(pc, semicolon, TokenIdSemicolon);
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2725,6 +2775,8 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int *token_index, bool
             pc->directive_list = nullptr;
 
             *token_index += 1;
+
+            normalize_parent_ptrs(node);
             return node;
         } else {
             AstNode *child = ast_parse_fn_decl(pc, token_index);
@@ -2768,6 +2820,7 @@ static AstNode *ast_parse_root_export_decl(ParseContext *pc, int *token_index, b
     *token_index += 1;
     ast_expect_token(pc, semicolon, TokenIdSemicolon);
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2795,6 +2848,7 @@ static AstNode *ast_parse_use(ParseContext *pc, int *token_index) {
     node->data.use.directives = pc->directive_list;
     pc->directive_list = nullptr;
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2895,12 +2949,13 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) {
             }
 
             node->data.struct_decl.fields.append(field_node);
+            normalize_parent_ptrs(field_node);
         } else {
             ast_invalid_token_error(pc, token);
         }
     }
 
-
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -2948,6 +3003,7 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, b
     node->data.error_value_decl.visib_mod = visib_mod;
     ast_buf_from_token(pc, name_tok, &node->data.error_value_decl.name);
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -3026,6 +3082,7 @@ static AstNode *ast_parse_root(ParseContext *pc, int *token_index) {
         ast_invalid_token_error(pc, &pc->tokens->at(*token_index));
     }
 
+    normalize_parent_ptrs(node);
     return node;
 }
 
@@ -3042,3 +3099,184 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner,
     pc.root = ast_parse_root(&pc, &token_index);
     return pc.root;
 }
+
+static void set_field(AstNode **field) {
+    if (*field) {
+        (*field)->parent_field = field;
+    }
+}
+
+static void set_list_fields(ZigList<AstNode*> *list) {
+    for (int i = 0; i < list->length; i += 1) {
+        set_field(&list->at(i));
+    }
+}
+
+void normalize_parent_ptrs(AstNode *node) {
+    switch (node->type) {
+        case NodeTypeRoot:
+            set_list_fields(&node->data.root.top_level_decls);
+            break;
+        case NodeTypeRootExportDecl:
+            set_list_fields(node->data.root_export_decl.directives);
+            break;
+        case NodeTypeFnProto:
+            set_field(&node->data.fn_proto.return_type);
+            set_list_fields(node->data.fn_proto.directives);
+            set_list_fields(&node->data.fn_proto.params);
+            break;
+        case NodeTypeFnDef:
+            set_field(&node->data.fn_def.fn_proto);
+            set_field(&node->data.fn_def.body);
+            break;
+        case NodeTypeFnDecl:
+            set_field(&node->data.fn_decl.fn_proto);
+            break;
+        case NodeTypeParamDecl:
+            set_field(&node->data.param_decl.type);
+            break;
+        case NodeTypeBlock:
+            set_list_fields(&node->data.block.statements);
+            break;
+        case NodeTypeExternBlock:
+            set_list_fields(node->data.extern_block.directives);
+            set_list_fields(&node->data.extern_block.fn_decls);
+            break;
+        case NodeTypeDirective:
+            // none
+            break;
+        case NodeTypeReturnExpr:
+            set_field(&node->data.return_expr.expr);
+            break;
+        case NodeTypeVariableDeclaration:
+            set_field(&node->data.variable_declaration.type);
+            set_field(&node->data.variable_declaration.expr);
+            break;
+        case NodeTypeErrorValueDecl:
+            // none
+            break;
+        case NodeTypeBinOpExpr:
+            set_field(&node->data.bin_op_expr.op1);
+            set_field(&node->data.bin_op_expr.op2);
+            break;
+        case NodeTypeNumberLiteral:
+            // none
+            break;
+        case NodeTypeStringLiteral:
+            // none
+            break;
+        case NodeTypeCharLiteral:
+            // none
+            break;
+        case NodeTypeErrorLiteral:
+            // none
+            break;
+        case NodeTypeSymbol:
+            // none
+            break;
+        case NodeTypePrefixOpExpr:
+            set_field(&node->data.prefix_op_expr.primary_expr);
+            break;
+        case NodeTypeFnCallExpr:
+            set_field(&node->data.fn_call_expr.fn_ref_expr);
+            set_list_fields(&node->data.fn_call_expr.params);
+            break;
+        case NodeTypeArrayAccessExpr:
+            set_field(&node->data.array_access_expr.array_ref_expr);
+            set_field(&node->data.array_access_expr.subscript);
+            break;
+        case NodeTypeSliceExpr:
+            set_field(&node->data.slice_expr.array_ref_expr);
+            set_field(&node->data.slice_expr.start);
+            set_field(&node->data.slice_expr.end);
+            break;
+        case NodeTypeFieldAccessExpr:
+            set_field(&node->data.field_access_expr.struct_expr);
+            break;
+        case NodeTypeUse:
+            set_list_fields(node->data.use.directives);
+            break;
+        case NodeTypeBoolLiteral:
+            // none
+            break;
+        case NodeTypeNullLiteral:
+            // none
+            break;
+        case NodeTypeIfBoolExpr:
+            set_field(&node->data.if_bool_expr.condition);
+            set_field(&node->data.if_bool_expr.then_block);
+            set_field(&node->data.if_bool_expr.else_node);
+            break;
+        case NodeTypeIfVarExpr:
+            set_field(&node->data.if_var_expr.var_decl.type);
+            set_field(&node->data.if_var_expr.var_decl.expr);
+            set_field(&node->data.if_var_expr.then_block);
+            set_field(&node->data.if_var_expr.else_node);
+            break;
+        case NodeTypeWhileExpr:
+            set_field(&node->data.while_expr.condition);
+            set_field(&node->data.while_expr.body);
+            break;
+        case NodeTypeForExpr:
+            set_field(&node->data.for_expr.elem_node);
+            set_field(&node->data.for_expr.array_expr);
+            set_field(&node->data.for_expr.index_node);
+            set_field(&node->data.for_expr.body);
+            break;
+        case NodeTypeSwitchExpr:
+            set_field(&node->data.switch_expr.expr);
+            set_list_fields(&node->data.switch_expr.prongs);
+            break;
+        case NodeTypeSwitchProng:
+            set_list_fields(&node->data.switch_prong.items);
+            set_field(&node->data.switch_prong.var_symbol);
+            set_field(&node->data.switch_prong.expr);
+            break;
+        case NodeTypeSwitchRange:
+            set_field(&node->data.switch_range.start);
+            set_field(&node->data.switch_range.end);
+            break;
+        case NodeTypeLabel:
+            // none
+            break;
+        case NodeTypeGoto:
+            // none
+            break;
+        case NodeTypeBreak:
+            // none
+            break;
+        case NodeTypeContinue:
+            // none
+            break;
+        case NodeTypeAsmExpr:
+            for (int i = 0; i < node->data.asm_expr.input_list.length; i += 1) {
+                AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
+                set_field(&asm_input->expr);
+            }
+            for (int i = 0; i < node->data.asm_expr.output_list.length; i += 1) {
+                AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
+                set_field(&asm_output->return_type);
+            }
+            break;
+        case NodeTypeStructDecl:
+            set_list_fields(&node->data.struct_decl.fields);
+            set_list_fields(&node->data.struct_decl.fns);
+            set_list_fields(node->data.struct_decl.directives);
+            break;
+        case NodeTypeStructField:
+            set_field(&node->data.struct_field.type);
+            set_list_fields(node->data.struct_field.directives);
+            break;
+        case NodeTypeContainerInitExpr:
+            set_field(&node->data.container_init_expr.type);
+            set_list_fields(&node->data.container_init_expr.entries);
+            break;
+        case NodeTypeStructValueField:
+            set_field(&node->data.struct_val_field.expr);
+            break;
+        case NodeTypeArrayType:
+            set_field(&node->data.array_type.size);
+            set_field(&node->data.array_type.child_type);
+            break;
+    }
+}
src/parser.hpp
@@ -24,4 +24,6 @@ const char *node_type_str(NodeType node_type);
 
 void ast_print(AstNode *node, int indent);
 
+void normalize_parent_ptrs(AstNode *node);
+
 #endif
test/run_tests.cpp
@@ -1317,7 +1317,7 @@ fn f() i32 => {
     const a = c"a";
     a
 }
-    )SOURCE", 1, ".tmp_source.zig:2:15: error: expected type 'i32', got '&const u8'");
+    )SOURCE", 1, ".tmp_source.zig:4:5: error: expected type 'i32', got '&const u8'");
 
     add_compile_fail_case("if condition is bool, not int", R"SOURCE(
 fn f() => {