authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-12-15 19:17:39-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-12-15 19:17:39-07:00
log5a8822c714e4ee2d442e76f36213d119530f0fea
tree45c71c52e8f7304efc2f32cda40b3983d8b76e5e
parent28c5a8f2cab193858717594fc91df3369980e18c

fix assignment operators for struct fields


6 files changed, 135 insertions(+), 66 deletions(-)

doc/langref.md+2-3
...@@ -142,7 +142,7 @@ FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)...@@ -142,7 +142,7 @@ FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
142142
143ArrayAccessExpression : token(LBracket) Expression token(RBracket)143ArrayAccessExpression : token(LBracket) Expression token(RBracket)
144144
145PrefixOp : token(Not) | token(Dash) | token(Tilde)145PrefixOp : token(Not) | token(Dash) | token(Tilde) | (token(Ampersand) option(token(Const)))
146146
147PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | token(Symbol) | Goto | BlockExpression147PrimaryExpression : token(Number) | token(String) | KeywordLiteral | GroupedExpression | token(Symbol) | Goto | BlockExpression
148148
...@@ -157,8 +157,7 @@ KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False)...@@ -157,8 +157,7 @@ KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False)
157157
158```158```
159x() x[] x.y159x() x[] x.y
160&x160!x -x ~x &x &const x
161!x -x ~x
162as161as
163* / %162* / %
164+ -163+ -
src/analyze.cpp+70-31
...@@ -987,6 +987,50 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B...@@ -987,6 +987,50 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
987 }987 }
988}988}
989989
990enum LValPurpose {
991 LValPurposeAssign,
992 LValPurposeAddressOf,
993};
994
995static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, BlockContext *block_context,
996 AstNode *lhs_node, LValPurpose purpose, bool is_ptr_const)
997{
998 TypeTableEntry *expected_rhs_type = nullptr;
999 if (lhs_node->type == NodeTypeSymbol) {
1000 Buf *name = &lhs_node->data.symbol;
1001 VariableTableEntry *var = find_variable(block_context, name);
1002 if (var) {
1003 if (purpose == LValPurposeAssign && var->is_const) {
1004 add_node_error(g, lhs_node,
1005 buf_sprintf("cannot assign to constant"));
1006 } else if (purpose == LValPurposeAddressOf && var->is_const && !is_ptr_const) {
1007 add_node_error(g, lhs_node,
1008 buf_sprintf("must use &const to get address of constant"));
1009 } else {
1010 expected_rhs_type = var->type;
1011 }
1012 } else {
1013 add_node_error(g, lhs_node,
1014 buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
1015 }
1016 } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
1017 expected_rhs_type = analyze_array_access_expr(g, import, block_context, lhs_node);
1018 } else if (lhs_node->type == NodeTypeFieldAccessExpr) {
1019 alloc_codegen_node(lhs_node);
1020 expected_rhs_type = analyze_field_access_expr(g, import, block_context, lhs_node);
1021 } else {
1022 if (purpose == LValPurposeAssign) {
1023 add_node_error(g, lhs_node,
1024 buf_sprintf("assignment target must be variable, field, or array element"));
1025 } else if (purpose == LValPurposeAddressOf) {
1026 add_node_error(g, lhs_node,
1027 buf_sprintf("addressof target must be variable, field, or array element"));
1028 }
1029 expected_rhs_type = g->builtin_types.entry_invalid;
1030 }
1031 return expected_rhs_type;
1032}
1033
990static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,1034static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
991 TypeTableEntry *expected_type, AstNode *node)1035 TypeTableEntry *expected_type, AstNode *node)
992{1036{
...@@ -1006,38 +1050,17 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,...@@ -1006,38 +1050,17 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
1006 case BinOpTypeAssignBoolOr:1050 case BinOpTypeAssignBoolOr:
1007 {1051 {
1008 AstNode *lhs_node = node->data.bin_op_expr.op1;1052 AstNode *lhs_node = node->data.bin_op_expr.op1;
1009 TypeTableEntry *expected_rhs_type = nullptr;1053
1010 if (lhs_node->type == NodeTypeSymbol) {1054 TypeTableEntry *expected_rhs_type = analyze_lvalue(g, import, context, lhs_node,
1011 Buf *name = &lhs_node->data.symbol;1055 LValPurposeAssign, false);
1012 VariableTableEntry *var = find_variable(context, name);1056 if (!is_op_allowed(expected_rhs_type, node->data.bin_op_expr.bin_op)) {
1013 if (var) {1057 if (expected_rhs_type->id != TypeTableEntryIdInvalid) {
1014 if (var->is_const) {
1015 add_node_error(g, lhs_node,
1016 buf_sprintf("cannot assign to constant variable"));
1017 } else {
1018 if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) {
1019 if (var->type->id != TypeTableEntryIdInvalid) {
1020 add_node_error(g, lhs_node,
1021 buf_sprintf("operator not allowed for type '%s'",
1022 buf_ptr(&var->type->name)));
1023 }
1024 } else {
1025 expected_rhs_type = var->type;
1026 }
1027 }
1028 } else {
1029 add_node_error(g, lhs_node,1058 add_node_error(g, lhs_node,
1030 buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));1059 buf_sprintf("operator not allowed for type '%s'",
1060 buf_ptr(&expected_rhs_type->name)));
1031 }1061 }
1032 } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
1033 expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node);
1034 } else if (lhs_node->type == NodeTypeFieldAccessExpr) {
1035 alloc_codegen_node(lhs_node);
1036 expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node);
1037 } else {
1038 add_node_error(g, lhs_node,
1039 buf_sprintf("assignment target must be variable, field, or array element"));
1040 }1062 }
1063
1041 analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);1064 analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);
1042 return g->builtin_types.entry_void;1065 return g->builtin_types.entry_void;
1043 }1066 }
...@@ -1388,6 +1411,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -1388,6 +1411,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
1388 break;1411 break;
1389 case NodeTypePrefixOpExpr:1412 case NodeTypePrefixOpExpr:
1390 switch (node->data.prefix_op_expr.prefix_op) {1413 switch (node->data.prefix_op_expr.prefix_op) {
1414 case PrefixOpInvalid:
1415 zig_unreachable();
1391 case PrefixOpBoolNot:1416 case PrefixOpBoolNot:
1392 analyze_expression(g, import, context, g->builtin_types.entry_bool,1417 analyze_expression(g, import, context, g->builtin_types.entry_bool,
1393 node->data.prefix_op_expr.primary_expr);1418 node->data.prefix_op_expr.primary_expr);
...@@ -1407,8 +1432,22 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -1407,8 +1432,22 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
1407 return_type = g->builtin_types.entry_i32;1432 return_type = g->builtin_types.entry_i32;
1408 break;1433 break;
1409 }1434 }
1410 case PrefixOpInvalid:1435 case PrefixOpAddressOf:
1411 zig_unreachable();1436 case PrefixOpConstAddressOf:
1437 {
1438 bool is_const = (node->data.prefix_op_expr.prefix_op == PrefixOpConstAddressOf);
1439
1440 TypeTableEntry *child_type = analyze_lvalue(g, import, context,
1441 node->data.prefix_op_expr.primary_expr, LValPurposeAddressOf, is_const);
1442
1443 if (child_type->id == TypeTableEntryIdInvalid) {
1444 return_type = g->builtin_types.entry_invalid;
1445 break;
1446 }
1447
1448 return_type = get_pointer_to_type(g, child_type, is_const);
1449 break;
1450 }
1412 }1451 }
1413 break;1452 break;
1414 case NodeTypeIfExpr:1453 case NodeTypeIfExpr:
src/codegen.cpp+56-29
...@@ -206,7 +206,7 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {...@@ -206,7 +206,7 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
206 return LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, "");206 return LLVMBuildInBoundsGEP(g->builder, array_ref_value, indices, 2, "");
207}207}
208208
209static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node) {209static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **out_type_entry) {
210 assert(node->type == NodeTypeFieldAccessExpr);210 assert(node->type == NodeTypeFieldAccessExpr);
211211
212 LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr);212 LLVMValueRef struct_ptr = gen_expr(g, node->data.field_access_expr.struct_expr);
...@@ -217,6 +217,8 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node) {...@@ -217,6 +217,8 @@ static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node) {
217217
218 assert(codegen_field_access->field_index >= 0);218 assert(codegen_field_access->field_index >= 0);
219219
220 *out_type_entry = codegen_field_access->type_struct_field->type_entry;
221
220 add_debug_source_node(g, node);222 add_debug_source_node(g, node);
221 return LLVMBuildStructGEP(g->builder, struct_ptr, codegen_field_access->field_index, "");223 return LLVMBuildStructGEP(g->builder, struct_ptr, codegen_field_access->field_index, "");
222}224}
...@@ -243,34 +245,78 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) {...@@ -243,34 +245,78 @@ static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node) {
243 zig_panic("gen_field_access_expr bad array field");245 zig_panic("gen_field_access_expr bad array field");
244 }246 }
245 } else if (struct_type->id == TypeTableEntryIdStruct) {247 } else if (struct_type->id == TypeTableEntryIdStruct) {
246 LLVMValueRef ptr = gen_field_ptr(g, node);248 TypeTableEntry *type_entry;
249 LLVMValueRef ptr = gen_field_ptr(g, node, &type_entry);
247 return LLVMBuildLoad(g->builder, ptr, "");250 return LLVMBuildLoad(g->builder, ptr, "");
248 } else {251 } else {
249 zig_panic("gen_field_access_expr bad struct type");252 zig_panic("gen_field_access_expr bad struct type");
250 }253 }
251}254}
252255
256static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *parent_node, AstNode *node,
257 TypeTableEntry **out_type_entry)
258{
259 LLVMValueRef target_ref;
260
261 if (node->type == NodeTypeSymbol) {
262 VariableTableEntry *var = find_variable(parent_node->codegen_node->expr_node.block_context,
263 &node->data.symbol);
264
265 // semantic checking ensures no variables are constant
266 assert(!var->is_const);
267
268 *out_type_entry = var->type;
269 target_ref = var->value_ref;
270 } else if (node->type == NodeTypeArrayAccessExpr) {
271 TypeTableEntry *array_type = get_expr_type(node->data.array_access_expr.array_ref_expr);
272 assert(array_type->id == TypeTableEntryIdArray);
273 *out_type_entry = array_type->data.array.child_type;
274 target_ref = gen_array_ptr(g, node);
275 } else if (node->type == NodeTypeFieldAccessExpr) {
276 target_ref = gen_field_ptr(g, node, out_type_entry);
277 } else {
278 zig_panic("bad assign target");
279 }
280
281 return target_ref;
282}
283
253static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {284static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
254 assert(node->type == NodeTypePrefixOpExpr);285 assert(node->type == NodeTypePrefixOpExpr);
255 assert(node->data.prefix_op_expr.primary_expr);286 assert(node->data.prefix_op_expr.primary_expr);
256287
257 LLVMValueRef expr = gen_expr(g, node->data.prefix_op_expr.primary_expr);288 AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
258289
259 switch (node->data.prefix_op_expr.prefix_op) {290 switch (node->data.prefix_op_expr.prefix_op) {
291 case PrefixOpInvalid:
292 zig_unreachable();
260 case PrefixOpNegation:293 case PrefixOpNegation:
261 add_debug_source_node(g, node);294 {
262 return LLVMBuildNeg(g->builder, expr, "");295 LLVMValueRef expr = gen_expr(g, expr_node);
296 add_debug_source_node(g, node);
297 return LLVMBuildNeg(g->builder, expr, "");
298 }
263 case PrefixOpBoolNot:299 case PrefixOpBoolNot:
264 {300 {
301 LLVMValueRef expr = gen_expr(g, expr_node);
265 LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));302 LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr));
266 add_debug_source_node(g, node);303 add_debug_source_node(g, node);
267 return LLVMBuildICmp(g->builder, LLVMIntEQ, expr, zero, "");304 return LLVMBuildICmp(g->builder, LLVMIntEQ, expr, zero, "");
268 }305 }
269 case PrefixOpBinNot:306 case PrefixOpBinNot:
270 add_debug_source_node(g, node);307 {
271 return LLVMBuildNot(g->builder, expr, "");308 LLVMValueRef expr = gen_expr(g, expr_node);
272 case PrefixOpInvalid:309 add_debug_source_node(g, node);
273 zig_unreachable();310 return LLVMBuildNot(g->builder, expr, "");
311 }
312 case PrefixOpAddressOf:
313 case PrefixOpConstAddressOf:
314 {
315 add_debug_source_node(g, node);
316 TypeTableEntry *lvalue_type;
317 return gen_lvalue(g, node, expr_node, &lvalue_type);
318 }
319
274 }320 }
275 zig_unreachable();321 zig_unreachable();
276}322}
...@@ -571,33 +617,14 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {...@@ -571,33 +617,14 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
571 return phi;617 return phi;
572}618}
573619
574
575static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) {620static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) {
576 assert(node->type == NodeTypeBinOpExpr);621 assert(node->type == NodeTypeBinOpExpr);
577622
578 AstNode *lhs_node = node->data.bin_op_expr.op1;623 AstNode *lhs_node = node->data.bin_op_expr.op1;
579624
580 LLVMValueRef target_ref;
581 TypeTableEntry *op1_type;625 TypeTableEntry *op1_type;
582 if (lhs_node->type == NodeTypeSymbol) {626 LLVMValueRef target_ref = gen_lvalue(g, node, lhs_node, &op1_type);
583 VariableTableEntry *var = find_variable(node->codegen_node->expr_node.block_context,
584 &lhs_node->data.symbol);
585
586 // semantic checking ensures no variables are constant
587 assert(!var->is_const);
588627
589 op1_type = var->type;
590 target_ref = var->value_ref;
591 } else if (lhs_node->type == NodeTypeArrayAccessExpr) {
592 TypeTableEntry *array_type = get_expr_type(lhs_node->data.array_access_expr.array_ref_expr);
593 assert(array_type->id == TypeTableEntryIdArray);
594 op1_type = array_type->data.array.child_type;
595 target_ref = gen_array_ptr(g, lhs_node);
596 } else if (lhs_node->type == NodeTypeFieldAccessExpr) {
597 target_ref = gen_field_ptr(g, lhs_node);
598 } else {
599 zig_panic("bad assign target");
600 }
601 LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2);628 LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2);
602629
603 if (node->data.bin_op_expr.bin_op == BinOpTypeAssign) {630 if (node->data.bin_op_expr.bin_op == BinOpTypeAssign) {
src/parser.cpp+2
...@@ -58,6 +58,8 @@ static const char *prefix_op_str(PrefixOp prefix_op) {...@@ -58,6 +58,8 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
58 case PrefixOpNegation: return "-";58 case PrefixOpNegation: return "-";
59 case PrefixOpBoolNot: return "!";59 case PrefixOpBoolNot: return "!";
60 case PrefixOpBinNot: return "~";60 case PrefixOpBinNot: return "~";
61 case PrefixOpAddressOf: return "&";
62 case PrefixOpConstAddressOf: return "&const";
61 }63 }
62 zig_unreachable();64 zig_unreachable();
63}65}
src/parser.hpp+2
...@@ -198,6 +198,8 @@ enum PrefixOp {...@@ -198,6 +198,8 @@ enum PrefixOp {
198 PrefixOpBoolNot,198 PrefixOpBoolNot,
199 PrefixOpBinNot,199 PrefixOpBinNot,
200 PrefixOpNegation,200 PrefixOpNegation,
201 PrefixOpAddressOf,
202 PrefixOpConstAddressOf,
201};203};
202204
203struct AstNodePrefixOpExpr {205struct AstNodePrefixOpExpr {
test/run_tests.cpp+3-3
...@@ -566,7 +566,7 @@ use "std.zig";...@@ -566,7 +566,7 @@ use "std.zig";
566566
567export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {567export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 {
568 var foo : Foo;568 var foo : Foo;
569 foo.a = foo.a + 1;569 foo.a += 1;
570 foo.b = foo.a == 1;570 foo.b = foo.a == 1;
571 test_foo(foo);571 test_foo(foo);
572 return 0;572 return 0;
...@@ -749,7 +749,7 @@ fn f() {...@@ -749,7 +749,7 @@ fn f() {
749 const a = 3;749 const a = 3;
750 a = 4;750 a = 4;
751}751}
752 )SOURCE", 1, ".tmp_source.zig:4:5: error: cannot assign to constant variable");752 )SOURCE", 1, ".tmp_source.zig:4:5: error: cannot assign to constant");
753753
754 add_compile_fail_case("use of undeclared identifier", R"SOURCE(754 add_compile_fail_case("use of undeclared identifier", R"SOURCE(
755fn f() {755fn f() {
...@@ -787,7 +787,7 @@ const x : i32 = 99;...@@ -787,7 +787,7 @@ const x : i32 = 99;
787fn f() {787fn f() {
788 x = 1;788 x = 1;
789}789}
790 )SOURCE", 1, ".tmp_source.zig:4:5: error: cannot assign to constant variable");790 )SOURCE", 1, ".tmp_source.zig:4:5: error: cannot assign to constant");
791791
792792
793 add_compile_fail_case("missing else clause", R"SOURCE(793 add_compile_fail_case("missing else clause", R"SOURCE(