Commit 05de70017d

Andrew Kelley <superjoe30@gmail.com>
2016-11-08 00:58:01
IR: support slice types
1 parent a2e3293
src/all_types.hpp
@@ -1447,6 +1447,8 @@ enum IrInstructionId {
     IrInstructionIdToPtrType,
     IrInstructionIdPtrTypeChild,
     IrInstructionIdSetFnTest,
+    IrInstructionIdArrayType,
+    IrInstructionIdSliceType,
 };
 
 struct IrInstruction {
@@ -1696,6 +1698,20 @@ struct IrInstructionSetFnTest {
     IrInstruction *is_test;
 };
 
+struct IrInstructionArrayType {
+    IrInstruction base;
+
+    IrInstruction *size;
+    IrInstruction *child_type;
+};
+
+struct IrInstructionSliceType {
+    IrInstruction base;
+
+    bool is_const;
+    IrInstruction *child_type;
+};
+
 enum LValPurpose {
     LValPurposeNone,
     LValPurposeAssign,
src/analyze.cpp
@@ -369,7 +369,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
     }
 }
 
-static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
+TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) {
     if (child_type->error_parent) {
         return child_type->error_parent;
     } else {
@@ -2582,7 +2582,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
             &fn_table_entry->analyzed_executable, expected_type, fn_proto->return_type);
     node->data.fn_def.implicit_return_type = block_return_type;
 
-    if (g->verbose) {
+    if (block_return_type->id != TypeTableEntryIdInvalid && g->verbose) {
         fprintf(stderr, "fn %s { // (analyzed)\n", buf_ptr(&fn_table_entry->symbol_name));
         ir_print(stderr, &fn_table_entry->analyzed_executable, 4);
         fprintf(stderr, "}\n");
src/analyze.hpp
@@ -31,6 +31,7 @@ TypeTableEntry *get_slice_type(CodeGen *g, TypeTableEntry *child_type, bool is_c
 TypeTableEntry *get_partial_container_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         ContainerKind kind, AstNode *decl_node, const char *name);
 TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x);
+TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type);
 bool handle_is_ptr(TypeTableEntry *type_entry);
 void find_libc_include_path(CodeGen *g);
 void find_libc_lib_path(CodeGen *g);
src/codegen.cpp
@@ -2966,6 +2966,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdPtrTypeChild:
         case IrInstructionIdFieldPtr:
         case IrInstructionIdSetFnTest:
+        case IrInstructionIdArrayType:
+        case IrInstructionIdSliceType:
             zig_unreachable();
         case IrInstructionIdReturn:
             return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
src/ir.cpp
@@ -189,6 +189,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnTest *) {
     return IrInstructionIdSetFnTest;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) {
+    return IrInstructionIdArrayType;
+}
+
+static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
+    return IrInstructionIdSliceType;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -716,19 +724,30 @@ static IrInstruction *ir_build_set_fn_test(IrBuilder *irb, AstNode *source_node,
     return &instruction->base;
 }
 
-//static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
-//    size_t result = 0;
-//    while (inner_block != outer_block) {
-//        if (inner_block->node->type == NodeTypeDefer &&
-//           (inner_block->node->data.defer.kind == ReturnKindError ||
-//            inner_block->node->data.defer.kind == ReturnKindMaybe))
-//        {
-//            result += 1;
-//        }
-//        inner_block = inner_block->parent;
-//    }
-//    return result;
-//}
+static IrInstruction *ir_build_array_type(IrBuilder *irb, AstNode *source_node, IrInstruction *size,
+        IrInstruction *child_type)
+{
+    IrInstructionArrayType *instruction = ir_build_instruction<IrInstructionArrayType>(irb, source_node);
+    instruction->size = size;
+    instruction->child_type = child_type;
+
+    ir_ref_instruction(size);
+    ir_ref_instruction(child_type);
+
+    return &instruction->base;
+}
+
+static IrInstruction *ir_build_slice_type(IrBuilder *irb, AstNode *source_node, bool is_const,
+        IrInstruction *child_type)
+{
+    IrInstructionSliceType *instruction = ir_build_instruction<IrInstructionSliceType>(irb, source_node);
+    instruction->is_const = is_const;
+    instruction->child_type = child_type;
+
+    ir_ref_instruction(child_type);
+
+    return &instruction->base;
+}
 
 static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block,
         bool gen_error_defers, bool gen_maybe_defers)
@@ -777,23 +796,6 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, AstNode *node) {
     zig_unreachable();
 }
 
-//static IrInstruction *ir_gen_return(IrBuilder *irb, AstNode *source_node, IrInstruction *value, ReturnKnowledge rk) {
-//    BlockContext *defer_inner_block = source_node->block_context;
-//    BlockContext *defer_outer_block = irb->node->block_context;
-//    if (rk == ReturnKnowledgeUnknown) {
-//        if (get_conditional_defer_count(defer_inner_block, defer_outer_block) > 0) {
-//            // generate branching code that checks the return value and generates defers
-//            // if the return value is error
-//            zig_panic("TODO");
-//        }
-//    } else if (rk != ReturnKnowledgeSkipDefers) {
-//        ir_gen_defers_for_block(irb, defer_inner_block, defer_outer_block,
-//                rk == ReturnKnowledgeKnownError, rk == ReturnKnowledgeKnownNull);
-//    }
-//
-//    return ir_build_return(irb, source_node, value);
-//}
-
 static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
     assert(basic_block);
 
@@ -1588,6 +1590,38 @@ static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, AstNode *node) {
     return ir_build_const_bool(irb, node, node->data.bool_literal.value);
 }
 
+static IrInstruction *ir_gen_array_type(IrBuilder *irb, AstNode *node) {
+    assert(node->type == NodeTypeArrayType);
+
+    AstNode *size_node = node->data.array_type.size;
+    AstNode *child_type_node = node->data.array_type.child_type;
+    bool is_const = node->data.array_type.is_const;
+
+    if (size_node) {
+        if (is_const) {
+            add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type"));
+            return irb->codegen->invalid_instruction;
+        }
+
+        IrInstruction *size_value = ir_gen_node(irb, size_node, node->block_context);
+        if (size_value == irb->codegen->invalid_instruction)
+            return size_value;
+
+        IrInstruction *child_type = ir_gen_node(irb, child_type_node, node->block_context);
+        if (child_type == irb->codegen->invalid_instruction)
+            return child_type;
+
+        return ir_build_array_type(irb, node, size_value, child_type);
+    } else {
+        IrInstruction *child_type = ir_gen_node_extra(irb, child_type_node,
+                node->block_context, LValPurposeAddressOf);
+        if (child_type == irb->codegen->invalid_instruction)
+            return child_type;
+
+        return ir_build_slice_type(irb, node, is_const, child_type);
+    }
+}
+
 static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context,
         LValPurpose lval)
 {
@@ -1627,6 +1661,8 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
             return ir_gen_this_literal(irb, node);
         case NodeTypeBoolLiteral:
             return ir_gen_bool_literal(irb, node);
+        case NodeTypeArrayType:
+            return ir_gen_array_type(irb, node);
         case NodeTypeUnwrapErrorExpr:
         case NodeTypeDefer:
         case NodeTypeSliceExpr:
@@ -1644,7 +1680,6 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont
         case NodeTypeZeroesLiteral:
         case NodeTypeErrorType:
         case NodeTypeTypeLiteral:
-        case NodeTypeArrayType:
         case NodeTypeVarLiteral:
         case NodeTypeRoot:
         case NodeTypeFnProto:
@@ -1703,51 +1738,6 @@ IrInstruction *ir_gen_fn(CodeGen *codegn, FnTableEntry *fn_entry) {
     return ir_gen(codegn, body_node, scope, ir_executable);
 }
 
-/*
-static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) {
-    assert(node->type == NodeTypeGoto);
-    Buf *label_name = node->data.goto_expr.name;
-    BlockContext *context = node->block_context;
-    assert(context);
-    LabelTableEntry *label = find_label(g, context, label_name);
-
-    if (!label) {
-        add_node_error(g, node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
-        return;
-    }
-
-    label->used = true;
-    node->data.goto_expr.label_entry = label;
-}
-
-    for (size_t i = 0; i < fn_table_entry->goto_list.length; i += 1) {
-        AstNode *goto_node = fn_table_entry->goto_list.at(i);
-        assert(goto_node->type == NodeTypeGoto);
-        analyze_goto_pass2(g, import, goto_node);
-    }
-
-    for (size_t i = 0; i < fn_table_entry->all_labels.length; i += 1) {
-        LabelTableEntry *label = fn_table_entry->all_labels.at(i);
-        if (!label->used) {
-            add_node_error(g, label->decl_node,
-                    buf_sprintf("label '%s' defined but not used",
-                        buf_ptr(label->decl_node->data.label.name)));
-        }
-    }
-*/
-
-//static LabelTableEntry *find_label(CodeGen *g, BlockContext *orig_context, Buf *name) {
-//    BlockContext *context = orig_context;
-//    while (context && context->fn_entry) {
-//        auto entry = context->label_table.maybe_get(name);
-//        if (entry) {
-//            return entry->value;
-//        }
-//        context = context->parent;
-//    }
-//    return nullptr;
-//}
-
 static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *other_type) {
     TypeTableEntry *other_type_underlying = get_underlying_type(other_type);
 
@@ -2970,6 +2960,59 @@ static TypeTableEntry *ir_analyze_unary_bool_not(IrAnalyze *ira, IrInstructionUn
     return bool_type;
 }
 
+static TypeTableEntry *ir_analyze_unary_prefix_op_err(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
+    assert(un_op_instruction->op_id == IrUnOpError);
+    IrInstruction *value = un_op_instruction->value->other;
+
+    TypeTableEntry *type_entry = value->type_entry;
+    if (type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    TypeTableEntry *meta_type = ir_resolve_type(ira, value);
+    TypeTableEntry *underlying_meta_type = get_underlying_type(meta_type);
+    switch (underlying_meta_type->id) {
+        case TypeTableEntryIdTypeDecl:
+            zig_unreachable();
+        case TypeTableEntryIdInvalid:
+            return ira->codegen->builtin_types.entry_invalid;
+        case TypeTableEntryIdVoid:
+        case TypeTableEntryIdBool:
+        case TypeTableEntryIdInt:
+        case TypeTableEntryIdFloat:
+        case TypeTableEntryIdPointer:
+        case TypeTableEntryIdArray:
+        case TypeTableEntryIdStruct:
+        case TypeTableEntryIdMaybe:
+        case TypeTableEntryIdErrorUnion:
+        case TypeTableEntryIdPureError:
+        case TypeTableEntryIdEnum:
+        case TypeTableEntryIdUnion:
+        case TypeTableEntryIdFn:
+        case TypeTableEntryIdGenericFn:
+            {
+                ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base,
+                        value->static_value.depends_on_compile_var);
+                TypeTableEntry *result_type = get_error_type(ira->codegen, meta_type);
+                out_val->data.x_type = result_type;
+                return ira->codegen->builtin_types.entry_type;
+            }
+        case TypeTableEntryIdMetaType:
+        case TypeTableEntryIdNumLitFloat:
+        case TypeTableEntryIdNumLitInt:
+        case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
+        case TypeTableEntryIdNamespace:
+        case TypeTableEntryIdBlock:
+        case TypeTableEntryIdUnreachable:
+        case TypeTableEntryIdVar:
+            add_node_error(ira->codegen, un_op_instruction->base.source_node,
+                    buf_sprintf("unable to wrap type '%s' in error type", buf_ptr(&meta_type->name)));
+            // TODO if meta_type is type decl, add note pointing to type decl declaration
+            return ira->codegen->builtin_types.entry_invalid;
+    }
+    zig_unreachable();
+}
+
 static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
     IrUnOp op_id = un_op_instruction->op_id;
     switch (op_id) {
@@ -2977,7 +3020,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
             zig_unreachable();
         case IrUnOpBoolNot:
             return ir_analyze_unary_bool_not(ira, un_op_instruction);
-            zig_panic("TODO analyze PrefixOpBoolNot");
         case IrUnOpBinNot:
             zig_panic("TODO analyze PrefixOpBinNot");
             //{
@@ -3106,31 +3148,7 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
             //    }
             //}
         case IrUnOpError:
-            zig_panic("TODO analyze PrefixOpError");
-            //{
-            //    TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, *expr_node);
-
-            //    if (type_entry->id == TypeTableEntryIdInvalid) {
-            //        return type_entry;
-            //    } else if (type_entry->id == TypeTableEntryIdMetaType) {
-            //        TypeTableEntry *meta_type = resolve_type(g, *expr_node);
-            //        if (meta_type->id == TypeTableEntryIdInvalid) {
-            //            return meta_type;
-            //        } else if (meta_type->id == TypeTableEntryIdUnreachable) {
-            //            add_node_error(g, node, buf_create_from_str("unable to wrap unreachable in error type"));
-            //            return g->builtin_types.entry_invalid;
-            //        } else {
-            //            return resolve_expr_const_val_as_type(g, node, get_error_type(g, meta_type), false);
-            //        }
-            //    } else if (type_entry->id == TypeTableEntryIdUnreachable) {
-            //        add_node_error(g, *expr_node, buf_sprintf("unable to wrap unreachable in error type"));
-            //        return g->builtin_types.entry_invalid;
-            //    } else {
-            //        // TODO eval const expr
-            //        return get_error_type(g, type_entry);
-            //    }
-
-            //}
+            return ir_analyze_unary_prefix_op_err(ira, un_op_instruction);
         case IrUnOpUnwrapError:
             zig_panic("TODO analyze PrefixOpUnwrapError");
             //{
@@ -3691,6 +3709,59 @@ static TypeTableEntry *ir_analyze_instruction_set_fn_test(IrAnalyze *ira,
     return ira->codegen->builtin_types.entry_void;
 }
 
+static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
+        IrInstructionSliceType *slice_type_instruction)
+{
+    IrInstruction *child_type = slice_type_instruction->child_type->other;
+    if (child_type->type_entry->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+    bool is_const = slice_type_instruction->is_const;
+
+    TypeTableEntry *resolved_child_type = ir_resolve_type(ira, child_type);
+    TypeTableEntry *canon_child_type = get_underlying_type(resolved_child_type);
+    switch (canon_child_type->id) {
+        case TypeTableEntryIdTypeDecl:
+            zig_unreachable();
+        case TypeTableEntryIdInvalid:
+            return ira->codegen->builtin_types.entry_invalid;
+        case TypeTableEntryIdVar:
+        case TypeTableEntryIdUnreachable:
+        case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
+        case TypeTableEntryIdBlock:
+            add_node_error(ira->codegen, slice_type_instruction->base.source_node,
+                    buf_sprintf("slice of type '%s' not allowed", buf_ptr(&resolved_child_type->name)));
+            // TODO if this is a typedecl, add error note showing the declaration of the type decl
+            return ira->codegen->builtin_types.entry_invalid;
+        case TypeTableEntryIdMetaType:
+        case TypeTableEntryIdVoid:
+        case TypeTableEntryIdBool:
+        case TypeTableEntryIdInt:
+        case TypeTableEntryIdFloat:
+        case TypeTableEntryIdPointer:
+        case TypeTableEntryIdArray:
+        case TypeTableEntryIdStruct:
+        case TypeTableEntryIdNumLitFloat:
+        case TypeTableEntryIdNumLitInt:
+        case TypeTableEntryIdMaybe:
+        case TypeTableEntryIdErrorUnion:
+        case TypeTableEntryIdPureError:
+        case TypeTableEntryIdEnum:
+        case TypeTableEntryIdUnion:
+        case TypeTableEntryIdFn:
+        case TypeTableEntryIdNamespace:
+        case TypeTableEntryIdGenericFn:
+            {
+                TypeTableEntry *result_type = get_slice_type(ira->codegen, resolved_child_type, is_const);
+                ConstExprValue *out_val = ir_build_const_from(ira, &slice_type_instruction->base,
+                        child_type->static_value.depends_on_compile_var);
+                out_val->data.x_type = result_type;
+                return ira->codegen->builtin_types.entry_type;
+            }
+    }
+    zig_unreachable();
+}
+
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -3735,11 +3806,14 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction);
         case IrInstructionIdSetFnTest:
             return ir_analyze_instruction_set_fn_test(ira, (IrInstructionSetFnTest *)instruction);
+        case IrInstructionIdSliceType:
+            return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
         case IrInstructionIdSwitchBr:
         case IrInstructionIdCast:
         case IrInstructionIdContainerInitList:
         case IrInstructionIdContainerInitFields:
         case IrInstructionIdStructFieldPtr:
+        case IrInstructionIdArrayType:
             zig_panic("TODO analyze more instructions");
     }
     zig_unreachable();
@@ -3836,6 +3910,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdPtrTypeChild:
         case IrInstructionIdReadField:
         case IrInstructionIdStructFieldPtr:
+        case IrInstructionIdArrayType:
+        case IrInstructionIdSliceType:
             return false;
     }
     zig_unreachable();
@@ -6782,53 +6858,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //            false, nullptr, false);
 //}
 //
-//static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *node)
-//{
-//    AstNode *size_node = node->data.array_type.size;
-//
-//    TypeTableEntry *child_type = analyze_type_expr_pointer_only(g, import, context,
-//            node->data.array_type.child_type, true);
-//
-//    if (child_type->id == TypeTableEntryIdUnreachable) {
-//        add_node_error(g, node, buf_create_from_str("array of unreachable not allowed"));
-//        return g->builtin_types.entry_invalid;
-//    } else if (child_type->id == TypeTableEntryIdInvalid) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    if (size_node) {
-//        child_type = analyze_type_expr(g, import, context, node->data.array_type.child_type);
-//        TypeTableEntry *size_type = analyze_expression(g, import, context,
-//                g->builtin_types.entry_usize, size_node);
-//        if (size_type->id == TypeTableEntryIdInvalid) {
-//            return g->builtin_types.entry_invalid;
-//        }
-//
-//        ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
-//        if (const_val->ok) {
-//            if (const_val->data.x_bignum.is_negative) {
-//                add_node_error(g, size_node,
-//                    buf_sprintf("array size %s is negative",
-//                        buf_ptr(bignum_to_buf(&const_val->data.x_bignum))));
-//                return g->builtin_types.entry_invalid;
-//            } else {
-//                return resolve_expr_const_val_as_type(g, node,
-//                        get_array_type(g, child_type, const_val->data.x_bignum.data.x_uint), false);
-//            }
-//        } else if (context->fn_entry) {
-//            return resolve_expr_const_val_as_type(g, node,
-//                    get_slice_type(g, child_type, node->data.array_type.is_const), false);
-//        } else {
-//            add_node_error(g, first_executing_node(size_node),
-//                    buf_sprintf("unable to evaluate constant expression"));
-//            return g->builtin_types.entry_invalid;
-//        }
-//    } else {
-//        TypeTableEntry *slice_type = get_slice_type(g, child_type, node->data.array_type.is_const);
-//        return resolve_expr_const_val_as_type(g, node, slice_type, false);
-//    }
-//}
 //
 //static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
 //        TypeTableEntry *expected_type, AstNode *node)
@@ -7218,3 +7247,126 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    }
 //}
 //
+//static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+//        TypeTableEntry *expected_type, AstNode *node)
+//{
+//    AstNode *size_node = node->data.array_type.size;
+//
+//    TypeTableEntry *child_type = analyze_type_expr_pointer_only(g, import, context,
+//            node->data.array_type.child_type, true);
+//
+//    if (child_type->id == TypeTableEntryIdUnreachable) {
+//        add_node_error(g, node, buf_create_from_str("array of unreachable not allowed"));
+//        return g->builtin_types.entry_invalid;
+//    } else if (child_type->id == TypeTableEntryIdInvalid) {
+//        return g->builtin_types.entry_invalid;
+//    }
+//
+//    if (size_node) {
+//        child_type = analyze_type_expr(g, import, context, node->data.array_type.child_type);
+//        TypeTableEntry *size_type = analyze_expression(g, import, context,
+//                g->builtin_types.entry_usize, size_node);
+//        if (size_type->id == TypeTableEntryIdInvalid) {
+//            return g->builtin_types.entry_invalid;
+//        }
+//
+//        ConstExprValue *const_val = &get_resolved_expr(size_node)->const_val;
+//        if (const_val->ok) {
+//            if (const_val->data.x_bignum.is_negative) {
+//                add_node_error(g, size_node,
+//                    buf_sprintf("array size %s is negative",
+//                        buf_ptr(bignum_to_buf(&const_val->data.x_bignum))));
+//                return g->builtin_types.entry_invalid;
+//            } else {
+//                return resolve_expr_const_val_as_type(g, node,
+//                        get_array_type(g, child_type, const_val->data.x_bignum.data.x_uint), false);
+//            }
+//        } else if (context->fn_entry) {
+//            return resolve_expr_const_val_as_type(g, node,
+//                    get_slice_type(g, child_type, node->data.array_type.is_const), false);
+//        } else {
+//            add_node_error(g, first_executing_node(size_node),
+//                    buf_sprintf("unable to evaluate constant expression"));
+//            return g->builtin_types.entry_invalid;
+//        }
+//    } else {
+//        TypeTableEntry *slice_type = get_slice_type(g, child_type, node->data.array_type.is_const);
+//        return resolve_expr_const_val_as_type(g, node, slice_type, false);
+//    }
+//}
+//static size_t get_conditional_defer_count(BlockContext *inner_block, BlockContext *outer_block) {
+//    size_t result = 0;
+//    while (inner_block != outer_block) {
+//        if (inner_block->node->type == NodeTypeDefer &&
+//           (inner_block->node->data.defer.kind == ReturnKindError ||
+//            inner_block->node->data.defer.kind == ReturnKindMaybe))
+//        {
+//            result += 1;
+//        }
+//        inner_block = inner_block->parent;
+//    }
+//    return result;
+//}
+
+
+//static IrInstruction *ir_gen_return(IrBuilder *irb, AstNode *source_node, IrInstruction *value, ReturnKnowledge rk) {
+//    BlockContext *defer_inner_block = source_node->block_context;
+//    BlockContext *defer_outer_block = irb->node->block_context;
+//    if (rk == ReturnKnowledgeUnknown) {
+//        if (get_conditional_defer_count(defer_inner_block, defer_outer_block) > 0) {
+//            // generate branching code that checks the return value and generates defers
+//            // if the return value is error
+//            zig_panic("TODO");
+//        }
+//    } else if (rk != ReturnKnowledgeSkipDefers) {
+//        ir_gen_defers_for_block(irb, defer_inner_block, defer_outer_block,
+//                rk == ReturnKnowledgeKnownError, rk == ReturnKnowledgeKnownNull);
+//    }
+//
+//    return ir_build_return(irb, source_node, value);
+//}
+/*
+static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) {
+    assert(node->type == NodeTypeGoto);
+    Buf *label_name = node->data.goto_expr.name;
+    BlockContext *context = node->block_context;
+    assert(context);
+    LabelTableEntry *label = find_label(g, context, label_name);
+
+    if (!label) {
+        add_node_error(g, node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
+        return;
+    }
+
+    label->used = true;
+    node->data.goto_expr.label_entry = label;
+}
+
+    for (size_t i = 0; i < fn_table_entry->goto_list.length; i += 1) {
+        AstNode *goto_node = fn_table_entry->goto_list.at(i);
+        assert(goto_node->type == NodeTypeGoto);
+        analyze_goto_pass2(g, import, goto_node);
+    }
+
+    for (size_t i = 0; i < fn_table_entry->all_labels.length; i += 1) {
+        LabelTableEntry *label = fn_table_entry->all_labels.at(i);
+        if (!label->used) {
+            add_node_error(g, label->decl_node,
+                    buf_sprintf("label '%s' defined but not used",
+                        buf_ptr(label->decl_node->data.label.name)));
+        }
+    }
+*/
+
+//static LabelTableEntry *find_label(CodeGen *g, BlockContext *orig_context, Buf *name) {
+//    BlockContext *context = orig_context;
+//    while (context && context->fn_entry) {
+//        auto entry = context->label_table.maybe_get(name);
+//        if (entry) {
+//            return entry->value;
+//        }
+//        context = context->parent;
+//    }
+//    return nullptr;
+//}
+
src/ir_print.cpp
@@ -394,6 +394,19 @@ static void ir_print_set_fn_test(IrPrint *irp, IrInstructionSetFnTest *instructi
     fprintf(irp->f, ")");
 }
 
+static void ir_print_array_type(IrPrint *irp, IrInstructionArrayType *instruction) {
+    fprintf(irp->f, "[");
+    ir_print_other_instruction(irp, instruction->size);
+    fprintf(irp->f, "]");
+    ir_print_other_instruction(irp, instruction->child_type);
+}
+
+static void ir_print_slice_type(IrPrint *irp, IrInstructionSliceType *instruction) {
+    const char *const_kw = instruction->is_const ? "const " : "";
+    fprintf(irp->f, "[]%s", const_kw);
+    ir_print_other_instruction(irp, instruction->child_type);
+}
+
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     ir_print_prefix(irp, instruction);
     switch (instruction->id) {
@@ -471,6 +484,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdSetFnTest:
             ir_print_set_fn_test(irp, (IrInstructionSetFnTest *)instruction);
             break;
+        case IrInstructionIdArrayType:
+            ir_print_array_type(irp, (IrInstructionArrayType *)instruction);
+            break;
+        case IrInstructionIdSliceType:
+            ir_print_slice_type(irp, (IrInstructionSliceType *)instruction);
+            break;
         case IrInstructionIdSwitchBr:
             zig_panic("TODO print more IR instructions");
     }