| author | |
| committer | |
| log | e8550814c5e4387e8b758736a1254a5c3b32674e |
| tree | e1f669d947987121be234fdd6bb7ccc55e4d7b2f |
| parent | 6e0c3dc173507b92fc04515659454044f918efbb |
4 files changed, 85 insertions(+), 38 deletions(-)
example/arrays/arrays.zig+2-4| ... | ... | @@ -9,9 +9,7 @@ extern { |
| 9 | 9 | export fn _start() -> unreachable { |
| 10 | 10 | let mut array : [i32; 10]; |
| 11 | 11 | |
| 12 | exit(array[1]); | |
| 13 | ||
| 14 | //array[4] = array[1] + 5; | |
| 15 | ||
| 12 | array[4] = array[1] + 5; | |
| 16 | 13 | |
| 14 | exit(0); | |
| 17 | 15 | } |
src/analyze.cpp+40-22| ... | ... | @@ -10,6 +10,9 @@ |
| 10 | 10 | #include "zig_llvm.hpp" |
| 11 | 11 | #include "os.hpp" |
| 12 | 12 | |
| 13 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 14 | TypeTableEntry *expected_type, AstNode *node); | |
| 15 | ||
| 13 | 16 | static AstNode *first_executing_node(AstNode *node) { |
| 14 | 17 | switch (node->type) { |
| 15 | 18 | case NodeTypeFnCallExpr: |
| ... | ... | @@ -476,6 +479,35 @@ LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) { |
| 476 | 479 | } |
| 477 | 480 | } |
| 478 | 481 | |
| 482 | static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 483 | AstNode *node) | |
| 484 | { | |
| 485 | TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, | |
| 486 | node->data.array_access_expr.array_ref_expr); | |
| 487 | ||
| 488 | TypeTableEntry *return_type; | |
| 489 | ||
| 490 | if (array_type->id == TypeTableEntryIdArray) { | |
| 491 | return_type = array_type->data.array.child_type; | |
| 492 | } else { | |
| 493 | if (array_type->id != TypeTableEntryIdInvalid) { | |
| 494 | add_node_error(g, node, buf_sprintf("array access of non-array")); | |
| 495 | } | |
| 496 | return_type = g->builtin_types.entry_invalid; | |
| 497 | } | |
| 498 | ||
| 499 | TypeTableEntry *subscript_type = analyze_expression(g, import, context, nullptr, | |
| 500 | node->data.array_access_expr.subscript); | |
| 501 | if (subscript_type->id != TypeTableEntryIdInt && | |
| 502 | subscript_type->id != TypeTableEntryIdInvalid) | |
| 503 | { | |
| 504 | add_node_error(g, node, | |
| 505 | buf_sprintf("array subscripts must be integers")); | |
| 506 | } | |
| 507 | ||
| 508 | return return_type; | |
| 509 | } | |
| 510 | ||
| 479 | 511 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 480 | 512 | TypeTableEntry *expected_type, AstNode *node) |
| 481 | 513 | { |
| ... | ... | @@ -593,6 +625,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 593 | 625 | case BinOpTypeAssign: |
| 594 | 626 | { |
| 595 | 627 | AstNode *lhs_node = node->data.bin_op_expr.op1; |
| 628 | TypeTableEntry *expected_rhs_type = nullptr; | |
| 596 | 629 | if (lhs_node->type == NodeTypeSymbol) { |
| 597 | 630 | Buf *name = &lhs_node->data.symbol; |
| 598 | 631 | LocalVariableTableEntry *var = find_local_variable(context, name); |
| ... | ... | @@ -601,18 +634,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 601 | 634 | add_node_error(g, lhs_node, |
| 602 | 635 | buf_sprintf("cannot assign to constant variable")); |
| 603 | 636 | } else { |
| 604 | analyze_expression(g, import, context, var->type, | |
| 605 | node->data.bin_op_expr.op2); | |
| 637 | expected_rhs_type = var->type; | |
| 606 | 638 | } |
| 607 | 639 | } else { |
| 608 | 640 | add_node_error(g, lhs_node, |
| 609 | 641 | buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); |
| 610 | 642 | } |
| 611 | ||
| 643 | } else if (lhs_node->type == NodeTypeArrayAccessExpr) { | |
| 644 | expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node); | |
| 612 | 645 | } else { |
| 613 | 646 | add_node_error(g, lhs_node, |
| 614 | 647 | buf_sprintf("expected a bare identifier")); |
| 615 | 648 | } |
| 649 | analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); | |
| 616 | 650 | return_type = g->builtin_types.entry_void; |
| 617 | 651 | break; |
| 618 | 652 | } |
| ... | ... | @@ -736,25 +770,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 736 | 770 | } |
| 737 | 771 | |
| 738 | 772 | case NodeTypeArrayAccessExpr: |
| 739 | { | |
| 740 | // here we are always reading the array | |
| 741 | TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, | |
| 742 | node->data.array_access_expr.array_ref_expr); | |
| 743 | if (array_type->id == TypeTableEntryIdArray) { | |
| 744 | TypeTableEntry *subscript_type = analyze_expression(g, import, context, | |
| 745 | nullptr, node->data.array_access_expr.subscript); | |
| 746 | if (subscript_type->id != TypeTableEntryIdInt) { | |
| 747 | add_node_error(g, node, | |
| 748 | buf_sprintf("array subscripts must be integers")); | |
| 749 | } | |
| 750 | return_type = array_type->data.array.child_type; | |
| 751 | } else { | |
| 752 | add_node_error(g, node, buf_sprintf("array access of non-array")); | |
| 753 | return_type = g->builtin_types.entry_invalid; | |
| 754 | } | |
| 755 | ||
| 756 | break; | |
| 757 | } | |
| 773 | // for reading array access; assignment handled elsewhere | |
| 774 | return_type = analyze_array_access_expr(g, import, context, node); | |
| 775 | break; | |
| 758 | 776 | case NodeTypeNumberLiteral: |
| 759 | 777 | // TODO: generic literal int type |
| 760 | 778 | return_type = g->builtin_types.entry_i32; |
src/codegen.cpp+28-12| ... | ... | @@ -167,7 +167,7 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 167 | 167 | } |
| 168 | 168 | } |
| 169 | 169 | |
| 170 | static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) { | |
| 170 | static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { | |
| 171 | 171 | assert(node->type == NodeTypeArrayAccessExpr); |
| 172 | 172 | |
| 173 | 173 | LLVMValueRef array_ref_value = gen_expr(g, node->data.array_access_expr.array_ref_expr); |
| ... | ... | @@ -180,8 +180,14 @@ static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) { |
| 180 | 180 | LLVMConstInt(LLVMInt32Type(), 0, false), |
| 181 | 181 | subscript_value |
| 182 | 182 | }; |
| 183 | LLVMValueRef result_ptr = LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, ""); | |
| 184 | return LLVMBuildLoad(g->builder, result_ptr, ""); | |
| 183 | return LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, ""); | |
| 184 | } | |
| 185 | ||
| 186 | static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) { | |
| 187 | assert(node->type == NodeTypeArrayAccessExpr); | |
| 188 | ||
| 189 | LLVMValueRef ptr = gen_array_ptr(g, node); | |
| 190 | return LLVMBuildLoad(g->builder, ptr, ""); | |
| 185 | 191 | } |
| 186 | 192 | |
| 187 | 193 | static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| ... | ... | @@ -437,22 +443,32 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) { |
| 437 | 443 | return phi; |
| 438 | 444 | } |
| 439 | 445 | |
| 446 | ||
| 440 | 447 | static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) { |
| 441 | 448 | assert(node->type == NodeTypeBinOpExpr); |
| 442 | 449 | |
| 443 | AstNode *symbol_node = node->data.bin_op_expr.op1; | |
| 444 | assert(symbol_node->type == NodeTypeSymbol); | |
| 450 | AstNode *lhs_node = node->data.bin_op_expr.op1; | |
| 445 | 451 | |
| 446 | LocalVariableTableEntry *var = find_local_variable(node->codegen_node->expr_node.block_context, | |
| 447 | &symbol_node->data.symbol); | |
| 452 | if (lhs_node->type == NodeTypeSymbol) { | |
| 453 | LocalVariableTableEntry *var = find_local_variable(node->codegen_node->expr_node.block_context, | |
| 454 | &lhs_node->data.symbol); | |
| 448 | 455 | |
| 449 | // semantic checking ensures no variables are constant | |
| 450 | assert(!var->is_const); | |
| 456 | // semantic checking ensures no variables are constant | |
| 457 | assert(!var->is_const); | |
| 451 | 458 | |
| 452 | LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2); | |
| 459 | LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2); | |
| 460 | ||
| 461 | add_debug_source_node(g, node); | |
| 462 | return LLVMBuildStore(g->builder, value, var->value_ref); | |
| 463 | } else if (lhs_node->type == NodeTypeArrayAccessExpr) { | |
| 464 | LLVMValueRef ptr = gen_array_ptr(g, lhs_node); | |
| 465 | LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2); | |
| 466 | add_debug_source_node(g, node); | |
| 467 | return LLVMBuildStore(g->builder, value, ptr); | |
| 468 | } else { | |
| 469 | zig_panic("bad assign target"); | |
| 470 | } | |
| 453 | 471 | |
| 454 | add_debug_source_node(g, node); | |
| 455 | return LLVMBuildStore(g->builder, value, var->value_ref); | |
| 456 | 472 | } |
| 457 | 473 | |
| 458 | 474 | static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { |
test/run_tests.cpp+15| ... | ... | @@ -520,6 +520,21 @@ fn f() { |
| 520 | 520 | (let a = 0); |
| 521 | 521 | } |
| 522 | 522 | )SOURCE", 1, ".tmp_source.zig:3:6: error: invalid token: 'let'"); |
| 523 | ||
| 524 | add_compile_fail_case("array access errors", R"SOURCE( | |
| 525 | fn f() { | |
| 526 | let mut bad : bool; | |
| 527 | i[i] = i[i]; | |
| 528 | bad[bad] = bad[bad]; | |
| 529 | } | |
| 530 | )SOURCE", 8, ".tmp_source.zig:4:5: error: use of undeclared identifier 'i'", | |
| 531 | ".tmp_source.zig:4:7: error: use of undeclared identifier 'i'", | |
| 532 | ".tmp_source.zig:4:12: error: use of undeclared identifier 'i'", | |
| 533 | ".tmp_source.zig:4:14: error: use of undeclared identifier 'i'", | |
| 534 | ".tmp_source.zig:5:8: error: array access of non-array", | |
| 535 | ".tmp_source.zig:5:8: error: array subscripts must be integers", | |
| 536 | ".tmp_source.zig:5:19: error: array access of non-array", | |
| 537 | ".tmp_source.zig:5:19: error: array subscripts must be integers"); | |
| 523 | 538 | } |
| 524 | 539 | |
| 525 | 540 | static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) { |