Commit e8550814c5

Andrew Kelley <superjoe30@gmail.com>
2015-12-08 23:31:43
support assigning to arrays
1 parent 6e0c3dc
Changed files (4)
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);
 }
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;
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) {
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) {