From e8550814c5e4387e8b758736a1254a5c3b32674e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 8 Dec 2015 15:31:43 -0700 Subject: [PATCH] support assigning to arrays --- example/arrays/arrays.zig | 6 ++-- src/analyze.cpp | 62 +++++++++++++++++++++++++-------------- src/codegen.cpp | 40 +++++++++++++++++-------- test/run_tests.cpp | 15 ++++++++++ 4 files changed, 85 insertions(+), 38 deletions(-) diff --git a/example/arrays/arrays.zig b/example/arrays/arrays.zig index 88ec8e411b..87ea40da6e 100644 --- a/example/arrays/arrays.zig +++ b/example/arrays/arrays.zig @@ -9,9 +9,7 @@ extern { export fn _start() -> unreachable { let mut array : [i32; 10]; - exit(array[1]); - - //array[4] = array[1] + 5; - + array[4] = array[1] + 5; + exit(0); } diff --git a/src/analyze.cpp b/src/analyze.cpp index c6f50ff5d3..4436457b01 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -10,6 +10,9 @@ #include "zig_llvm.hpp" #include "os.hpp" +static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node); + static AstNode *first_executing_node(AstNode *node) { switch (node->type) { case NodeTypeFnCallExpr: @@ -476,6 +479,35 @@ LocalVariableTableEntry *find_local_variable(BlockContext *context, Buf *name) { } } +static TypeTableEntry *analyze_array_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + AstNode *node) +{ + TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, + node->data.array_access_expr.array_ref_expr); + + TypeTableEntry *return_type; + + if (array_type->id == TypeTableEntryIdArray) { + return_type = array_type->data.array.child_type; + } else { + if (array_type->id != TypeTableEntryIdInvalid) { + add_node_error(g, node, buf_sprintf("array access of non-array")); + } + return_type = g->builtin_types.entry_invalid; + } + + TypeTableEntry *subscript_type = analyze_expression(g, import, context, nullptr, + node->data.array_access_expr.subscript); + if (subscript_type->id != TypeTableEntryIdInt && + subscript_type->id != TypeTableEntryIdInvalid) + { + add_node_error(g, node, + buf_sprintf("array subscripts must be integers")); + } + + return return_type; +} + static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -593,6 +625,7 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case BinOpTypeAssign: { AstNode *lhs_node = node->data.bin_op_expr.op1; + TypeTableEntry *expected_rhs_type = nullptr; if (lhs_node->type == NodeTypeSymbol) { Buf *name = &lhs_node->data.symbol; LocalVariableTableEntry *var = find_local_variable(context, name); @@ -601,18 +634,19 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, add_node_error(g, lhs_node, buf_sprintf("cannot assign to constant variable")); } else { - analyze_expression(g, import, context, var->type, - node->data.bin_op_expr.op2); + expected_rhs_type = var->type; } } else { add_node_error(g, lhs_node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); } - + } else if (lhs_node->type == NodeTypeArrayAccessExpr) { + expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node); } else { add_node_error(g, lhs_node, buf_sprintf("expected a bare identifier")); } + analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); return_type = g->builtin_types.entry_void; break; } @@ -736,25 +770,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, } case NodeTypeArrayAccessExpr: - { - // here we are always reading the array - TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, - node->data.array_access_expr.array_ref_expr); - if (array_type->id == TypeTableEntryIdArray) { - TypeTableEntry *subscript_type = analyze_expression(g, import, context, - nullptr, node->data.array_access_expr.subscript); - if (subscript_type->id != TypeTableEntryIdInt) { - add_node_error(g, node, - buf_sprintf("array subscripts must be integers")); - } - return_type = array_type->data.array.child_type; - } else { - add_node_error(g, node, buf_sprintf("array access of non-array")); - return_type = g->builtin_types.entry_invalid; - } - - break; - } + // for reading array access; assignment handled elsewhere + return_type = analyze_array_access_expr(g, import, context, node); + break; case NodeTypeNumberLiteral: // TODO: generic literal int type return_type = g->builtin_types.entry_i32; diff --git a/src/codegen.cpp b/src/codegen.cpp index 366873c721..d3e76bc8f4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -167,7 +167,7 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { } } -static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) { +static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeArrayAccessExpr); 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) { LLVMConstInt(LLVMInt32Type(), 0, false), subscript_value }; - LLVMValueRef result_ptr = LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, ""); - return LLVMBuildLoad(g->builder, result_ptr, ""); + return LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, ""); +} + +static LLVMValueRef gen_array_access_expr(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeArrayAccessExpr); + + LLVMValueRef ptr = gen_array_ptr(g, node); + return LLVMBuildLoad(g->builder, ptr, ""); } 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) { return phi; } + static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); - AstNode *symbol_node = node->data.bin_op_expr.op1; - assert(symbol_node->type == NodeTypeSymbol); + AstNode *lhs_node = node->data.bin_op_expr.op1; - LocalVariableTableEntry *var = find_local_variable(node->codegen_node->expr_node.block_context, - &symbol_node->data.symbol); + if (lhs_node->type == NodeTypeSymbol) { + LocalVariableTableEntry *var = find_local_variable(node->codegen_node->expr_node.block_context, + &lhs_node->data.symbol); - // semantic checking ensures no variables are constant - assert(!var->is_const); + // semantic checking ensures no variables are constant + assert(!var->is_const); - LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2); + LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2); + + add_debug_source_node(g, node); + return LLVMBuildStore(g->builder, value, var->value_ref); + } else if (lhs_node->type == NodeTypeArrayAccessExpr) { + LLVMValueRef ptr = gen_array_ptr(g, lhs_node); + LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2); + add_debug_source_node(g, node); + return LLVMBuildStore(g->builder, value, ptr); + } else { + zig_panic("bad assign target"); + } - add_debug_source_node(g, node); - return LLVMBuildStore(g->builder, value, var->value_ref); } static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 5d44e6e690..4a58f6df02 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -520,6 +520,21 @@ fn f() { (let a = 0); } )SOURCE", 1, ".tmp_source.zig:3:6: error: invalid token: 'let'"); + + add_compile_fail_case("array access errors", R"SOURCE( +fn f() { + let mut bad : bool; + i[i] = i[i]; + bad[bad] = bad[bad]; +} + )SOURCE", 8, ".tmp_source.zig:4:5: error: use of undeclared identifier 'i'", + ".tmp_source.zig:4:7: error: use of undeclared identifier 'i'", + ".tmp_source.zig:4:12: error: use of undeclared identifier 'i'", + ".tmp_source.zig:4:14: error: use of undeclared identifier 'i'", + ".tmp_source.zig:5:8: error: array access of non-array", + ".tmp_source.zig:5:8: error: array subscripts must be integers", + ".tmp_source.zig:5:19: error: array access of non-array", + ".tmp_source.zig:5:19: error: array subscripts must be integers"); } static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) {