From 1a0111d4c36578b64d8532724afd9be9ca61d3d1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 30 Oct 2016 02:46:16 -0400 Subject: [PATCH] *WIP* --- src/all_types.hpp | 40 ++- src/analyze.cpp | 79 +----- src/analyze.hpp | 9 +- src/codegen.cpp | 108 ++----- src/ir.cpp | 704 ++++++++++++++++++++++++++++++++++++++++------ src/ir_print.cpp | 66 ++++- 6 files changed, 762 insertions(+), 244 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 1ec123bc74..e655882cbe 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1330,6 +1330,7 @@ struct CodeGen { LLVMValueRef err_name_table; IrInstruction *invalid_instruction; + Buf *len_buf; }; struct VariableTableEntry { @@ -1428,6 +1429,8 @@ enum IrInstructionId { IrInstructionIdLoadPtr, IrInstructionIdStorePtr, IrInstructionIdFieldPtr, + IrInstructionIdStructFieldPtr, + IrInstructionIdReadField, IrInstructionIdElemPtr, IrInstructionIdVarPtr, IrInstructionIdCall, @@ -1438,6 +1441,9 @@ enum IrInstructionId { IrInstructionIdContainerInitList, IrInstructionIdContainerInitFields, IrInstructionIdUnreachable, + IrInstructionIdTypeOf, + IrInstructionIdToPtrType, + IrInstructionIdPtrTypeChild, }; struct IrInstruction { @@ -1573,8 +1579,22 @@ struct IrInstructionStorePtr { struct IrInstructionFieldPtr { IrInstruction base; + IrInstruction *container_ptr; + Buf *field_name; +}; + +struct IrInstructionStructFieldPtr { + IrInstruction base; + IrInstruction *struct_ptr; - Buf field_name; + TypeStructField *field; +}; + +struct IrInstructionReadField { + IrInstruction base; + + IrInstruction *container_ptr; + Buf *field_name; }; struct IrInstructionElemPtr { @@ -1648,6 +1668,24 @@ struct IrInstructionUnreachable { IrInstruction base; }; +struct IrInstructionTypeOf { + IrInstruction base; + + IrInstruction *value; +}; + +struct IrInstructionToPtrType { + IrInstruction base; + + IrInstruction *value; +}; + +struct IrInstructionPtrTypeChild { + IrInstruction base; + + IrInstruction *value; +}; + enum LValPurpose { LValPurposeNone, LValPurposeAssign, diff --git a/src/analyze.cpp b/src/analyze.cpp index d67937a7a5..0ecd4065f0 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -192,7 +192,7 @@ static BlockContext **get_container_block_context_ptr(TypeTableEntry *type_entry zig_unreachable(); } -static BlockContext *get_container_block_context(TypeTableEntry *type_entry) { +BlockContext *get_container_block_context(TypeTableEntry *type_entry) { return *get_container_block_context_ptr(type_entry); } @@ -217,7 +217,7 @@ static size_t bits_needed_for_unsigned(uint64_t x) { } } -static bool type_is_complete(TypeTableEntry *type_entry) { +bool type_is_complete(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: case TypeTableEntryIdVar: @@ -2422,7 +2422,7 @@ VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, Buf *n return nullptr; } -static TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name) { +TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name) { for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[i]; if (buf_eql_buf(type_enum_field->name, name)) { @@ -2493,7 +2493,7 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp return enum_type; } -static TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { +TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) { assert(type_entry->id == TypeTableEntryIdStruct); assert(type_entry->data.structure.complete); for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { @@ -2539,18 +2539,18 @@ static bool is_container(TypeTableEntry *type_entry) { zig_unreachable(); } -static bool is_container_ref(TypeTableEntry *type_entry) { +bool is_container_ref(TypeTableEntry *type_entry) { return (type_entry->id == TypeTableEntryIdPointer) ? is_container(type_entry->data.pointer.child_type) : is_container(type_entry); } -static TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) { +TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) { assert(is_container_ref(type_entry)); return (type_entry->id == TypeTableEntryIdPointer) ? type_entry->data.pointer.child_type : type_entry; } -static void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { +void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdStruct: resolve_struct_type(g, type_entry->data.structure.decl_node->owner, type_entry); @@ -4034,69 +4034,6 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, return expr_return_type; } -static TypeTableEntry *analyze_for_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, - TypeTableEntry *expected_type, AstNode *node) -{ - assert(node->type == NodeTypeForExpr); - - AstNode *array_node = node->data.for_expr.array_expr; - TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, array_node); - TypeTableEntry *child_type; - if (array_type->id == TypeTableEntryIdInvalid) { - child_type = array_type; - } else if (array_type->id == TypeTableEntryIdArray) { - child_type = array_type->data.array.child_type; - } else if (array_type->id == TypeTableEntryIdStruct && - array_type->data.structure.is_slice) - { - TypeTableEntry *pointer_type = array_type->data.structure.fields[0].type_entry; - assert(pointer_type->id == TypeTableEntryIdPointer); - child_type = pointer_type->data.pointer.child_type; - } else { - add_node_error(g, node, - buf_sprintf("iteration over non array type '%s'", buf_ptr(&array_type->name))); - child_type = g->builtin_types.entry_invalid; - } - - TypeTableEntry *var_type; - if (child_type->id != TypeTableEntryIdInvalid && node->data.for_expr.elem_is_ptr) { - var_type = get_pointer_to_type(g, child_type, false); - } else { - var_type = child_type; - } - - BlockContext *child_context = new_block_context(node, context); - child_context->parent_loop_node = node; - - AstNode *elem_var_node = node->data.for_expr.elem_node; - if (!elem_var_node) { - add_node_error(g, node->data.for_expr.body, - buf_sprintf("for loop expression missing element parameter")); - return g->builtin_types.entry_invalid; - } - - elem_var_node->block_context = child_context; - Buf *elem_var_name = elem_var_node->data.symbol_expr.symbol; - node->data.for_expr.elem_var = add_local_var(g, elem_var_node, import, child_context, elem_var_name, - var_type, true, nullptr); - - AstNode *index_var_node = node->data.for_expr.index_node; - if (index_var_node) { - Buf *index_var_name = index_var_node->data.symbol_expr.symbol; - index_var_node->block_context = child_context; - node->data.for_expr.index_var = add_local_var(g, index_var_node, import, child_context, index_var_name, - g->builtin_types.entry_usize, true, nullptr); - } else { - node->data.for_expr.index_var = add_local_var(g, node, import, child_context, nullptr, - g->builtin_types.entry_usize, true, nullptr); - } - - analyze_expression(g, import, child_context, g->builtin_types.entry_void, node->data.for_expr.body); - - - return g->builtin_types.entry_void; -} - static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -5294,7 +5231,7 @@ static TypeTableEntry *analyze_expression_pointer_only(CodeGen *g, ImportTableEn return_type = analyze_while_expr(g, import, context, expected_type, node); break; case NodeTypeForExpr: - return_type = analyze_for_expr(g, import, context, expected_type, node); + zig_panic("moved to ir.cpp"); break; case NodeTypeArrayType: return_type = analyze_array_type(g, import, context, expected_type, node); diff --git a/src/analyze.hpp b/src/analyze.hpp index f73981b68d..605fe0ae6d 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -50,7 +50,7 @@ TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, ImportTableEntry *im AstNode **child_nodes, TypeTableEntry **child_types, size_t child_count); - +// TODO move these over, these used to be static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type); VariableTableEntry *find_variable(CodeGen *g, BlockContext *orig_context, Buf *name); AstNode *find_decl(BlockContext *context, Buf *name); @@ -59,5 +59,12 @@ TopLevelDecl *get_as_top_level_decl(AstNode *node); void mark_impure_fn(CodeGen *g, BlockContext *context, AstNode *node); bool type_is_codegen_pointer(TypeTableEntry *type); TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry); +TypeTableEntry *container_ref_type(TypeTableEntry *type_entry); +bool type_is_complete(TypeTableEntry *type_entry); +void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry); +TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name); +BlockContext *get_container_block_context(TypeTableEntry *type_entry); +TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name); +bool is_container_ref(TypeTableEntry *type_entry); #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 908c1986b4..4b98153e94 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -66,6 +66,8 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) { g->is_test_build = false; g->want_h_file = true; + g->len_buf = buf_create_from_str("len"); + // the error.Ok value g->error_decls.append(nullptr); @@ -2940,12 +2942,29 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } } +static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executable, + IrInstructionStructFieldPtr *instruction) +{ + LLVMValueRef struct_ptr = ir_llvm_value(g, instruction->struct_ptr); + TypeStructField *field = instruction->field; + + if (!type_has_bits(field->type_entry)) + return nullptr; + + assert(field->gen_index != SIZE_MAX); + return LLVMBuildStructGEP(g->builder, struct_ptr, field->gen_index, ""); +} + static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) { set_debug_source_node(g, instruction->source_node); switch (instruction->id) { case IrInstructionIdInvalid: case IrInstructionIdConst: + case IrInstructionIdTypeOf: + case IrInstructionIdToPtrType: + case IrInstructionIdPtrTypeChild: + case IrInstructionIdFieldPtr: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -2973,12 +2992,14 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_elem_ptr(g, executable, (IrInstructionElemPtr *)instruction); case IrInstructionIdCall: return ir_render_call(g, executable, (IrInstructionCall *)instruction); + case IrInstructionIdStructFieldPtr: + return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction); case IrInstructionIdSwitchBr: case IrInstructionIdPhi: case IrInstructionIdBuiltinCall: case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitFields: - case IrInstructionIdFieldPtr: + case IrInstructionIdReadField: zig_panic("TODO render more IR instructions to LLVM"); } zig_unreachable(); @@ -3284,89 +3305,6 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { return nullptr; } -static LLVMValueRef gen_for_expr(CodeGen *g, AstNode *node) { - assert(node->type == NodeTypeForExpr); - assert(node->data.for_expr.array_expr); - assert(node->data.for_expr.body); - - zig_panic("TODO IR for loop"); - //VariableTableEntry *elem_var = node->data.for_expr.elem_var; - //assert(elem_var); - - //TypeTableEntry *array_type = get_expr_type(node->data.for_expr.array_expr); - - //VariableTableEntry *index_var = node->data.for_expr.index_var; - //assert(index_var); - //LLVMValueRef index_ptr = index_var->value_ref; - //LLVMValueRef one_const = LLVMConstInt(g->builtin_types.entry_usize->type_ref, 1, false); - - //LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForCond"); - //LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForBody"); - //LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForEnd"); - //LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForContinue"); - - //LLVMValueRef array_val = gen_array_base_ptr(g, node->data.for_expr.array_expr); - //set_debug_source_node(g, node); - //LLVMBuildStore(g->builder, LLVMConstNull(index_var->type->type_ref), index_ptr); - - //gen_var_debug_decl(g, index_var); - - //LLVMValueRef len_val; - //TypeTableEntry *child_type; - //if (array_type->id == TypeTableEntryIdArray) { - // len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, - // array_type->data.array.len, false); - // child_type = array_type->data.array.child_type; - //} else if (array_type->id == TypeTableEntryIdStruct) { - // assert(array_type->data.structure.is_slice); - // TypeTableEntry *child_ptr_type = array_type->data.structure.fields[0].type_entry; - // assert(child_ptr_type->id == TypeTableEntryIdPointer); - // child_type = child_ptr_type->data.pointer.child_type; - // size_t len_index = array_type->data.structure.fields[1].gen_index; - // assert(len_index != SIZE_MAX); - // LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, array_val, len_index, ""); - // len_val = LLVMBuildLoad(g->builder, len_field_ptr, ""); - //} else { - // zig_unreachable(); - //} - //LLVMBuildBr(g->builder, cond_block); - - //LLVMPositionBuilderAtEnd(g->builder, cond_block); - //LLVMValueRef index_val = LLVMBuildLoad(g->builder, index_ptr, ""); - //LLVMValueRef cond = LLVMBuildICmp(g->builder, LLVMIntSLT, index_val, len_val, ""); - //LLVMBuildCondBr(g->builder, cond, body_block, end_block); - - //LLVMPositionBuilderAtEnd(g->builder, body_block); - //LLVMValueRef elem_ptr = gen_array_elem_ptr(g, node, array_val, array_type, index_val); - - //LLVMValueRef elem_val; - //if (node->data.for_expr.elem_is_ptr) { - // elem_val = elem_ptr; - //} else { - // elem_val = handle_is_ptr(child_type) ? elem_ptr : LLVMBuildLoad(g->builder, elem_ptr, ""); - //} - //gen_assign_raw(g, node, BinOpTypeAssign, elem_var->value_ref, elem_val, elem_var->type, child_type); - //gen_var_debug_decl(g, elem_var); - //g->break_block_stack.append(end_block); - //g->continue_block_stack.append(continue_block); - //gen_expr(g, node->data.for_expr.body); - //g->break_block_stack.pop(); - //g->continue_block_stack.pop(); - //if (get_expr_type(node->data.for_expr.body)->id != TypeTableEntryIdUnreachable) { - // set_debug_source_node(g, node); - // LLVMBuildBr(g->builder, continue_block); - //} - - //LLVMPositionBuilderAtEnd(g->builder, continue_block); - //set_debug_source_node(g, node); - //LLVMValueRef new_index_val = LLVMBuildNSWAdd(g->builder, index_val, one_const, ""); - //LLVMBuildStore(g->builder, new_index_val, index_ptr); - //LLVMBuildBr(g->builder, cond_block); - - //LLVMPositionBuilderAtEnd(g->builder, end_block); - //return nullptr; -} - //static LLVMValueRef gen_break(CodeGen *g, AstNode *node) { // assert(node->type == NodeTypeBreak); // LLVMBasicBlockRef dest_block = g->break_block_stack.last(); @@ -3773,7 +3711,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeWhileExpr: return gen_while_expr(g, node); case NodeTypeForExpr: - return gen_for_expr(g, node); + zig_panic("moved to ir render"); case NodeTypeAsmExpr: return gen_asm_expr(g, node); case NodeTypeSymbol: diff --git a/src/ir.cpp b/src/ir.cpp index e8d491e573..0f97073824 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2,6 +2,7 @@ #include "error.hpp" #include "eval.hpp" #include "ir.hpp" +#include "ir_print.hpp" struct IrExecContext { ConstExprValue *mem_slot_list; @@ -32,6 +33,7 @@ struct IrAnalyze { static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, BlockContext *scope); static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context, LValPurpose lval); +static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction); static void ir_instruction_append(IrBasicBlock *basic_block, IrInstruction *instruction) { assert(basic_block); @@ -127,6 +129,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldPtr *) { return IrInstructionIdFieldPtr; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionStructFieldPtr *) { + return IrInstructionIdStructFieldPtr; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionReadField *) { + return IrInstructionIdReadField; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) { return IrInstructionIdElemPtr; } @@ -167,6 +177,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionUnreachable *) { return IrInstructionIdUnreachable; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeOf *) { + return IrInstructionIdTypeOf; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionToPtrType *) { + return IrInstructionIdToPtrType; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionPtrTypeChild *) { + return IrInstructionIdPtrTypeChild; +} + template static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) { T *special_instruction = allocate(1); @@ -256,6 +278,14 @@ static IrInstruction *ir_build_const_void(IrBuilder *irb, AstNode *source_node) return &const_instruction->base; } +static IrInstruction *ir_build_const_undefined(IrBuilder *irb, AstNode *source_node) { + IrInstructionConst *const_instruction = ir_build_instruction(irb, source_node); + const_instruction->base.static_value.ok = true; + const_instruction->base.static_value.special = ConstValSpecialUndef; + const_instruction->base.type_entry = irb->codegen->builtin_types.entry_undef; + return &const_instruction->base; +} + static IrInstruction *ir_build_const_bignum(IrBuilder *irb, AstNode *source_node, BigNum *bignum) { IrInstructionConst *const_instruction = ir_build_instruction(irb, source_node); const_instruction->base.type_entry = (bignum->kind == BigNumKindInt) ? @@ -265,6 +295,14 @@ static IrInstruction *ir_build_const_bignum(IrBuilder *irb, AstNode *source_node return &const_instruction->base; } +static IrInstruction *ir_build_const_usize(IrBuilder *irb, AstNode *source_node, uint64_t value) { + IrInstructionConst *const_instruction = ir_build_instruction(irb, source_node); + const_instruction->base.type_entry = irb->codegen->builtin_types.entry_usize; + const_instruction->base.static_value.ok = true; + bignum_init_unsigned(&const_instruction->base.static_value.data.x_bignum, value); + return &const_instruction->base; +} + static IrInstruction *ir_create_const_type(IrBuilder *irb, AstNode *source_node, TypeTableEntry *type_entry) { IrInstructionConst *const_instruction = ir_create_instruction(irb->exec, source_node); const_instruction->base.type_entry = irb->codegen->builtin_types.entry_type; @@ -356,6 +394,67 @@ static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_ return new_instruction; } +static IrInstruction *ir_build_field_ptr(IrBuilder *irb, AstNode *source_node, + IrInstruction *container_ptr, Buf *field_name) +{ + IrInstructionFieldPtr *instruction = ir_build_instruction(irb, source_node); + instruction->container_ptr = container_ptr; + instruction->field_name = field_name; + + ir_ref_instruction(container_ptr); + + return &instruction->base; +} + +//static IrInstruction *ir_build_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, +// IrInstruction *container_ptr, Buf *field_name) +//{ +// IrInstruction *new_instruction = ir_build_field_ptr(irb, old_instruction->source_node, container_ptr, field_name); +// ir_link_new_instruction(new_instruction, old_instruction); +// return new_instruction; +//} + +static IrInstruction *ir_build_read_field(IrBuilder *irb, AstNode *source_node, + IrInstruction *container_ptr, Buf *field_name) +{ + IrInstructionReadField *instruction = ir_build_instruction(irb, source_node); + instruction->container_ptr = container_ptr; + instruction->field_name = field_name; + + ir_ref_instruction(container_ptr); + + return &instruction->base; +} + +//static IrInstruction *ir_build_read_field_from(IrBuilder *irb, IrInstruction *old_instruction, +// IrInstruction *container_ptr, Buf *field_name) +//{ +// IrInstruction *new_instruction = ir_build_read_field(irb, old_instruction->source_node, container_ptr, field_name); +// ir_link_new_instruction(new_instruction, old_instruction); +// return new_instruction; +//} + +static IrInstruction *ir_build_struct_field_ptr(IrBuilder *irb, AstNode *source_node, + IrInstruction *struct_ptr, TypeStructField *field) +{ + IrInstructionStructFieldPtr *instruction = ir_build_instruction(irb, source_node); + instruction->struct_ptr = struct_ptr; + instruction->field = field; + + ir_ref_instruction(struct_ptr); + + return &instruction->base; +} + +static IrInstruction *ir_build_struct_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction, + IrInstruction *struct_ptr, TypeStructField *type_struct_field) +{ + IrInstruction *new_instruction = ir_build_struct_field_ptr(irb, old_instruction->source_node, + struct_ptr, type_struct_field); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static IrInstruction *ir_build_call(IrBuilder *irb, AstNode *source_node, IrInstruction *fn, size_t arg_count, IrInstruction **args) { @@ -564,6 +663,33 @@ static IrInstruction *ir_build_load_ptr_from(IrBuilder *irb, IrInstruction *old_ return new_instruction; } +static IrInstruction *ir_build_typeof(IrBuilder *irb, AstNode *source_node, IrInstruction *value) { + IrInstructionTypeOf *instruction = ir_build_instruction(irb, source_node); + instruction->value = value; + + ir_ref_instruction(value); + + return &instruction->base; +} + +static IrInstruction *ir_build_to_ptr_type(IrBuilder *irb, AstNode *source_node, IrInstruction *value) { + IrInstructionToPtrType *instruction = ir_build_instruction(irb, source_node); + instruction->value = value; + + ir_ref_instruction(value); + + return &instruction->base; +} + +static IrInstruction *ir_build_ptr_type_child(IrBuilder *irb, AstNode *source_node, IrInstruction *value) { + IrInstructionPtrTypeChild *instruction = ir_build_instruction(irb, source_node); + instruction->value = value; + + ir_ref_instruction(value); + + 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) { @@ -648,11 +774,11 @@ static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) { irb->current_basic_block = basic_block; } -static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, Buf *name, - bool is_const, bool is_shadowable) +static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, BlockContext *scope, + Buf *name, bool is_const, bool is_shadowable) { VariableTableEntry *variable_entry = allocate(1); - variable_entry->block_context = node->block_context; + variable_entry->block_context = scope; variable_entry->import = node->owner; variable_entry->shadowable = is_shadowable; variable_entry->mem_slot_index = SIZE_MAX; @@ -686,6 +812,7 @@ static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, Buf *n node->block_context->var_table.put(&variable_entry->name, variable_entry); } else { + assert(is_shadowable); // TODO replace _anon with @anon and make sure all tests still pass buf_init_from_str(&variable_entry->name, "_anon"); } @@ -697,10 +824,10 @@ static VariableTableEntry *add_local_var(CodeGen *codegen, AstNode *node, Buf *n } // Set name to nullptr to make the variable anonymous (not visible to programmer). -static VariableTableEntry *ir_add_local_var(IrBuilder *irb, AstNode *node, Buf *name, +static VariableTableEntry *ir_add_local_var(IrBuilder *irb, AstNode *node, BlockContext *scope, Buf *name, bool is_const, bool is_shadowable) { - VariableTableEntry *var = add_local_var(irb->codegen, node, name, is_const, is_shadowable); + VariableTableEntry *var = add_local_var(irb->codegen, node, scope, name, is_const, is_shadowable); var->mem_slot_index = exec_next_mem_slot(irb->exec); return var; } @@ -963,6 +1090,23 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, AstNode *node, LValPur return ir_build_load_ptr(irb, node, ptr_instruction); } +static IrInstruction *ir_gen_field_access(IrBuilder *irb, AstNode *node, LValPurpose lval) { + assert(node->type == NodeTypeFieldAccessExpr); + + AstNode *container_ref_node = node->data.field_access_expr.struct_expr; + Buf *field_name = node->data.field_access_expr.field_name; + + IrInstruction *container_ref_instruction = ir_gen_node(irb, container_ref_node, node->block_context); + if (container_ref_instruction == irb->codegen->invalid_instruction) + return container_ref_instruction; + + if (lval == LValPurposeNone) { + return ir_build_read_field(irb, node, container_ref_instruction, field_name); + } else { + return ir_build_field_ptr(irb, node, container_ref_instruction, field_name); + } +} + static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) { assert(node->type == NodeTypeFnCallExpr); @@ -990,6 +1134,12 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) { if (builtin_fn->id == BuiltinFnIdUnreachable) { return ir_build_unreachable(irb, node); + } else if (builtin_fn->id == BuiltinFnIdTypeof) { + AstNode *arg_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg = ir_gen_node(irb, arg_node, node->block_context); + if (arg == irb->codegen->invalid_instruction) + return arg; + return ir_build_typeof(irb, node, arg); } IrInstruction **args = allocate(actual_param_count); @@ -1183,7 +1333,8 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, AstNode *node) { bool is_shadowable = false; bool is_const = variable_declaration->is_const; bool is_extern = variable_declaration->is_extern; - VariableTableEntry *var = ir_add_local_var(irb, node, variable_declaration->symbol, is_const, is_shadowable); + VariableTableEntry *var = ir_add_local_var(irb, node, node->block_context, + variable_declaration->symbol, is_const, is_shadowable); if (!is_extern && !variable_declaration->expr) { var->type = irb->codegen->builtin_types.entry_invalid; @@ -1232,6 +1383,102 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, AstNode *node) { return ir_build_const_void(irb, node); } +static IrInstruction *ir_gen_for_expr(IrBuilder *irb, AstNode *node) { + assert(node->type == NodeTypeForExpr); + + BlockContext *parent_scope = node->block_context; + + AstNode *array_node = node->data.for_expr.array_expr; + AstNode *elem_node = node->data.for_expr.elem_node; + AstNode *index_node = node->data.for_expr.index_node; + AstNode *body_node = node->data.for_expr.body; + + if (!elem_node) { + add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter")); + return irb->codegen->invalid_instruction; + } + assert(elem_node->type == NodeTypeSymbol); + + IrInstruction *array_val = ir_gen_node(irb, array_node, parent_scope); + if (array_val == irb->codegen->invalid_instruction) + return array_val; + + IrInstruction *array_type = ir_build_typeof(irb, array_node, array_val); + IrInstruction *pointer_type = ir_build_to_ptr_type(irb, array_node, array_type); + IrInstruction *elem_var_type; + if (node->data.for_expr.elem_is_ptr) { + elem_var_type = pointer_type; + } else { + elem_var_type = ir_build_ptr_type_child(irb, elem_node, pointer_type); + } + + BlockContext *child_scope = new_block_context(node, parent_scope); + child_scope->parent_loop_node = node; + elem_node->block_context = child_scope; + + // TODO make it an error to write to element variable or i variable. + + Buf *elem_var_name = elem_node->data.symbol_expr.symbol; + node->data.for_expr.elem_var = ir_add_local_var(irb, elem_node, child_scope, elem_var_name, false, false); + IrInstruction *undefined_value = ir_build_const_undefined(irb, elem_node); + ir_build_var_decl(irb, elem_node, node->data.for_expr.elem_var, elem_var_type, undefined_value); + IrInstruction *elem_var_ptr = ir_build_var_ptr(irb, node, node->data.for_expr.elem_var); + + if (index_node) { + Buf *index_var_name = index_node->data.symbol_expr.symbol; + index_node->block_context = child_scope; + node->data.for_expr.index_var = ir_add_local_var(irb, index_node, child_scope, index_var_name, false, false); + } else { + node->data.for_expr.index_var = ir_add_local_var(irb, node, child_scope, nullptr, false, true); + } + IrInstruction *usize = ir_build_const_type(irb, node, irb->codegen->builtin_types.entry_usize); + IrInstruction *zero = ir_build_const_usize(irb, node, 0); + IrInstruction *one = ir_build_const_usize(irb, node, 1); + ir_build_var_decl(irb, index_node, node->data.for_expr.index_var, usize, zero); + IrInstruction *index_ptr = ir_build_var_ptr(irb, node, node->data.for_expr.index_var); + + + IrBasicBlock *cond_block = ir_build_basic_block(irb, "ForCond"); + IrBasicBlock *body_block = ir_build_basic_block(irb, "ForBody"); + IrBasicBlock *end_block = ir_build_basic_block(irb, "ForEnd"); + IrBasicBlock *continue_block = ir_build_basic_block(irb, "ForContinue"); + + IrInstruction *len_val = ir_build_read_field(irb, node, array_val, irb->codegen->len_buf); + ir_build_br(irb, node, cond_block); + + ir_set_cursor_at_end(irb, cond_block); + IrInstruction *index_val = ir_build_load_ptr(irb, node, index_ptr); + IrInstruction *cond = ir_build_bin_op(irb, node, IrBinOpCmpLessThan, index_val, len_val); + ir_build_cond_br(irb, node, cond, body_block, end_block); + + ir_set_cursor_at_end(irb, body_block); + IrInstruction *elem_ptr = ir_build_elem_ptr(irb, node, array_val, index_val); + IrInstruction *elem_val; + if (node->data.for_expr.elem_is_ptr) { + elem_val = elem_ptr; + } else { + elem_val = ir_build_load_ptr(irb, node, elem_ptr); + } + ir_build_store_ptr(irb, node, elem_var_ptr, elem_val); + + irb->break_block_stack.append(end_block); + irb->continue_block_stack.append(continue_block); + ir_gen_node(irb, body_node, child_scope); + irb->break_block_stack.pop(); + irb->continue_block_stack.pop(); + + ir_build_br(irb, node, continue_block); + + ir_set_cursor_at_end(irb, continue_block); + IrInstruction *new_index_val = ir_build_bin_op(irb, node, IrBinOpAdd, index_val, one); + ir_build_store_ptr(irb, node, index_ptr, new_index_val); + ir_build_br(irb, node, cond_block); + + ir_set_cursor_at_end(irb, end_block); + return ir_build_const_void(irb, node); + +} + static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockContext *block_context, LValPurpose lval) { @@ -1259,16 +1506,18 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, BlockCont return ir_gen_var_decl(irb, node); case NodeTypeWhileExpr: return ir_gen_while_expr(irb, node); + case NodeTypeForExpr: + return ir_gen_for_expr(irb, node); case NodeTypeArrayAccessExpr: return ir_gen_array_access(irb, node, lval); case NodeTypeReturnExpr: return ir_gen_return(irb, node); + case NodeTypeFieldAccessExpr: + return ir_gen_field_access(irb, node, lval); case NodeTypeUnwrapErrorExpr: case NodeTypeDefer: case NodeTypeSliceExpr: - case NodeTypeFieldAccessExpr: case NodeTypeIfVarExpr: - case NodeTypeForExpr: case NodeTypeAsmExpr: case NodeTypeGoto: case NodeTypeBreak: @@ -1587,6 +1836,12 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } } + // implicit undefined literal to anything + if (actual_type->id == TypeTableEntryIdUndefLit) { + return ImplicitCastMatchResultYes; + } + + return ImplicitCastMatchResultNo; } @@ -1661,6 +1916,45 @@ static ConstExprValue *ir_get_out_val(IrInstruction *instruction) { return &instruction->static_value; } +static TypeTableEntry *ir_analyze_void(IrAnalyze *ira, IrInstruction *instruction) { + ConstExprValue *const_val = ir_get_out_val(instruction); + const_val->ok = true; + return ira->codegen->builtin_types.entry_void; +} + +static TypeTableEntry *ir_analyze_const_usize(IrAnalyze *ira, IrInstruction *instruction, uint64_t value, + bool depends_on_compile_var) +{ + ConstExprValue *const_val = ir_get_out_val(instruction); + const_val->ok = true; + const_val->depends_on_compile_var = depends_on_compile_var; + bignum_init_unsigned(&const_val->data.x_bignum, value); + return ira->codegen->builtin_types.entry_usize; +} + +static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { + if (type_value == ira->codegen->invalid_instruction) + return ira->codegen->builtin_types.entry_invalid; + + if (type_value->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + if (type_value->type_entry->id != TypeTableEntryIdMetaType) { + add_node_error(ira->codegen, type_value->source_node, + buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + ConstExprValue *const_val = &type_value->static_value; + if (!const_val->ok) { + add_node_error(ira->codegen, type_value->source_node, + buf_sprintf("unable to evaluate constant expression")); + return ira->codegen->builtin_types.entry_invalid; + } + + return const_val->data.x_type; +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *dest_type, IrInstruction *value) { @@ -1911,6 +2205,11 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpEnumToInt, false); } + // explicit cast from undefined to anything + if (actual_type->id == TypeTableEntryIdUndefLit) { + return ir_resolve_cast(ira, source_instr, value, dest_type, CastOpNoop, false); + } + add_node_error(ira->codegen, source_instr->source_node, buf_sprintf("invalid cast from type '%s' to '%s'", buf_ptr(&actual_type->name), @@ -1918,26 +2217,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ira->codegen->invalid_instruction; } -static TypeTableEntry *ir_get_canonical_type(IrAnalyze *ira, IrInstruction *type_value) { - if (type_value == ira->codegen->invalid_instruction) - return ira->codegen->builtin_types.entry_invalid; - - if (type_value->type_entry->id == TypeTableEntryIdInvalid) - return ira->codegen->builtin_types.entry_invalid; - - if (type_value->type_entry->id != TypeTableEntryIdMetaType) { - add_node_error(ira->codegen, type_value->source_node, buf_sprintf("expected type, found expression")); - return ira->codegen->builtin_types.entry_invalid; - } - - if (!type_value->static_value.ok) { - add_node_error(ira->codegen, type_value->source_node, buf_sprintf("unable to evaluate constant expression")); - return ira->codegen->builtin_types.entry_invalid; - } - - return get_underlying_type(type_value->static_value.data.x_type); -} - static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type) { assert(value); assert(value != ira->codegen->invalid_instruction); @@ -1976,14 +2255,22 @@ static IrInstruction *ir_get_casted_value(IrAnalyze *ira, IrInstruction *value, static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira, IrInstructionReturn *return_instruction) { - IrInstruction *value = ir_get_casted_value(ira, return_instruction->value->other, ira->explicit_return_type); - if (value == ira->codegen->invalid_instruction) - return ira->codegen->builtin_types.entry_invalid; + IrInstruction *value = return_instruction->value->other; + if (value == ira->codegen->invalid_instruction) { + ir_finish_bb(ira); + return ira->codegen->builtin_types.entry_unreachable; + } ira->implicit_return_type_list.append(value); - IrInstruction *new_instruction = ir_build_return_from(&ira->new_irb, &return_instruction->base, value); + IrInstruction *casted_value = ir_get_casted_value(ira, value, ira->explicit_return_type); + if (casted_value == ira->codegen->invalid_instruction) { + ir_finish_bb(ira); + return ira->codegen->builtin_types.entry_unreachable; + } + + ir_build_return_from(&ira->new_irb, &return_instruction->base, casted_value); ir_finish_bb(ira); - return new_instruction->type_entry; + return ira->codegen->builtin_types.entry_unreachable; } static TypeTableEntry *ir_analyze_instruction_const(IrAnalyze *ira, IrInstructionConst *const_instruction) { @@ -2354,6 +2641,10 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi } static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { + IrInstruction *init_value = decl_var_instruction->init_value->other; + if (init_value->type_entry->id == TypeTableEntryIdInvalid) + return init_value->type_entry; + VariableTableEntry *var = decl_var_instruction->var; AstNodeVariableDeclaration *variable_declaration = &var->decl_node->data.variable_declaration; bool is_export = (variable_declaration->top_level_decl.visib_mod == VisibModExport); @@ -2365,13 +2656,12 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc IrInstruction *var_type = nullptr; if (decl_var_instruction->var_type != nullptr) { var_type = decl_var_instruction->var_type->other; - TypeTableEntry *proposed_type = ir_get_canonical_type(ira, var_type); + TypeTableEntry *proposed_type = ir_resolve_type(ira, var_type); explicit_type = validate_var_type(ira->codegen, var_type->source_node, proposed_type); if (explicit_type->id == TypeTableEntryIdInvalid) return explicit_type; } - IrInstruction *init_value = decl_var_instruction->init_value->other; IrInstruction *casted_init_value = ir_get_casted_value(ira, init_value, explicit_type); TypeTableEntry *result_type = get_underlying_type(casted_init_value->type_entry); switch (result_type->id) { @@ -2423,7 +2713,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc } var->type = result_type; - assert(var->type != nullptr); // should have been caught by the parser + assert(var->type); ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; *mem_slot = casted_init_value->static_value; @@ -3480,44 +3770,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio // return g->builtin_types.entry_invalid; // } // } -// case BuiltinFnIdTypeof: -// { -// AstNode *expr_node = node->data.fn_call_expr.params.at(0); -// TypeTableEntry *type_entry = analyze_expression(g, import, context, nullptr, expr_node); -// -// switch (type_entry->id) { -// case TypeTableEntryIdInvalid: -// return type_entry; -// case TypeTableEntryIdNumLitFloat: -// case TypeTableEntryIdNumLitInt: -// case TypeTableEntryIdUndefLit: -// case TypeTableEntryIdNullLit: -// case TypeTableEntryIdNamespace: -// case TypeTableEntryIdBlock: -// case TypeTableEntryIdGenericFn: -// case TypeTableEntryIdVar: -// add_node_error(g, expr_node, -// buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name))); -// return g->builtin_types.entry_invalid; -// case TypeTableEntryIdMetaType: -// case TypeTableEntryIdVoid: -// case TypeTableEntryIdBool: -// case TypeTableEntryIdUnreachable: -// case TypeTableEntryIdInt: -// case TypeTableEntryIdFloat: -// case TypeTableEntryIdPointer: -// case TypeTableEntryIdArray: -// case TypeTableEntryIdStruct: -// case TypeTableEntryIdMaybe: -// case TypeTableEntryIdErrorUnion: -// case TypeTableEntryIdPureError: -// case TypeTableEntryIdEnum: -// case TypeTableEntryIdUnion: -// case TypeTableEntryIdFn: -// case TypeTableEntryIdTypeDecl: -// return resolve_expr_const_val_as_type(g, node, type_entry, false); -// } -// } // case BuiltinFnIdCInclude: // { // if (!context->c_import_buf) { @@ -3867,8 +4119,10 @@ static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) { TypeTableEntry *bool_type = ira->codegen->builtin_types.entry_bool; IrInstruction *condition = ir_get_casted_value(ira, cond_br_instruction->condition->other, bool_type); - if (condition == ira->codegen->invalid_instruction) - return ira->codegen->builtin_types.entry_invalid; + if (condition == ira->codegen->invalid_instruction) { + ir_finish_bb(ira); + return ira->codegen->builtin_types.entry_unreachable; + } // TODO detect backward jumps if (condition->static_value.ok) { @@ -3892,6 +4146,7 @@ static TypeTableEntry *ir_analyze_instruction_builtin_call(IrAnalyze *ira, switch (builtin_call_instruction->fn->id) { case BuiltinFnIdInvalid: case BuiltinFnIdUnreachable: + case BuiltinFnIdTypeof: zig_unreachable(); case BuiltinFnIdMemcpy: case BuiltinFnIdMemset: @@ -3900,7 +4155,6 @@ static TypeTableEntry *ir_analyze_instruction_builtin_call(IrAnalyze *ira, case BuiltinFnIdMaxValue: case BuiltinFnIdMinValue: case BuiltinFnIdMemberCount: - case BuiltinFnIdTypeof: case BuiltinFnIdAddWithOverflow: case BuiltinFnIdSubWithOverflow: case BuiltinFnIdMulWithOverflow: @@ -3938,9 +4192,9 @@ static TypeTableEntry *ir_analyze_instruction_builtin_call(IrAnalyze *ira, static TypeTableEntry *ir_analyze_instruction_unreachable(IrAnalyze *ira, IrInstructionUnreachable *unreachable_instruction) { - IrInstruction *new_instruction = ir_build_unreachable_from(&ira->new_irb, &unreachable_instruction->base); + ir_build_unreachable_from(&ira->new_irb, &unreachable_instruction->base); ir_finish_bb(ira); - return new_instruction->type_entry; + return ira->codegen->builtin_types.entry_unreachable; } static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionPhi *phi_instruction) { @@ -3997,7 +4251,10 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP static TypeTableEntry *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructionVarPtr *var_ptr_instruction) { VariableTableEntry *var = var_ptr_instruction->var; - TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, var_ptr_instruction->var->type, false); + if (var->type->id == TypeTableEntryIdInvalid) + return var->type; + + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, var->type, false); // TODO once the anlayze code is fully ported over to IR we won't need this SIZE_MAX thing. if (var->mem_slot_index != SIZE_MAX) { ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; @@ -4057,6 +4314,159 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc return return_type; } +static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira, + TypeTableEntry *bare_struct_type, Buf *field_name, IrInstructionFieldPtr *field_ptr_instruction, + TypeTableEntry *container_type) +{ + if (!is_slice(bare_struct_type)) { + BlockContext *container_block_context = get_container_block_context(bare_struct_type); + assert(container_block_context); + auto entry = container_block_context->decl_table.maybe_get(field_name); + AstNode *fn_decl_node = entry ? entry->value : nullptr; + if (fn_decl_node && fn_decl_node->type == NodeTypeFnProto) { + zig_panic("TODO member function call"); + } + } + add_node_error(ira->codegen, field_ptr_instruction->base.source_node, + buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name))); + return ira->codegen->builtin_types.entry_invalid; +} + + +static TypeTableEntry *ir_analyze_container_member_access(IrAnalyze *ira, Buf *field_name, + IrInstructionFieldPtr *field_ptr_instruction, TypeTableEntry *container_type) +{ + IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other; + TypeTableEntry *bare_type = container_ref_type(container_type); + if (!type_is_complete(bare_type)) { + resolve_container_type(ira->codegen, bare_type); + } + + if (bare_type->id == TypeTableEntryIdStruct) { + TypeStructField *field = find_struct_type_field(bare_type, field_name); + if (field) { + ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); + return get_pointer_to_type(ira->codegen, field->type_entry, false); + } else { + return ir_analyze_container_member_access_inner(ira, bare_type, field_name, + field_ptr_instruction, container_type); + } + } else if (bare_type->id == TypeTableEntryIdEnum) { + zig_panic("TODO enum field ptr"); + } else if (bare_type->id == TypeTableEntryIdUnion) { + zig_panic("TODO"); + } else { + zig_unreachable(); + } +} + +static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) { + IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other; + Buf *field_name = field_ptr_instruction->field_name; + + TypeTableEntry *container_type = container_ptr->type_entry; + if (container_type->id == TypeTableEntryIdInvalid) { + return container_type; + } else if (is_container_ref(container_type)) { + return ir_analyze_container_member_access(ira, field_name, field_ptr_instruction, container_type); + } else if (container_type->id == TypeTableEntryIdArray) { + if (buf_eql_str(field_name, "len")) { + add_node_error(ira->codegen, field_ptr_instruction->base.source_node, + buf_sprintf("pointer to array length not available")); + return ira->codegen->builtin_types.entry_invalid; + } else { + add_node_error(ira->codegen, field_ptr_instruction->base.source_node, + buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), + buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (container_type->id == TypeTableEntryIdMetaType) { + TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr); + + if (child_type->id == TypeTableEntryIdInvalid) { + return ira->codegen->builtin_types.entry_invalid; + } else if (child_type->id == TypeTableEntryIdEnum) { + zig_panic("TODO enum type field"); + } else if (child_type->id == TypeTableEntryIdStruct) { + zig_panic("TODO struct type field"); + } else if (child_type->id == TypeTableEntryIdPureError) { + zig_panic("TODO error type field"); + } else if (child_type->id == TypeTableEntryIdInt) { + zig_panic("TODO integer type field"); + } else { + add_node_error(ira->codegen, field_ptr_instruction->base.source_node, + buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (container_type->id == TypeTableEntryIdNamespace) { + zig_panic("TODO namespace field access"); + } else { + add_node_error(ira->codegen, field_ptr_instruction->base.source_node, + buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } +} + +static TypeTableEntry *ir_analyze_read_field_as_ptr_load(IrAnalyze *ira, + IrInstructionReadField *read_field_instruction) +{ + IrInstruction *old_field_ptr_inst = ir_build_field_ptr(&ira->old_irb, read_field_instruction->base.source_node, + read_field_instruction->container_ptr, read_field_instruction->field_name); + IrInstruction *old_load_ptr_inst = ir_build_load_ptr(&ira->old_irb, read_field_instruction->base.source_node, + old_field_ptr_inst); + ir_analyze_instruction(ira, old_field_ptr_inst); + TypeTableEntry *result_type = ir_analyze_instruction(ira, old_load_ptr_inst); + read_field_instruction->base.other = old_load_ptr_inst->other; + return result_type; +} + +static TypeTableEntry *ir_analyze_instruction_read_field(IrAnalyze *ira, + IrInstructionReadField *read_field_instruction) +{ + IrInstruction *container_ptr = read_field_instruction->container_ptr->other; + Buf *field_name = read_field_instruction->field_name; + + TypeTableEntry *container_type = container_ptr->type_entry; + if (container_type->id == TypeTableEntryIdInvalid) { + return container_type; + } else if (is_container_ref(container_type)) { + return ir_analyze_read_field_as_ptr_load(ira, read_field_instruction); + } else if (container_type->id == TypeTableEntryIdArray) { + if (buf_eql_str(field_name, "len")) { + return ir_analyze_const_usize(ira, &read_field_instruction->base, container_type->data.array.len, false); + } else { + add_node_error(ira->codegen, read_field_instruction->base.source_node, + buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), + buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (container_type->id == TypeTableEntryIdMetaType) { + TypeTableEntry *child_type = ir_resolve_type(ira, container_ptr); + + if (child_type->id == TypeTableEntryIdInvalid) { + return ira->codegen->builtin_types.entry_invalid; + } else if (child_type->id == TypeTableEntryIdEnum) { + zig_panic("TODO enum type field"); + } else if (child_type->id == TypeTableEntryIdStruct) { + zig_panic("TODO struct type field"); + } else if (child_type->id == TypeTableEntryIdPureError) { + zig_panic("TODO error type field"); + } else if (child_type->id == TypeTableEntryIdInt) { + zig_panic("TODO integer type field"); + } else { + add_node_error(ira->codegen, read_field_instruction->base.source_node, + buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (container_type->id == TypeTableEntryIdNamespace) { + zig_panic("TODO namespace field access"); + } else { + add_node_error(ira->codegen, read_field_instruction->base.source_node, + buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } +} + static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { IrInstruction *ptr = load_ptr_instruction->ptr->other; TypeTableEntry *type_entry = ptr->type_entry; @@ -4084,7 +4494,12 @@ static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstruc static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstructionStorePtr *store_ptr_instruction) { IrInstruction *ptr = store_ptr_instruction->ptr->other; + if (ptr->type_entry->id == TypeTableEntryIdInvalid) + return ptr->type_entry; + IrInstruction *value = store_ptr_instruction->value->other; + if (value->type_entry->id == TypeTableEntryIdInvalid) + return value->type_entry; TypeTableEntry *child_type = ptr->type_entry->data.pointer.child_type; IrInstruction *casted_value = ir_get_casted_value(ira, value, child_type); @@ -4095,7 +4510,7 @@ static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstru ConstExprValue *dest_val = ptr->static_value.data.x_ptr.ptr[0]; if (dest_val->ok) { *dest_val = casted_value->static_value; - return ira->codegen->builtin_types.entry_void; + return ir_analyze_void(ira, &store_ptr_instruction->base); } } @@ -4120,13 +4535,108 @@ static TypeTableEntry *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstru } new_ptr_inst->type_entry = ptr->type_entry; ir_build_store_ptr(&ira->new_irb, store_ptr_instruction->base.source_node, new_ptr_inst, casted_value); - return ira->codegen->builtin_types.entry_void; + return ir_analyze_void(ira, &store_ptr_instruction->base); } ir_build_store_ptr_from(&ira->new_irb, &store_ptr_instruction->base, ptr, casted_value); return ira->codegen->builtin_types.entry_void; } +static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructionTypeOf *typeof_instruction) { + IrInstruction *expr_value = typeof_instruction->value->other; + TypeTableEntry *type_entry = expr_value->type_entry; + switch (type_entry->id) { + case TypeTableEntryIdInvalid: + return type_entry; + case TypeTableEntryIdVar: + add_node_error(ira->codegen, expr_value->source_node, + buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdUndefLit: + case TypeTableEntryIdNullLit: + case TypeTableEntryIdNamespace: + case TypeTableEntryIdBlock: + case TypeTableEntryIdGenericFn: + case TypeTableEntryIdMetaType: + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdPointer: + case TypeTableEntryIdArray: + case TypeTableEntryIdStruct: + case TypeTableEntryIdMaybe: + case TypeTableEntryIdErrorUnion: + case TypeTableEntryIdPureError: + case TypeTableEntryIdEnum: + case TypeTableEntryIdUnion: + case TypeTableEntryIdFn: + case TypeTableEntryIdTypeDecl: + { + ConstExprValue *out_val = ir_get_out_val(&typeof_instruction->base); + out_val->ok = true; + // TODO depends_on_compile_var should be set based on whether the type of the expression + // depends_on_compile_var. but we currently don't have a thing to tell us if the type of + // something depends on a compile var + out_val->data.x_type = type_entry; + + return ira->codegen->builtin_types.entry_type; + } + } + + zig_unreachable(); +} + +static TypeTableEntry *ir_analyze_instruction_to_ptr_type(IrAnalyze *ira, + IrInstructionToPtrType *to_ptr_type_instruction) +{ + IrInstruction *type_value = to_ptr_type_instruction->value->other; + TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); + if (type_entry->id == TypeTableEntryIdInvalid) + return type_entry; + + TypeTableEntry *ptr_type; + if (type_entry->id == TypeTableEntryIdArray) { + ptr_type = get_pointer_to_type(ira->codegen, type_entry->data.array.child_type, false); + } else if (is_slice(type_entry)) { + ptr_type = type_entry->data.structure.fields[0].type_entry; + } else { + add_node_error(ira->codegen, to_ptr_type_instruction->base.source_node, + buf_sprintf("expected array type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + ConstExprValue *out_val = ir_get_out_val(&to_ptr_type_instruction->base); + out_val->ok = true; + out_val->depends_on_compile_var = type_value->static_value.depends_on_compile_var; + out_val->data.x_type = ptr_type; + return ira->codegen->builtin_types.entry_type; +} + +static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira, + IrInstructionPtrTypeChild *ptr_type_child_instruction) +{ + IrInstruction *type_value = ptr_type_child_instruction->value->other; + TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); + if (type_entry->id == TypeTableEntryIdInvalid) + return type_entry; + + if (type_entry->id != TypeTableEntryIdPointer) { + add_node_error(ira->codegen, ptr_type_child_instruction->base.source_node, + buf_sprintf("expected pointer type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->builtin_types.entry_invalid; + } + + ConstExprValue *out_val = ir_get_out_val(&ptr_type_child_instruction->base); + out_val->ok = true; + out_val->depends_on_compile_var = type_value->static_value.depends_on_compile_var; + out_val->data.x_type = type_entry->data.pointer.child_type; + return ira->codegen->builtin_types.entry_type; +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -4145,12 +4655,14 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_load_ptr(ira, (IrInstructionLoadPtr *)instruction); case IrInstructionIdStorePtr: return ir_analyze_instruction_store_ptr(ira, (IrInstructionStorePtr *)instruction); - case IrInstructionIdFieldPtr: - zig_panic("TODO field ptr"); case IrInstructionIdElemPtr: return ir_analyze_instruction_elem_ptr(ira, (IrInstructionElemPtr *)instruction); case IrInstructionIdVarPtr: return ir_analyze_instruction_var_ptr(ira, (IrInstructionVarPtr *)instruction); + case IrInstructionIdFieldPtr: + return ir_analyze_instruction_field_ptr(ira, (IrInstructionFieldPtr *)instruction); + case IrInstructionIdReadField: + return ir_analyze_instruction_read_field(ira, (IrInstructionReadField *)instruction); case IrInstructionIdCall: return ir_analyze_instruction_call(ira, (IrInstructionCall *)instruction); case IrInstructionIdBr: @@ -4163,10 +4675,17 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_unreachable(ira, (IrInstructionUnreachable *)instruction); case IrInstructionIdPhi: return ir_analyze_instruction_phi(ira, (IrInstructionPhi *)instruction); + case IrInstructionIdTypeOf: + return ir_analyze_instruction_typeof(ira, (IrInstructionTypeOf *)instruction); + case IrInstructionIdToPtrType: + return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction); + case IrInstructionIdPtrTypeChild: + return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction); case IrInstructionIdSwitchBr: case IrInstructionIdCast: case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitFields: + case IrInstructionIdStructFieldPtr: zig_panic("TODO analyze more instructions"); } zig_unreachable(); @@ -4175,8 +4694,13 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) { TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction); instruction->type_entry = instruction_type; - if (instruction->other) + if (instruction->other) { instruction->other->type_entry = instruction_type; + } else { + assert(instruction_type->id == TypeTableEntryIdInvalid || + instruction_type->id == TypeTableEntryIdUnreachable); + instruction->other = instruction; + } return instruction_type; } @@ -4209,6 +4733,11 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl while (ira->block_queue_index < ira->old_bb_queue.length) { IrInstruction *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); + + fprintf(stderr, "===%zu====", old_instruction->debug_id); + ir_print(stderr, ira->new_irb.exec, 4); + fprintf(stderr, "========"); + if (old_instruction->ref_count == 0 && !ir_has_side_effects(old_instruction)) { ira->instruction_index += 1; continue; @@ -4299,6 +4828,11 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFieldPtr: case IrInstructionIdElemPtr: case IrInstructionIdVarPtr: + case IrInstructionIdTypeOf: + case IrInstructionIdToPtrType: + case IrInstructionIdPtrTypeChild: + case IrInstructionIdReadField: + case IrInstructionIdStructFieldPtr: return false; case IrInstructionIdBuiltinCall: return ir_builtin_call_has_side_effects((IrInstructionBuiltinCall *)instruction); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 95c968f71d..6d57974ba5 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -22,6 +22,16 @@ static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction) { } static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, ConstExprValue *const_val) { + switch (const_val->special) { + case ConstValSpecialUndef: + fprintf(irp->f, "undefined"); + return; + case ConstValSpecialZeroes: + fprintf(irp->f, "zeroes"); + return; + case ConstValSpecialOther: + break; + } switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); @@ -343,6 +353,43 @@ static void ir_print_store_ptr(IrPrint *irp, IrInstructionStorePtr *instruction) ir_print_other_instruction(irp, instruction->value); } +static void ir_print_typeof(IrPrint *irp, IrInstructionTypeOf *instruction) { + fprintf(irp->f, "@typeOf("); + ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, ")"); +} + +static void ir_print_to_ptr_type(IrPrint *irp, IrInstructionToPtrType *instruction) { + fprintf(irp->f, "@toPtrType("); + ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, ")"); +} + +static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *instruction) { + fprintf(irp->f, "@ptrTypeChild("); + ir_print_other_instruction(irp, instruction->value); + fprintf(irp->f, ")"); +} + +static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) { + fprintf(irp->f, "@FieldPtr(&"); + ir_print_other_instruction(irp, instruction->container_ptr); + fprintf(irp->f, ".%s", buf_ptr(instruction->field_name)); + fprintf(irp->f, ")"); +} + +static void ir_print_read_field(IrPrint *irp, IrInstructionReadField *instruction) { + ir_print_other_instruction(irp, instruction->container_ptr); + fprintf(irp->f, ".%s", buf_ptr(instruction->field_name)); +} + +static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) { + fprintf(irp->f, "@StructFieldPtr(&"); + ir_print_other_instruction(irp, instruction->struct_ptr); + fprintf(irp->f, ".%s", buf_ptr(instruction->field->name)); + fprintf(irp->f, ")"); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -402,8 +449,25 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdStorePtr: ir_print_store_ptr(irp, (IrInstructionStorePtr *)instruction); break; - case IrInstructionIdSwitchBr: + case IrInstructionIdTypeOf: + ir_print_typeof(irp, (IrInstructionTypeOf *)instruction); + break; + case IrInstructionIdToPtrType: + ir_print_to_ptr_type(irp, (IrInstructionToPtrType *)instruction); + break; + case IrInstructionIdPtrTypeChild: + ir_print_ptr_type_child(irp, (IrInstructionPtrTypeChild *)instruction); + break; case IrInstructionIdFieldPtr: + ir_print_field_ptr(irp, (IrInstructionFieldPtr *)instruction); + break; + case IrInstructionIdReadField: + ir_print_read_field(irp, (IrInstructionReadField *)instruction); + break; + case IrInstructionIdStructFieldPtr: + ir_print_struct_field_ptr(irp, (IrInstructionStructFieldPtr *)instruction); + break; + case IrInstructionIdSwitchBr: zig_panic("TODO print more IR instructions"); } fprintf(irp->f, "\n");