| author | |
| committer | |
| log | 24048b2af62f1b36678aa08a20d0754cb712e485 |
| tree | 09519ba8509ba40a3c1d9c85eee7d35f181ca37b |
| parent | 0541532ed6cf5a32b57a4a3f74e6d3a1699223a7 |
7 files changed, 228 insertions(+), 193 deletions(-)
src/all_types.hpp+16-3| ... | ... | @@ -642,9 +642,11 @@ struct AstNodeBoolLiteral { |
| 642 | 642 | }; |
| 643 | 643 | |
| 644 | 644 | struct AstNodeBreakExpr { |
| 645 | bool is_inline; // TODO | |
| 645 | 646 | }; |
| 646 | 647 | |
| 647 | 648 | struct AstNodeContinueExpr { |
| 649 | bool is_inline; // TODO | |
| 648 | 650 | }; |
| 649 | 651 | |
| 650 | 652 | struct AstNodeArrayType { |
| ... | ... | @@ -1233,11 +1235,21 @@ struct LabelTableEntry { |
| 1233 | 1235 | bool used; |
| 1234 | 1236 | }; |
| 1235 | 1237 | |
| 1238 | enum ScopeId { | |
| 1239 | ScopeIdDecls, | |
| 1240 | ScopeIdBlock, | |
| 1241 | ScopeIdDefer, | |
| 1242 | ScopeIdVarDecl, | |
| 1243 | ScopeIdCImport, | |
| 1244 | ScopeIdLoop, | |
| 1245 | ScopeIdFnDef, | |
| 1246 | }; | |
| 1247 | ||
| 1236 | 1248 | struct Scope { |
| 1237 | AstNode *node; | |
| 1249 | ScopeId id; | |
| 1250 | AstNode *source_node; | |
| 1238 | 1251 | |
| 1239 | // if the scope has a parent, this is it. Every scope has a parent except | |
| 1240 | // ScopeIdGlobal | |
| 1252 | // if the scope has a parent, this is it | |
| 1241 | 1253 | Scope *parent; |
| 1242 | 1254 | |
| 1243 | 1255 | ZigLLVMDIScope *di_scope; |
| ... | ... | @@ -1293,6 +1305,7 @@ struct ScopeCImport { |
| 1293 | 1305 | // This scope is created for a loop such as for or while in order to |
| 1294 | 1306 | // make break and continue statements work. |
| 1295 | 1307 | // NodeTypeForExpr or NodeTypeWhileExpr |
| 1308 | // TODO I think we can get rid of this | |
| 1296 | 1309 | struct ScopeLoop { |
| 1297 | 1310 | Scope base; |
| 1298 | 1311 | }; |
src/analyze.cpp+15-21| ... | ... | @@ -130,15 +130,16 @@ ScopeDecls *get_container_scope(TypeTableEntry *type_entry) { |
| 130 | 130 | return *get_container_scope_ptr(type_entry); |
| 131 | 131 | } |
| 132 | 132 | |
| 133 | void init_scope(Scope *dest, AstNode *node, Scope *parent) { | |
| 134 | dest->node = node; | |
| 133 | void init_scope(Scope *dest, ScopeId id, AstNode *source_node, Scope *parent) { | |
| 134 | dest->id = id; | |
| 135 | dest->source_node = source_node; | |
| 135 | 136 | dest->parent = parent; |
| 136 | 137 | } |
| 137 | 138 | |
| 138 | 139 | static ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import) { |
| 139 | 140 | assert(node->type == NodeTypeRoot || node->type == NodeTypeContainerDecl); |
| 140 | 141 | ScopeDecls *scope = allocate<ScopeDecls>(1); |
| 141 | init_scope(&scope->base, node, parent); | |
| 142 | init_scope(&scope->base, ScopeIdDecls, node, parent); | |
| 142 | 143 | scope->decl_table.init(4); |
| 143 | 144 | scope->container_type = container_type; |
| 144 | 145 | scope->import = import; |
| ... | ... | @@ -148,7 +149,7 @@ static ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEnt |
| 148 | 149 | Scope *create_block_scope(AstNode *node, Scope *parent) { |
| 149 | 150 | assert(node->type == NodeTypeBlock); |
| 150 | 151 | ScopeBlock *scope = allocate<ScopeBlock>(1); |
| 151 | init_scope(&scope->base, node, parent); | |
| 152 | init_scope(&scope->base, ScopeIdBlock, node, parent); | |
| 152 | 153 | scope->label_table.init(1); |
| 153 | 154 | return &scope->base; |
| 154 | 155 | } |
| ... | ... | @@ -156,14 +157,13 @@ Scope *create_block_scope(AstNode *node, Scope *parent) { |
| 156 | 157 | Scope *create_defer_scope(AstNode *node, Scope *parent) { |
| 157 | 158 | assert(node->type == NodeTypeDefer); |
| 158 | 159 | ScopeDefer *scope = allocate<ScopeDefer>(1); |
| 159 | init_scope(&scope->base, node, parent); | |
| 160 | init_scope(&scope->base, ScopeIdDefer, node, parent); | |
| 160 | 161 | return &scope->base; |
| 161 | 162 | } |
| 162 | 163 | |
| 163 | 164 | Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var) { |
| 164 | assert(node->type == NodeTypeVariableDeclaration || node->type == NodeTypeParamDecl); | |
| 165 | 165 | ScopeVarDecl *scope = allocate<ScopeVarDecl>(1); |
| 166 | init_scope(&scope->base, node, parent); | |
| 166 | init_scope(&scope->base, ScopeIdVarDecl, node, parent); | |
| 167 | 167 | scope->var = var; |
| 168 | 168 | return &scope->base; |
| 169 | 169 | } |
| ... | ... | @@ -171,7 +171,7 @@ Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var) { |
| 171 | 171 | Scope *create_cimport_scope(AstNode *node, Scope *parent) { |
| 172 | 172 | assert(node->type == NodeTypeFnCallExpr); |
| 173 | 173 | ScopeCImport *scope = allocate<ScopeCImport>(1); |
| 174 | init_scope(&scope->base, node, parent); | |
| 174 | init_scope(&scope->base, ScopeIdCImport, node, parent); | |
| 175 | 175 | buf_resize(&scope->c_import_buf, 0); |
| 176 | 176 | return &scope->base; |
| 177 | 177 | } |
| ... | ... | @@ -179,21 +179,21 @@ Scope *create_cimport_scope(AstNode *node, Scope *parent) { |
| 179 | 179 | Scope *create_loop_scope(AstNode *node, Scope *parent) { |
| 180 | 180 | assert(node->type == NodeTypeWhileExpr || node->type == NodeTypeForExpr); |
| 181 | 181 | ScopeLoop *scope = allocate<ScopeLoop>(1); |
| 182 | init_scope(&scope->base, node, parent); | |
| 182 | init_scope(&scope->base, ScopeIdLoop, node, parent); | |
| 183 | 183 | return &scope->base; |
| 184 | 184 | } |
| 185 | 185 | |
| 186 | 186 | ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) { |
| 187 | 187 | assert(node->type == NodeTypeFnDef); |
| 188 | 188 | ScopeFnDef *scope = allocate<ScopeFnDef>(1); |
| 189 | init_scope(&scope->base, node, parent); | |
| 189 | init_scope(&scope->base, ScopeIdFnDef, node, parent); | |
| 190 | 190 | scope->fn_entry = fn_entry; |
| 191 | 191 | return scope; |
| 192 | 192 | } |
| 193 | 193 | |
| 194 | 194 | ImportTableEntry *get_scope_import(Scope *scope) { |
| 195 | 195 | while (scope) { |
| 196 | if (scope->node->type == NodeTypeRoot || scope->node->type == NodeTypeContainerDecl) { | |
| 196 | if (scope->id == ScopeIdDecls) { | |
| 197 | 197 | ScopeDecls *decls_scope = (ScopeDecls *)scope; |
| 198 | 198 | assert(decls_scope->import); |
| 199 | 199 | return decls_scope->import; |
| ... | ... | @@ -1991,9 +1991,7 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * |
| 1991 | 1991 | |
| 1992 | 1992 | Tld *find_decl(Scope *scope, Buf *name) { |
| 1993 | 1993 | while (scope) { |
| 1994 | if (scope->node->type == NodeTypeRoot || | |
| 1995 | scope->node->type == NodeTypeContainerDecl) | |
| 1996 | { | |
| 1994 | if (scope->id == ScopeIdDecls) { | |
| 1997 | 1995 | ScopeDecls *decls_scope = (ScopeDecls *)scope; |
| 1998 | 1996 | auto entry = decls_scope->decl_table.maybe_get(name); |
| 1999 | 1997 | if (entry) |
| ... | ... | @@ -2006,15 +2004,11 @@ Tld *find_decl(Scope *scope, Buf *name) { |
| 2006 | 2004 | |
| 2007 | 2005 | VariableTableEntry *find_variable(CodeGen *g, Scope *scope, Buf *name) { |
| 2008 | 2006 | while (scope) { |
| 2009 | if (scope->node->type == NodeTypeVariableDeclaration || | |
| 2010 | scope->node->type == NodeTypeParamDecl) | |
| 2011 | { | |
| 2007 | if (scope->id == ScopeIdVarDecl) { | |
| 2012 | 2008 | ScopeVarDecl *var_scope = (ScopeVarDecl *)scope; |
| 2013 | 2009 | if (buf_eql_buf(name, &var_scope->var->name)) |
| 2014 | 2010 | return var_scope->var; |
| 2015 | } else if (scope->node->type == NodeTypeRoot || | |
| 2016 | scope->node->type == NodeTypeContainerDecl) | |
| 2017 | { | |
| 2011 | } else if (scope->id == ScopeIdDecls) { | |
| 2018 | 2012 | ScopeDecls *decls_scope = (ScopeDecls *)scope; |
| 2019 | 2013 | auto entry = decls_scope->decl_table.maybe_get(name); |
| 2020 | 2014 | if (entry) { |
| ... | ... | @@ -2034,7 +2028,7 @@ VariableTableEntry *find_variable(CodeGen *g, Scope *scope, Buf *name) { |
| 2034 | 2028 | |
| 2035 | 2029 | FnTableEntry *scope_fn_entry(Scope *scope) { |
| 2036 | 2030 | while (scope) { |
| 2037 | if (scope->node->type == NodeTypeFnDef) { | |
| 2031 | if (scope->id == ScopeIdFnDef) { | |
| 2038 | 2032 | ScopeFnDef *fn_scope = (ScopeFnDef *)scope; |
| 2039 | 2033 | return fn_scope->fn_entry; |
| 2040 | 2034 | } |
src/ast_render.cpp+58-7| ... | ... | @@ -364,6 +364,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 364 | 364 | case NodeTypeSwitchProng: |
| 365 | 365 | case NodeTypeSwitchRange: |
| 366 | 366 | case NodeTypeLabel: |
| 367 | case NodeTypeStructValueField: | |
| 367 | 368 | zig_unreachable(); |
| 368 | 369 | case NodeTypeRoot: |
| 369 | 370 | for (size_t i = 0; i < node->data.root.top_level_decls.length; i += 1) { |
| ... | ... | @@ -602,9 +603,30 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 602 | 603 | } |
| 603 | 604 | case NodeTypeContainerInitExpr: |
| 604 | 605 | render_node_ungrouped(ar, node->data.container_init_expr.type); |
| 605 | fprintf(ar->f, "{"); | |
| 606 | assert(node->data.container_init_expr.entries.length == 0); | |
| 606 | if (node->data.container_init_expr.kind == ContainerInitKindStruct) { | |
| 607 | fprintf(ar->f, "{\n"); | |
| 608 | ar->indent += ar->indent_size; | |
| 609 | } else { | |
| 610 | fprintf(ar->f, "{"); | |
| 611 | } | |
| 612 | for (size_t i = 0; i < node->data.container_init_expr.entries.length; i += 1) { | |
| 613 | AstNode *entry = node->data.container_init_expr.entries.at(i); | |
| 614 | if (entry->type == NodeTypeStructValueField) { | |
| 615 | Buf *name = entry->data.struct_val_field.name; | |
| 616 | AstNode *expr = entry->data.struct_val_field.expr; | |
| 617 | fprintf(ar->f, ".%s = ", buf_ptr(name)); | |
| 618 | render_node_grouped(ar, expr); | |
| 619 | fprintf(ar->f, ",\n"); | |
| 620 | } else { | |
| 621 | if (i != 0) | |
| 622 | fprintf(ar->f, ", "); | |
| 623 | render_node_grouped(ar, entry); | |
| 624 | } | |
| 625 | } | |
| 607 | 626 | fprintf(ar->f, "}"); |
| 627 | if (node->data.container_init_expr.kind == ContainerInitKindStruct) { | |
| 628 | ar->indent -= ar->indent_size; | |
| 629 | } | |
| 608 | 630 | break; |
| 609 | 631 | case NodeTypeArrayType: |
| 610 | 632 | { |
| ... | ... | @@ -788,7 +810,40 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 788 | 810 | } |
| 789 | 811 | case NodeTypeGoto: |
| 790 | 812 | { |
| 791 | fprintf(ar->f, "goto %s", buf_ptr(node->data.goto_expr.name)); | |
| 813 | const char *inline_str = node->data.goto_expr.is_inline ? "inline " : ""; | |
| 814 | fprintf(ar->f, "%sgoto %s", inline_str, buf_ptr(node->data.goto_expr.name)); | |
| 815 | break; | |
| 816 | } | |
| 817 | case NodeTypeForExpr: | |
| 818 | { | |
| 819 | const char *inline_str = node->data.for_expr.is_inline ? "inline " : ""; | |
| 820 | fprintf(ar->f, "%sfor (", inline_str); | |
| 821 | render_node_grouped(ar, node->data.for_expr.array_expr); | |
| 822 | fprintf(ar->f, ") "); | |
| 823 | if (node->data.for_expr.elem_node) { | |
| 824 | fprintf(ar->f, "|"); | |
| 825 | if (node->data.for_expr.elem_is_ptr) | |
| 826 | fprintf(ar->f, "*"); | |
| 827 | render_node_grouped(ar, node->data.for_expr.elem_node); | |
| 828 | if (node->data.for_expr.index_node) { | |
| 829 | fprintf(ar->f, ", "); | |
| 830 | render_node_grouped(ar, node->data.for_expr.index_node); | |
| 831 | } | |
| 832 | fprintf(ar->f, "| "); | |
| 833 | } | |
| 834 | render_node_grouped(ar, node->data.for_expr.body); | |
| 835 | break; | |
| 836 | } | |
| 837 | case NodeTypeBreak: | |
| 838 | { | |
| 839 | const char *inline_str = node->data.break_expr.is_inline ? "inline " : ""; | |
| 840 | fprintf(ar->f, "%sbreak", inline_str); | |
| 841 | break; | |
| 842 | } | |
| 843 | case NodeTypeContinue: | |
| 844 | { | |
| 845 | const char *inline_str = node->data.continue_expr.is_inline ? "inline " : ""; | |
| 846 | fprintf(ar->f, "%scontinue", inline_str); | |
| 792 | 847 | break; |
| 793 | 848 | } |
| 794 | 849 | case NodeTypeFnDecl: |
| ... | ... | @@ -797,12 +852,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 797 | 852 | case NodeTypeUnwrapErrorExpr: |
| 798 | 853 | case NodeTypeSliceExpr: |
| 799 | 854 | case NodeTypeStructField: |
| 800 | case NodeTypeStructValueField: | |
| 801 | 855 | case NodeTypeUse: |
| 802 | 856 | case NodeTypeZeroesLiteral: |
| 803 | case NodeTypeForExpr: | |
| 804 | case NodeTypeBreak: | |
| 805 | case NodeTypeContinue: | |
| 806 | 857 | zig_panic("TODO more ast rendering"); |
| 807 | 858 | } |
| 808 | 859 | } |
src/codegen.cpp+50-35| ... | ... | @@ -277,40 +277,55 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { |
| 277 | 277 | if (scope->di_scope) |
| 278 | 278 | return scope->di_scope; |
| 279 | 279 | |
| 280 | if (scope->node->type == NodeTypeFnDef) { | |
| 281 | assert(scope->parent); | |
| 282 | ScopeFnDef *fn_scope = (ScopeFnDef *)scope; | |
| 283 | FnTableEntry *fn_table_entry = fn_scope->fn_entry; | |
| 284 | unsigned line_number = fn_table_entry->proto_node->line + 1; | |
| 285 | unsigned scope_line = line_number; | |
| 286 | bool is_definition = fn_table_entry->fn_def_node != nullptr; | |
| 287 | unsigned flags = 0; | |
| 288 | bool is_optimized = g->is_release_build; | |
| 289 | ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder, | |
| 290 | get_di_scope(g, scope->parent), buf_ptr(&fn_table_entry->symbol_name), "", | |
| 291 | scope->node->owner->di_file, line_number, | |
| 292 | fn_table_entry->type_entry->di_type, fn_table_entry->internal_linkage, | |
| 293 | is_definition, scope_line, flags, is_optimized, nullptr); | |
| 294 | ||
| 295 | scope->di_scope = ZigLLVMSubprogramToScope(subprogram); | |
| 296 | ZigLLVMFnSetSubprogram(fn_llvm_value(g, fn_table_entry), subprogram); | |
| 297 | } else if (scope->node->type == NodeTypeRoot) { | |
| 298 | scope->di_scope = ZigLLVMFileToScope(scope->node->owner->di_file); | |
| 299 | } else if (scope->node->type == NodeTypeContainerDecl) { | |
| 300 | ScopeDecls *decls_scope = (ScopeDecls *)scope; | |
| 301 | assert(decls_scope->container_type); | |
| 302 | scope->di_scope = ZigLLVMTypeToScope(decls_scope->container_type->di_type); | |
| 303 | } else { | |
| 304 | assert(scope->parent); | |
| 305 | ZigLLVMDILexicalBlock *di_block = ZigLLVMCreateLexicalBlock(g->dbuilder, | |
| 306 | get_di_scope(g, scope->parent), | |
| 307 | scope->node->owner->di_file, | |
| 308 | scope->node->line + 1, | |
| 309 | scope->node->column + 1); | |
| 310 | scope->di_scope = ZigLLVMLexicalBlockToScope(di_block); | |
| 280 | ImportTableEntry *import = get_scope_import(scope); | |
| 281 | switch (scope->id) { | |
| 282 | case ScopeIdCImport: | |
| 283 | zig_unreachable(); | |
| 284 | case ScopeIdFnDef: | |
| 285 | { | |
| 286 | assert(scope->parent); | |
| 287 | ScopeFnDef *fn_scope = (ScopeFnDef *)scope; | |
| 288 | FnTableEntry *fn_table_entry = fn_scope->fn_entry; | |
| 289 | unsigned line_number = fn_table_entry->proto_node->line + 1; | |
| 290 | unsigned scope_line = line_number; | |
| 291 | bool is_definition = fn_table_entry->fn_def_node != nullptr; | |
| 292 | unsigned flags = 0; | |
| 293 | bool is_optimized = g->is_release_build; | |
| 294 | ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder, | |
| 295 | get_di_scope(g, scope->parent), buf_ptr(&fn_table_entry->symbol_name), "", | |
| 296 | import->di_file, line_number, | |
| 297 | fn_table_entry->type_entry->di_type, fn_table_entry->internal_linkage, | |
| 298 | is_definition, scope_line, flags, is_optimized, nullptr); | |
| 299 | ||
| 300 | scope->di_scope = ZigLLVMSubprogramToScope(subprogram); | |
| 301 | ZigLLVMFnSetSubprogram(fn_llvm_value(g, fn_table_entry), subprogram); | |
| 302 | return scope->di_scope; | |
| 303 | } | |
| 304 | case ScopeIdDecls: | |
| 305 | if (scope->parent) { | |
| 306 | ScopeDecls *decls_scope = (ScopeDecls *)scope; | |
| 307 | assert(decls_scope->container_type); | |
| 308 | scope->di_scope = ZigLLVMTypeToScope(decls_scope->container_type->di_type); | |
| 309 | } else { | |
| 310 | scope->di_scope = ZigLLVMFileToScope(import->di_file); | |
| 311 | } | |
| 312 | return scope->di_scope; | |
| 313 | case ScopeIdBlock: | |
| 314 | case ScopeIdDefer: | |
| 315 | case ScopeIdVarDecl: | |
| 316 | case ScopeIdLoop: | |
| 317 | { | |
| 318 | assert(scope->parent); | |
| 319 | ZigLLVMDILexicalBlock *di_block = ZigLLVMCreateLexicalBlock(g->dbuilder, | |
| 320 | get_di_scope(g, scope->parent), | |
| 321 | import->di_file, | |
| 322 | scope->source_node->line + 1, | |
| 323 | scope->source_node->column + 1); | |
| 324 | scope->di_scope = ZigLLVMLexicalBlockToScope(di_block); | |
| 325 | return scope->di_scope; | |
| 326 | } | |
| 311 | 327 | } |
| 312 | ||
| 313 | return scope->di_scope; | |
| 328 | zig_unreachable(); | |
| 314 | 329 | } |
| 315 | 330 | |
| 316 | 331 | static void clear_debug_source_node(CodeGen *g) { |
| ... | ... | @@ -400,11 +415,11 @@ static bool ir_want_debug_safety(CodeGen *g, IrInstruction *instruction) { |
| 400 | 415 | // TODO memoize |
| 401 | 416 | Scope *scope = instruction->scope; |
| 402 | 417 | while (scope) { |
| 403 | if (scope->node->type == NodeTypeBlock) { | |
| 418 | if (scope->id == ScopeIdBlock) { | |
| 404 | 419 | ScopeBlock *block_scope = (ScopeBlock *)scope; |
| 405 | 420 | if (block_scope->safety_set_node) |
| 406 | 421 | return !block_scope->safety_off; |
| 407 | } else if (scope->node->type == NodeTypeRoot || scope->node->type == NodeTypeContainerDecl) { | |
| 422 | } else if (scope->id == ScopeIdDecls) { | |
| 408 | 423 | ScopeDecls *decls_scope = (ScopeDecls *)scope; |
| 409 | 424 | if (decls_scope->safety_set_node) |
| 410 | 425 | return !decls_scope->safety_off; |
src/ir.cpp+73-126| ... | ... | @@ -18,12 +18,17 @@ struct IrExecContext { |
| 18 | 18 | size_t mem_slot_count; |
| 19 | 19 | }; |
| 20 | 20 | |
| 21 | struct LoopStackItem { | |
| 22 | IrBasicBlock *break_block; | |
| 23 | IrBasicBlock *continue_block; | |
| 24 | bool is_inline; | |
| 25 | }; | |
| 26 | ||
| 21 | 27 | struct IrBuilder { |
| 22 | 28 | CodeGen *codegen; |
| 23 | 29 | IrExecutable *exec; |
| 24 | 30 | IrBasicBlock *current_basic_block; |
| 25 | ZigList<IrBasicBlock *> break_block_stack; | |
| 26 | ZigList<IrBasicBlock *> continue_block_stack; | |
| 31 | ZigList<LoopStackItem> loop_stack; | |
| 27 | 32 | }; |
| 28 | 33 | |
| 29 | 34 | struct IrAnalyze { |
| ... | ... | @@ -1241,13 +1246,17 @@ static void ir_gen_defers_for_block(IrBuilder *irb, Scope *parent_scope, Scope * |
| 1241 | 1246 | bool gen_error_defers, bool gen_maybe_defers) |
| 1242 | 1247 | { |
| 1243 | 1248 | while (inner_scope != outer_scope) { |
| 1244 | if (inner_scope->node->type == NodeTypeDefer && | |
| 1245 | ((inner_scope->node->data.defer.kind == ReturnKindUnconditional) || | |
| 1246 | (gen_error_defers && inner_scope->node->data.defer.kind == ReturnKindError) || | |
| 1247 | (gen_maybe_defers && inner_scope->node->data.defer.kind == ReturnKindMaybe))) | |
| 1248 | { | |
| 1249 | AstNode *defer_expr_node = inner_scope->node->data.defer.expr; | |
| 1250 | ir_gen_node(irb, defer_expr_node, parent_scope); | |
| 1249 | if (inner_scope->id == ScopeIdDefer) { | |
| 1250 | assert(inner_scope->source_node->type == NodeTypeDefer); | |
| 1251 | ReturnKind defer_kind = inner_scope->source_node->data.defer.kind; | |
| 1252 | if (defer_kind == ReturnKindUnconditional || | |
| 1253 | (gen_error_defers && defer_kind == ReturnKindError) || | |
| 1254 | (gen_maybe_defers && defer_kind == ReturnKindMaybe)) | |
| 1255 | { | |
| 1256 | AstNode *defer_expr_node = inner_scope->source_node->data.defer.expr; | |
| 1257 | ir_gen_node(irb, defer_expr_node, parent_scope); | |
| 1258 | } | |
| 1259 | ||
| 1251 | 1260 | } |
| 1252 | 1261 | inner_scope = inner_scope->parent; |
| 1253 | 1262 | } |
| ... | ... | @@ -2070,11 +2079,12 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n |
| 2070 | 2079 | |
| 2071 | 2080 | ir_set_cursor_at_end(irb, body_block); |
| 2072 | 2081 | |
| 2073 | irb->break_block_stack.append(end_block); | |
| 2074 | irb->continue_block_stack.append(continue_block); | |
| 2082 | LoopStackItem *loop_stack_item = irb->loop_stack.add_one(); | |
| 2083 | loop_stack_item->break_block = end_block; | |
| 2084 | loop_stack_item->continue_block = continue_block; | |
| 2085 | loop_stack_item->is_inline = is_inline; | |
| 2075 | 2086 | ir_gen_node(irb, node->data.while_expr.body, scope); |
| 2076 | irb->break_block_stack.pop(); | |
| 2077 | irb->continue_block_stack.pop(); | |
| 2087 | irb->loop_stack.pop(); | |
| 2078 | 2088 | |
| 2079 | 2089 | ir_build_br(irb, scope, node, continue_block, is_inline); |
| 2080 | 2090 | ir_set_cursor_at_end(irb, end_block); |
| ... | ... | @@ -2096,9 +2106,11 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo |
| 2096 | 2106 | } |
| 2097 | 2107 | assert(elem_node->type == NodeTypeSymbol); |
| 2098 | 2108 | |
| 2099 | IrInstruction *array_val = ir_gen_node(irb, array_node, parent_scope); | |
| 2100 | if (array_val == irb->codegen->invalid_instruction) | |
| 2101 | return array_val; | |
| 2109 | IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPurposeAddressOf); | |
| 2110 | if (array_val_ptr == irb->codegen->invalid_instruction) | |
| 2111 | return array_val_ptr; | |
| 2112 | ||
| 2113 | IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr); | |
| 2102 | 2114 | |
| 2103 | 2115 | IrInstruction *array_type = ir_build_typeof(irb, parent_scope, array_node, array_val); |
| 2104 | 2116 | IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_type); |
| ... | ... | @@ -2155,7 +2167,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo |
| 2155 | 2167 | ir_build_cond_br(irb, child_scope, node, cond, body_block, end_block, is_inline); |
| 2156 | 2168 | |
| 2157 | 2169 | ir_set_cursor_at_end(irb, body_block); |
| 2158 | IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val, index_val, true); | |
| 2170 | IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, true); | |
| 2159 | 2171 | IrInstruction *elem_val; |
| 2160 | 2172 | if (node->data.for_expr.elem_is_ptr) { |
| 2161 | 2173 | elem_val = elem_ptr; |
| ... | ... | @@ -2164,11 +2176,12 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo |
| 2164 | 2176 | } |
| 2165 | 2177 | ir_build_store_ptr(irb, child_scope, node, elem_var_ptr, elem_val); |
| 2166 | 2178 | |
| 2167 | irb->break_block_stack.append(end_block); | |
| 2168 | irb->continue_block_stack.append(continue_block); | |
| 2179 | LoopStackItem *loop_stack_item = irb->loop_stack.add_one(); | |
| 2180 | loop_stack_item->break_block = end_block; | |
| 2181 | loop_stack_item->continue_block = continue_block; | |
| 2182 | loop_stack_item->is_inline = is_inline; | |
| 2169 | 2183 | ir_gen_node(irb, body_node, child_scope); |
| 2170 | irb->break_block_stack.pop(); | |
| 2171 | irb->continue_block_stack.pop(); | |
| 2184 | irb->loop_stack.pop(); | |
| 2172 | 2185 | |
| 2173 | 2186 | ir_build_br(irb, child_scope, node, continue_block, is_inline); |
| 2174 | 2187 | |
| ... | ... | @@ -2195,14 +2208,14 @@ static IrInstruction *ir_gen_this_literal(IrBuilder *irb, Scope *scope, AstNode |
| 2195 | 2208 | return ir_build_const_fn(irb, scope, node, fn_entry); |
| 2196 | 2209 | } |
| 2197 | 2210 | |
| 2198 | if (scope->node->type == NodeTypeContainerDecl) { | |
| 2211 | if (scope->id == ScopeIdDecls) { | |
| 2199 | 2212 | ScopeDecls *decls_scope = (ScopeDecls *)scope; |
| 2200 | 2213 | TypeTableEntry *container_type = decls_scope->container_type; |
| 2201 | 2214 | assert(container_type); |
| 2202 | 2215 | return ir_build_const_type(irb, scope, node, container_type); |
| 2203 | 2216 | } |
| 2204 | 2217 | |
| 2205 | if (scope->node->type == NodeTypeBlock) | |
| 2218 | if (scope->id == ScopeIdBlock) | |
| 2206 | 2219 | return ir_build_const_scope(irb, scope, node, scope); |
| 2207 | 2220 | |
| 2208 | 2221 | zig_unreachable(); |
| ... | ... | @@ -2246,8 +2259,7 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n |
| 2246 | 2259 | |
| 2247 | 2260 | return ir_build_array_type(irb, scope, node, size_value, child_type); |
| 2248 | 2261 | } else { |
| 2249 | IrInstruction *child_type = ir_gen_node_extra(irb, child_type_node, | |
| 2250 | scope, LValPurposeAddressOf); | |
| 2262 | IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope); | |
| 2251 | 2263 | if (child_type == irb->codegen->invalid_instruction) |
| 2252 | 2264 | return child_type; |
| 2253 | 2265 | |
| ... | ... | @@ -2575,7 +2587,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 2575 | 2587 | |
| 2576 | 2588 | static LabelTableEntry *find_label(IrExecutable *exec, Scope *scope, Buf *name) { |
| 2577 | 2589 | while (scope) { |
| 2578 | if (scope->node->type == NodeTypeBlock) { | |
| 2590 | if (scope->id == ScopeIdBlock) { | |
| 2579 | 2591 | ScopeBlock *block_scope = (ScopeBlock *)scope; |
| 2580 | 2592 | auto entry = block_scope->label_table.maybe_get(name); |
| 2581 | 2593 | if (entry) |
| ... | ... | @@ -2589,7 +2601,7 @@ static LabelTableEntry *find_label(IrExecutable *exec, Scope *scope, Buf *name) |
| 2589 | 2601 | |
| 2590 | 2602 | static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) { |
| 2591 | 2603 | while (scope) { |
| 2592 | if (scope->node->type == NodeTypeBlock) | |
| 2604 | if (scope->id == ScopeIdBlock) | |
| 2593 | 2605 | return (ScopeBlock *)scope; |
| 2594 | 2606 | scope = scope->parent; |
| 2595 | 2607 | } |
| ... | ... | @@ -2637,6 +2649,36 @@ static IrInstruction *ir_gen_goto(IrBuilder *irb, Scope *scope, AstNode *node) { |
| 2637 | 2649 | return ir_build_unreachable(irb, scope, node); |
| 2638 | 2650 | } |
| 2639 | 2651 | |
| 2652 | static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *scope, AstNode *node) { | |
| 2653 | assert(node->type == NodeTypeBreak); | |
| 2654 | ||
| 2655 | if (irb->loop_stack.length == 0) { | |
| 2656 | add_node_error(irb->codegen, node, | |
| 2657 | buf_sprintf("'break' expression outside loop")); | |
| 2658 | return irb->codegen->invalid_instruction; | |
| 2659 | } | |
| 2660 | ||
| 2661 | bool is_inline = ir_should_inline(irb) || node->data.break_expr.is_inline; | |
| 2662 | LoopStackItem *loop_stack_item = &irb->loop_stack.last(); | |
| 2663 | IrBasicBlock *dest_block = loop_stack_item->break_block; | |
| 2664 | return ir_build_br(irb, scope, node, dest_block, is_inline); | |
| 2665 | } | |
| 2666 | ||
| 2667 | static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *scope, AstNode *node) { | |
| 2668 | assert(node->type == NodeTypeContinue); | |
| 2669 | ||
| 2670 | if (irb->loop_stack.length == 0) { | |
| 2671 | add_node_error(irb->codegen, node, | |
| 2672 | buf_sprintf("'continue' expression outside loop")); | |
| 2673 | return irb->codegen->invalid_instruction; | |
| 2674 | } | |
| 2675 | ||
| 2676 | bool is_inline = ir_should_inline(irb) || node->data.continue_expr.is_inline; | |
| 2677 | LoopStackItem *loop_stack_item = &irb->loop_stack.last(); | |
| 2678 | IrBasicBlock *dest_block = loop_stack_item->continue_block; | |
| 2679 | return ir_build_br(irb, scope, node, dest_block, is_inline); | |
| 2680 | } | |
| 2681 | ||
| 2640 | 2682 | static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LValPurpose lval) { |
| 2641 | 2683 | if (lval == LValPurposeNone) |
| 2642 | 2684 | return value; |
| ... | ... | @@ -2712,11 +2754,13 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop |
| 2712 | 2754 | return ir_lval_wrap(irb, scope, ir_gen_goto(irb, scope, node), lval); |
| 2713 | 2755 | case NodeTypeTypeLiteral: |
| 2714 | 2756 | return ir_lval_wrap(irb, scope, ir_gen_type_literal(irb, scope, node), lval); |
| 2757 | case NodeTypeBreak: | |
| 2758 | return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval); | |
| 2759 | case NodeTypeContinue: | |
| 2760 | return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval); | |
| 2715 | 2761 | case NodeTypeUnwrapErrorExpr: |
| 2716 | 2762 | case NodeTypeDefer: |
| 2717 | 2763 | case NodeTypeSliceExpr: |
| 2718 | case NodeTypeBreak: | |
| 2719 | case NodeTypeContinue: | |
| 2720 | 2764 | case NodeTypeCharLiteral: |
| 2721 | 2765 | case NodeTypeZeroesLiteral: |
| 2722 | 2766 | case NodeTypeErrorType: |
| ... | ... | @@ -7704,87 +7748,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 7704 | 7748 | // } |
| 7705 | 7749 | //} |
| 7706 | 7750 | // |
| 7707 | //static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 7708 | // TypeTableEntry *expected_type, AstNode *node) | |
| 7709 | //{ | |
| 7710 | // assert(node->type == NodeTypeWhileExpr); | |
| 7711 | // | |
| 7712 | // AstNode **condition_node = &node->data.while_expr.condition; | |
| 7713 | // AstNode *while_body_node = node->data.while_expr.body; | |
| 7714 | // AstNode **continue_expr_node = &node->data.while_expr.continue_expr; | |
| 7715 | // | |
| 7716 | // TypeTableEntry *condition_type = analyze_expression(g, import, context, | |
| 7717 | // g->builtin_types.entry_bool, *condition_node); | |
| 7718 | // | |
| 7719 | // if (*continue_expr_node) { | |
| 7720 | // analyze_expression(g, import, context, g->builtin_types.entry_void, *continue_expr_node); | |
| 7721 | // } | |
| 7722 | // | |
| 7723 | // BlockContext *child_context = new_block_context(node, context); | |
| 7724 | // child_context->parent_loop_node = node; | |
| 7725 | // | |
| 7726 | // analyze_expression(g, import, child_context, g->builtin_types.entry_void, while_body_node); | |
| 7727 | // | |
| 7728 | // | |
| 7729 | // TypeTableEntry *expr_return_type = g->builtin_types.entry_void; | |
| 7730 | // | |
| 7731 | // if (condition_type->id == TypeTableEntryIdInvalid) { | |
| 7732 | // expr_return_type = g->builtin_types.entry_invalid; | |
| 7733 | // } else { | |
| 7734 | // // if the condition is a simple constant expression and there are no break statements | |
| 7735 | // // then the return type is unreachable | |
| 7736 | // ConstExprValue *const_val = &get_resolved_expr(*condition_node)->const_val; | |
| 7737 | // if (const_val->ok) { | |
| 7738 | // if (const_val->data.x_bool) { | |
| 7739 | // node->data.while_expr.condition_always_true = true; | |
| 7740 | // if (!node->data.while_expr.contains_break) { | |
| 7741 | // expr_return_type = g->builtin_types.entry_unreachable; | |
| 7742 | // } | |
| 7743 | // } | |
| 7744 | // } | |
| 7745 | // } | |
| 7746 | // | |
| 7747 | // return expr_return_type; | |
| 7748 | //} | |
| 7749 | // | |
| 7750 | //static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 7751 | // TypeTableEntry *expected_type, AstNode *node) | |
| 7752 | //{ | |
| 7753 | // assert(node->type == NodeTypeBreak); | |
| 7754 | // | |
| 7755 | // AstNode *loop_node = context->parent_loop_node; | |
| 7756 | // if (loop_node) { | |
| 7757 | // if (loop_node->type == NodeTypeWhileExpr) { | |
| 7758 | // loop_node->data.while_expr.contains_break = true; | |
| 7759 | // } else if (loop_node->type == NodeTypeForExpr) { | |
| 7760 | // loop_node->data.for_expr.contains_break = true; | |
| 7761 | // } else { | |
| 7762 | // zig_unreachable(); | |
| 7763 | // } | |
| 7764 | // } else { | |
| 7765 | // add_node_error(g, node, buf_sprintf("'break' expression outside loop")); | |
| 7766 | // } | |
| 7767 | // return g->builtin_types.entry_unreachable; | |
| 7768 | //} | |
| 7769 | // | |
| 7770 | //static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 7771 | // TypeTableEntry *expected_type, AstNode *node) | |
| 7772 | //{ | |
| 7773 | // AstNode *loop_node = context->parent_loop_node; | |
| 7774 | // if (loop_node) { | |
| 7775 | // if (loop_node->type == NodeTypeWhileExpr) { | |
| 7776 | // loop_node->data.while_expr.contains_continue = true; | |
| 7777 | // } else if (loop_node->type == NodeTypeForExpr) { | |
| 7778 | // loop_node->data.for_expr.contains_continue = true; | |
| 7779 | // } else { | |
| 7780 | // zig_unreachable(); | |
| 7781 | // } | |
| 7782 | // } else { | |
| 7783 | // add_node_error(g, node, buf_sprintf("'continue' expression outside loop")); | |
| 7784 | // } | |
| 7785 | // return g->builtin_types.entry_unreachable; | |
| 7786 | //} | |
| 7787 | // | |
| 7788 | 7751 | //static TypeTableEntry *analyze_defer(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context, |
| 7789 | 7752 | // TypeTableEntry *expected_type, AstNode *node) |
| 7790 | 7753 | //{ |
| ... | ... | @@ -8592,22 +8555,6 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 8592 | 8555 | // } |
| 8593 | 8556 | //} |
| 8594 | 8557 | // |
| 8595 | //static LLVMValueRef gen_break(CodeGen *g, AstNode *node) { | |
| 8596 | // assert(node->type == NodeTypeBreak); | |
| 8597 | // LLVMBasicBlockRef dest_block = g->break_block_stack.last(); | |
| 8598 | // | |
| 8599 | // set_debug_source_node(g, node); | |
| 8600 | // return LLVMBuildBr(g->builder, dest_block); | |
| 8601 | //} | |
| 8602 | ||
| 8603 | //static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) { | |
| 8604 | // assert(node->type == NodeTypeContinue); | |
| 8605 | // LLVMBasicBlockRef dest_block = g->continue_block_stack.last(); | |
| 8606 | // | |
| 8607 | // set_debug_source_node(g, node); | |
| 8608 | // return LLVMBuildBr(g->builder, dest_block); | |
| 8609 | //} | |
| 8610 | // | |
| 8611 | 8558 | //static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl, |
| 8612 | 8559 | // bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type, bool var_is_ptr) |
| 8613 | 8560 | //{ |
src/ir_print.cpp+1-1| ... | ... | @@ -104,7 +104,7 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const |
| 104 | 104 | } |
| 105 | 105 | case TypeTableEntryIdBlock: |
| 106 | 106 | { |
| 107 | AstNode *node = const_val->data.x_block->node; | |
| 107 | AstNode *node = const_val->data.x_block->source_node; | |
| 108 | 108 | fprintf(irp->f, "(scope:%zu:%zu)", node->line + 1, node->column + 1); |
| 109 | 109 | return; |
| 110 | 110 | } |
test/self_hosted2.zig+15| ... | ... | @@ -139,6 +139,20 @@ fn testFnWithInlineArgs() { |
| 139 | 139 | } |
| 140 | 140 | |
| 141 | 141 | |
| 142 | fn testContinueInForLoop() { | |
| 143 | const array = []i32 {1, 2, 3, 4, 5}; | |
| 144 | var sum : i32 = 0; | |
| 145 | for (array) |x| { | |
| 146 | sum += x; | |
| 147 | if (x < 3) { | |
| 148 | continue; | |
| 149 | } | |
| 150 | break; | |
| 151 | } | |
| 152 | assert(sum == 6); | |
| 153 | } | |
| 154 | ||
| 155 | ||
| 142 | 156 | fn assert(ok: bool) { |
| 143 | 157 | if (!ok) |
| 144 | 158 | @unreachable(); |
| ... | ... | @@ -158,6 +172,7 @@ fn runAllTests() { |
| 158 | 172 | testCompileTimeFib(); |
| 159 | 173 | testCompileTimeGenericEval(); |
| 160 | 174 | testFnWithInlineArgs(); |
| 175 | testContinueInForLoop(); | |
| 161 | 176 | } |
| 162 | 177 | |
| 163 | 178 | export nakedcc fn _start() -> unreachable { |