| 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 +9,7 @@ extern { |
| 9 | export fn _start() -> unreachable { | 9 | export fn _start() -> unreachable { |
| 10 | let mut array : [i32; 10]; | 10 | let mut array : [i32; 10]; |
| 11 | 11 | ||
| 12 | exit(array[1]); | 12 | array[4] = array[1] + 5; |
| 13 | |||
| 14 | //array[4] = array[1] + 5; | ||
| 15 | |||
| 16 | 13 | ||
| 14 | exit(0); | ||
| 17 | } | 15 | } |
src/analyze.cpp+40-22| ... | @@ -10,6 +10,9 @@ | ... | @@ -10,6 +10,9 @@ |
| 10 | #include "zig_llvm.hpp" | 10 | #include "zig_llvm.hpp" |
| 11 | #include "os.hpp" | 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 | static AstNode *first_executing_node(AstNode *node) { | 16 | static AstNode *first_executing_node(AstNode *node) { |
| 14 | switch (node->type) { | 17 | switch (node->type) { |
| 15 | case NodeTypeFnCallExpr: | 18 | case NodeTypeFnCallExpr: |
| ... | @@ -476,6 +479,35 @@ LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) { | ... | @@ -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 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 511 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 480 | TypeTableEntry *expected_type, AstNode *node) | 512 | TypeTableEntry *expected_type, AstNode *node) |
| 481 | { | 513 | { |
| ... | @@ -593,6 +625,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -593,6 +625,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 593 | case BinOpTypeAssign: | 625 | case BinOpTypeAssign: |
| 594 | { | 626 | { |
| 595 | AstNode *lhs_node = node->data.bin_op_expr.op1; | 627 | AstNode *lhs_node = node->data.bin_op_expr.op1; |
| 628 | TypeTableEntry *expected_rhs_type = nullptr; | ||
| 596 | if (lhs_node->type == NodeTypeSymbol) { | 629 | if (lhs_node->type == NodeTypeSymbol) { |
| 597 | Buf *name = &lhs_node->data.symbol; | 630 | Buf *name = &lhs_node->data.symbol; |
| 598 | LocalVariableTableEntry *var = find_local_variable(context, name); | 631 | LocalVariableTableEntry *var = find_local_variable(context, name); |
| ... | @@ -601,18 +634,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -601,18 +634,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 601 | add_node_error(g, lhs_node, | 634 | add_node_error(g, lhs_node, |
| 602 | buf_sprintf("cannot assign to constant variable")); | 635 | buf_sprintf("cannot assign to constant variable")); |
| 603 | } else { | 636 | } else { |
| 604 | analyze_expression(g, import, context, var->type, | 637 | expected_rhs_type = var->type; |
| 605 | node->data.bin_op_expr.op2); | ||
| 606 | } | 638 | } |
| 607 | } else { | 639 | } else { |
| 608 | add_node_error(g, lhs_node, | 640 | add_node_error(g, lhs_node, |
| 609 | buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); | 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 | } else { | 645 | } else { |
| 613 | add_node_error(g, lhs_node, | 646 | add_node_error(g, lhs_node, |
| 614 | buf_sprintf("expected a bare identifier")); | 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 | return_type = g->builtin_types.entry_void; | 650 | return_type = g->builtin_types.entry_void; |
| 617 | break; | 651 | break; |
| 618 | } | 652 | } |
| ... | @@ -736,25 +770,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -736,25 +770,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 736 | } | 770 | } |
| 737 | 771 | ||
| 738 | case NodeTypeArrayAccessExpr: | 772 | case NodeTypeArrayAccessExpr: |
| 739 | { | 773 | // for reading array access; assignment handled elsewhere |
| 740 | // here we are always reading the array | 774 | return_type = analyze_array_access_expr(g, import, context, node); |
| 741 | TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, | 775 | break; |
| 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 | } | ||
| 758 | case NodeTypeNumberLiteral: | 776 | case NodeTypeNumberLiteral: |
| 759 | // TODO: generic literal int type | 777 | // TODO: generic literal int type |
| 760 | return_type = g->builtin_types.entry_i32; | 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,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 | assert(node->type == NodeTypeArrayAccessExpr); | 171 | assert(node->type == NodeTypeArrayAccessExpr); |
| 172 | 172 | ||
| 173 | LLVMValueRef array_ref_value = gen_expr(g, node->data.array_access_expr.array_ref_expr); | 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,8 +180,14 @@ static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) { |
| 180 | LLVMConstInt(LLVMInt32Type(), 0, false), | 180 | LLVMConstInt(LLVMInt32Type(), 0, false), |
| 181 | subscript_value | 181 | subscript_value |
| 182 | }; | 182 | }; |
| 183 | LLVMValueRef result_ptr = LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, ""); | 183 | return LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, ""); |
| 184 | return LLVMBuildLoad(g->builder, result_ptr, ""); | 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 | static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { | 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,22 +443,32 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) { |
| 437 | return phi; | 443 | return phi; |
| 438 | } | 444 | } |
| 439 | 445 | ||
| 446 | |||
| 440 | static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) { | 447 | static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) { |
| 441 | assert(node->type == NodeTypeBinOpExpr); | 448 | assert(node->type == NodeTypeBinOpExpr); |
| 442 | 449 | ||
| 443 | AstNode *symbol_node = node->data.bin_op_expr.op1; | 450 | AstNode *lhs_node = node->data.bin_op_expr.op1; |
| 444 | assert(symbol_node->type == NodeTypeSymbol); | ||
| 445 | 451 | ||
| 446 | LocalVariableTableEntry *var = find_local_variable(node->codegen_node->expr_node.block_context, | 452 | if (lhs_node->type == NodeTypeSymbol) { |
| 447 | &symbol_node->data.symbol); | 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 | 456 | // semantic checking ensures no variables are constant |
| 450 | assert(!var->is_const); | 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 | static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { | 474 | static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { |
test/run_tests.cpp+15| ... | @@ -520,6 +520,21 @@ fn f() { | ... | @@ -520,6 +520,21 @@ fn f() { |
| 520 | (let a = 0); | 520 | (let a = 0); |
| 521 | } | 521 | } |
| 522 | )SOURCE", 1, ".tmp_source.zig:3:6: error: invalid token: 'let'"); | 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 | static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) { | 540 | static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) { |