From bb39e503c0179b18c4e440bae021e786c70deb06 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 7 Jan 2018 00:20:26 -0500 Subject: [PATCH] fix struct inside function referencing local const closes #672 the crash and compile errors are fixed but structs inside functions still get named after the functions they're in. this will be fixed later. --- src/all_types.hpp | 11 ++---- src/ir.cpp | 82 +++++++++++++++++++++++------------------ test/cases/misc.zig | 19 ++++++++++ test/compile_errors.zig | 12 ++++++ 4 files changed, 81 insertions(+), 43 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 12ecc84a86..0b25798d7f 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -37,13 +37,7 @@ struct ScopeDecls; struct ZigWindowsSDK; struct Tld; struct TldExport; - -struct IrGotoItem { - AstNode *source_node; - IrBasicBlock *bb; - size_t instruction_index; - Scope *scope; -}; +struct IrAnalyze; struct IrExecutable { ZigList basic_block_list; @@ -53,13 +47,13 @@ struct IrExecutable { size_t *backward_branch_count; size_t backward_branch_quota; bool invalid; - ZigList goto_list; bool is_inline; FnTableEntry *fn_entry; Buf *c_import_buf; AstNode *source_node; IrExecutable *parent_exec; IrExecutable *source_exec; + IrAnalyze *analysis; Scope *begin_scope; ZigList tld_list; }; @@ -1626,6 +1620,7 @@ struct VariableTableEntry { LLVMValueRef param_value_ref; bool shadowable; size_t mem_slot_index; + IrExecutable *owner_exec; size_t ref_count; VarLinkage linkage; IrInstruction *decl_instruction; diff --git a/src/ir.cpp b/src/ir.cpp index 84ba977c94..df75041f39 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2530,8 +2530,10 @@ static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *s bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime) { VariableTableEntry *var = create_local_var(irb->codegen, node, scope, name, src_is_const, gen_is_const, is_shadowable, is_comptime); - if (is_comptime != nullptr || gen_is_const) + if (is_comptime != nullptr || gen_is_const) { var->mem_slot_index = exec_next_mem_slot(irb->exec); + var->owner_exec = irb->exec; + } assert(var->child_scope); return var; } @@ -7037,48 +7039,48 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node if (expected_type != nullptr && type_is_invalid(expected_type)) return codegen->invalid_instruction; - IrExecutable ir_executable = {0}; - ir_executable.source_node = source_node; - ir_executable.parent_exec = parent_exec; - ir_executable.name = exec_name; - ir_executable.is_inline = true; - ir_executable.fn_entry = fn_entry; - ir_executable.c_import_buf = c_import_buf; - ir_executable.begin_scope = scope; - ir_gen(codegen, node, scope, &ir_executable); + IrExecutable *ir_executable = allocate(1); + ir_executable->source_node = source_node; + ir_executable->parent_exec = parent_exec; + ir_executable->name = exec_name; + ir_executable->is_inline = true; + ir_executable->fn_entry = fn_entry; + ir_executable->c_import_buf = c_import_buf; + ir_executable->begin_scope = scope; + ir_gen(codegen, node, scope, ir_executable); - if (ir_executable.invalid) + if (ir_executable->invalid) return codegen->invalid_instruction; if (codegen->verbose_ir) { fprintf(stderr, "\nSource: "); ast_render(codegen, stderr, node, 4); fprintf(stderr, "\n{ // (IR)\n"); - ir_print(codegen, stderr, &ir_executable, 4); + ir_print(codegen, stderr, ir_executable, 4); fprintf(stderr, "}\n"); } - IrExecutable analyzed_executable = {0}; - analyzed_executable.source_node = source_node; - analyzed_executable.parent_exec = parent_exec; - analyzed_executable.source_exec = &ir_executable; - analyzed_executable.name = exec_name; - analyzed_executable.is_inline = true; - analyzed_executable.fn_entry = fn_entry; - analyzed_executable.c_import_buf = c_import_buf; - analyzed_executable.backward_branch_count = backward_branch_count; - analyzed_executable.backward_branch_quota = backward_branch_quota; - analyzed_executable.begin_scope = scope; - TypeTableEntry *result_type = ir_analyze(codegen, &ir_executable, &analyzed_executable, expected_type, node); + IrExecutable *analyzed_executable = allocate(1); + analyzed_executable->source_node = source_node; + analyzed_executable->parent_exec = parent_exec; + analyzed_executable->source_exec = ir_executable; + analyzed_executable->name = exec_name; + analyzed_executable->is_inline = true; + analyzed_executable->fn_entry = fn_entry; + analyzed_executable->c_import_buf = c_import_buf; + analyzed_executable->backward_branch_count = backward_branch_count; + analyzed_executable->backward_branch_quota = backward_branch_quota; + analyzed_executable->begin_scope = scope; + TypeTableEntry *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, expected_type, node); if (type_is_invalid(result_type)) return codegen->invalid_instruction; if (codegen->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); - ir_print(codegen, stderr, &analyzed_executable, 4); + ir_print(codegen, stderr, analyzed_executable, 4); fprintf(stderr, "}\n"); } - return ir_exec_const_result(codegen, &analyzed_executable); + return ir_exec_const_result(codegen, analyzed_executable); } static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value) { @@ -9334,6 +9336,8 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, explicit_type); bool is_comptime_var = ir_get_var_is_comptime(var); + bool var_class_requires_const = false; + TypeTableEntry *result_type = casted_init_value->value.type; if (type_is_invalid(result_type)) { result_type = ira->codegen->builtin_types.entry_invalid; @@ -9345,6 +9349,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc result_type = ira->codegen->builtin_types.entry_invalid; break; case VarClassRequiredConst: + var_class_requires_const = true; if (!var->src_is_const && !is_comptime_var) { ir_add_error_node(ira, source_node, buf_sprintf("variable of type '%s' must be const or comptime", @@ -9366,8 +9371,6 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_void; } - bool is_comptime = ir_get_var_is_comptime(var); - if (decl_var_instruction->align_value == nullptr) { var->align_bytes = get_abi_alignment(ira->codegen, result_type); } else { @@ -9382,12 +9385,12 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc ConstExprValue *mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; *mem_slot = casted_init_value->value; - if (is_comptime) { + if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { ir_build_const_from(ira, &decl_var_instruction->base); return ira->codegen->builtin_types.entry_void; } } - } else if (is_comptime) { + } else if (is_comptime_var) { ir_add_error(ira, &decl_var_instruction->base, buf_sprintf("cannot store runtime value in compile time variable")); var->value->type = ira->codegen->builtin_types.entry_invalid; @@ -9690,6 +9693,10 @@ static VariableTableEntry *get_fn_var_by_index(FnTableEntry *fn_entry, size_t in static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr) { + if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) { + assert(ira->codegen->errors.length != 0); + return ira->codegen->invalid_instruction; + } assert(var->value->type); if (type_is_invalid(var->value->type)) return ira->codegen->invalid_instruction; @@ -9700,9 +9707,14 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, if (var->value->special == ConstValSpecialStatic) { mem_slot = var->value; } else { - // TODO once the analyze code is fully ported over to IR we won't need this SIZE_MAX thing. - if (var->mem_slot_index != SIZE_MAX && (comptime_var_mem || var->gen_is_const)) - mem_slot = &ira->exec_context.mem_slot_list[var->mem_slot_index]; + if (var->mem_slot_index != SIZE_MAX && (comptime_var_mem || var->gen_is_const)) { + // find the relevant exec_context + assert(var->owner_exec != nullptr); + assert(var->owner_exec->analysis != nullptr); + IrExecContext *exec_context = &var->owner_exec->analysis->exec_context; + assert(var->mem_slot_index < exec_context->mem_slot_count); + mem_slot = &exec_context->mem_slot_list[var->mem_slot_index]; + } } bool is_const = (var->value->type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const; @@ -15328,8 +15340,8 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl assert(!old_exec->invalid); assert(expected_type == nullptr || !type_is_invalid(expected_type)); - IrAnalyze ir_analyze_data = {}; - IrAnalyze *ira = &ir_analyze_data; + IrAnalyze *ira = allocate(1); + old_exec->analysis = ira; ira->codegen = codegen; ira->explicit_return_type = expected_type; diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 0f9201c8e6..b6ec6e63cc 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -577,3 +577,22 @@ test "implicit comptime while" { @compileError("bad"); } } + +test "struct inside function" { + testStructInFn(); + comptime testStructInFn(); +} + +fn testStructInFn() { + const BlockKind = u32; + + const Block = struct { + kind: BlockKind, + }; + + var block = Block { .kind = 1234 }; + + block.kind += 1; + + assert(block.kind == 1235); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 60e5c3614d..ec3ec78664 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,18 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) { + cases.add("bad identifier in function with struct defined inside function which references local const", + \\export fn entry() { + \\ const BlockKind = u32; + \\ + \\ const Block = struct { + \\ kind: BlockKind, + \\ }; + \\ + \\ bogus; + \\} + , ".tmp_source.zig:8:5: error: use of undeclared identifier 'bogus'"); + cases.add("labeled break not found", \\export fn entry() { \\ blah: while (true) {