authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-12-06 23:09:46-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-12-06 23:09:46-07:00
log3c3be10a605085d8855e54b8c6a769d6ad1e00d9
treef3c31aa240dcd35937a9e5d99a266b393db5800a
parentdfb48a2c6bc034dab8d7c17db8d67089fc68043d

add mutable local variables


7 files changed, 226 insertions(+), 20 deletions(-)

example/expressions/expressions.zig+16
......@@ -1,3 +1,5 @@
1export executable "expressions";
2
13#link("c")
24extern {
35 fn puts(s: *const u8) -> i32;
......@@ -28,6 +30,8 @@ export fn _start() -> unreachable {
2830
2931 void_fun(1, void, 2);
3032
33 test_mutable_vars();
34
3135 other_exit();
3236}
3337
......@@ -38,3 +42,15 @@ fn void_fun(a : i32, b : void, c : i32) -> void {
3842 let w : void = z; // void
3943 if (x + y == 4) { return w; }
4044}
45
46fn test_mutable_vars() {
47 let mut i = 0;
48loop_start:
49 if i == 3 {
50 goto done;
51 }
52 puts("loop");
53 i = i + 1;
54 goto loop_start;
55done:
56}
src/analyze.cpp+46-3
......@@ -362,6 +362,13 @@ static BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
362362 else
363363 context->root = context;
364364 context->variable_table.init(8);
365
366 AstNode *fn_def_node = context->root->node;
367 assert(fn_def_node->type == NodeTypeFnDef);
368 assert(fn_def_node->codegen_node);
369 FnDefNode *fn_def_info = &fn_def_node->codegen_node->data.fn_def_node;
370 fn_def_info->all_block_contexts.append(context);
371
365372 return context;
366373}
367374
......@@ -388,8 +395,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
388395 return_type = g->builtin_types.entry_void;
389396 for (int i = 0; i < node->data.block.statements.length; i += 1) {
390397 AstNode *child = node->data.block.statements.at(i);
391 if (child->type == NodeTypeLabel)
398 if (child->type == NodeTypeLabel) {
399 LabelTableEntry *label_entry = child->codegen_node->data.label_entry;
400 assert(label_entry);
401 label_entry->entered_from_fallthrough = (return_type != g->builtin_types.entry_unreachable);
402 return_type = g->builtin_types.entry_void;
392403 continue;
404 }
393405 if (return_type == g->builtin_types.entry_unreachable) {
394406 if (child->type == NodeTypeVoid) {
395407 // {unreachable;void;void} is allowed.
......@@ -457,6 +469,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
457469 LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
458470 buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol);
459471 variable_entry->type = type;
472 variable_entry->is_const = variable_declaration->is_const;
473 variable_entry->decl_node = node;
460474 context->variable_table.put(&variable_entry->name, variable_entry);
461475 }
462476 return_type = g->builtin_types.entry_void;
......@@ -482,6 +496,32 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
482496 case NodeTypeBinOpExpr:
483497 {
484498 switch (node->data.bin_op_expr.bin_op) {
499 case BinOpTypeAssign:
500 {
501 AstNode *lhs_node = node->data.bin_op_expr.op1;
502 if (lhs_node->type == NodeTypeSymbol) {
503 Buf *name = &lhs_node->data.symbol;
504 LocalVariableTableEntry *var = find_local_variable(context, name);
505 if (var) {
506 if (var->is_const) {
507 add_node_error(g, lhs_node,
508 buf_sprintf("cannot assign to constant variable"));
509 } else {
510 analyze_expression(g, import, context, var->type,
511 node->data.bin_op_expr.op2);
512 }
513 } else {
514 add_node_error(g, lhs_node,
515 buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
516 }
517
518 } else {
519 add_node_error(g, lhs_node,
520 buf_sprintf("expected a bare identifier"));
521 }
522 return_type = g->builtin_types.entry_void;
523 break;
524 }
485525 case BinOpTypeBoolOr:
486526 case BinOpTypeBoolAnd:
487527 analyze_expression(g, import, context, g->builtin_types.entry_bool,
......@@ -721,7 +761,10 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
721761 AstNode *fn_proto_node = node->data.fn_def.fn_proto;
722762 assert(fn_proto_node->type == NodeTypeFnProto);
723763
764 assert(!node->codegen_node);
765 node->codegen_node = allocate<CodeGenNode>(1);
724766 BlockContext *context = new_block_context(node, nullptr);
767 node->codegen_node->data.fn_def_node.block_context = context;
725768
726769 AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
727770 for (int i = 0; i < fn_proto->params.length; i += 1) {
......@@ -736,6 +779,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
736779 LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
737780 buf_init_from_buf(&variable_entry->name, &param_decl->name);
738781 variable_entry->type = type;
782 variable_entry->is_const = true;
783 variable_entry->decl_node = param_decl_node;
739784
740785 LocalVariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name);
741786 if (!existing_entry) {
......@@ -756,9 +801,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
756801 TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry;
757802 TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);
758803
759 node->codegen_node = allocate<CodeGenNode>(1);
760804 node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type;
761 node->codegen_node->data.fn_def_node.block_context = context;
762805
763806 {
764807 FnTableEntry *fn_table_entry = fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry;
src/codegen.cpp+68-8
......@@ -102,6 +102,7 @@ static int count_non_void_params(CodeGen *g, ZigList<AstNode *> *params) {
102102}
103103
104104static void add_debug_source_node(CodeGen *g, AstNode *node) {
105 // TODO g->block_scopes.last() is not always correct and should probably integrate with BlockContext
105106 LLVMZigSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, g->block_scopes.last());
106107}
107108
......@@ -210,6 +211,8 @@ static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {
210211 LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
211212
212213 switch (node->data.bin_op_expr.bin_op) {
214 case BinOpTypeAssign:
215 zig_panic("TODO assignment");
213216 case BinOpTypeBinOr:
214217 add_debug_source_node(g, node);
215218 return LLVMBuildOr(g->builder, val1, val2, "");
......@@ -358,8 +361,28 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
358361 return phi;
359362}
360363
364static LLVMValueRef gen_assign_expr(CodeGen *g, AstNode *node) {
365 assert(node->type == NodeTypeBinOpExpr);
366
367 AstNode *symbol_node = node->data.bin_op_expr.op1;
368 assert(symbol_node->type == NodeTypeSymbol);
369
370 LocalVariableTableEntry *var = find_local_variable(node->codegen_node->expr_node.block_context,
371 &symbol_node->data.symbol);
372
373 // semantic checking ensures no variables are constant
374 assert(!var->is_const);
375
376 LLVMValueRef value = gen_expr(g, node->data.bin_op_expr.op2);
377
378 add_debug_source_node(g, node);
379 return LLVMBuildStore(g->builder, value, var->value_ref);
380}
381
361382static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
362383 switch (node->data.bin_op_expr.bin_op) {
384 case BinOpTypeAssign:
385 return gen_assign_expr(g, node);
363386 case BinOpTypeInvalid:
364387 zig_unreachable();
365388 case BinOpTypeBoolOr:
......@@ -498,9 +521,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
498521 case NodeTypeVariableDeclaration:
499522 {
500523 LocalVariableTableEntry *variable = find_local_variable(node->codegen_node->expr_node.block_context, &node->data.variable_declaration.symbol);
501 assert(node->data.variable_declaration.expr);
502 variable->value_ref = gen_expr(g, node->data.variable_declaration.expr);
503 return nullptr;
524 if (variable->is_const) {
525 assert(node->data.variable_declaration.expr);
526 variable->value_ref = gen_expr(g, node->data.variable_declaration.expr);
527 return nullptr;
528 } else {
529 if (node->data.variable_declaration.expr) {
530 LLVMValueRef value = gen_expr(g, node->data.variable_declaration.expr);
531
532 add_debug_source_node(g, node);
533 return LLVMBuildStore(g->builder, value, variable->value_ref);
534 } else {
535
536 }
537 }
504538 }
505539 case NodeTypeCastExpr:
506540 return gen_cast_expr(g, node);
......@@ -542,7 +576,11 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
542576 case NodeTypeSymbol:
543577 {
544578 LocalVariableTableEntry *variable = find_local_variable(node->codegen_node->expr_node.block_context, &node->data.symbol);
545 return variable->value_ref;
579 if (variable->is_const) {
580 return variable->value_ref;
581 } else {
582 return LLVMBuildLoad(g->builder, variable->value_ref, "");
583 }
546584 }
547585 case NodeTypeBlock:
548586 return gen_block(g, node, nullptr);
......@@ -551,11 +589,15 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
551589 return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block);
552590 case NodeTypeLabel:
553591 {
554 LLVMBasicBlockRef basic_block = node->codegen_node->data.label_entry->basic_block;
555 add_debug_source_node(g, node);
556 LLVMValueRef result = LLVMBuildBr(g->builder, basic_block);
592 LabelTableEntry *label_entry = node->codegen_node->data.label_entry;
593 assert(label_entry);
594 LLVMBasicBlockRef basic_block = label_entry->basic_block;
595 if (label_entry->entered_from_fallthrough) {
596 add_debug_source_node(g, node);
597 LLVMBuildBr(g->builder, basic_block);
598 }
557599 LLVMPositionBuilderAtEnd(g->builder, basic_block);
558 return result;
600 return nullptr;
559601 }
560602 case NodeTypeRoot:
561603 case NodeTypeRootExportDecl:
......@@ -696,6 +738,24 @@ static void do_code_gen(CodeGen *g) {
696738
697739 build_label_blocks(g, fn_def_node->data.fn_def.body);
698740
741 // allocate all local variables
742 for (int i = 0; i < codegen_fn_def->all_block_contexts.length; i += 1) {
743 BlockContext *block_context = codegen_fn_def->all_block_contexts.at(i);
744
745 auto it = block_context->variable_table.entry_iterator();
746 for (;;) {
747 auto *entry = it.next();
748 if (!entry)
749 break;
750
751 LocalVariableTableEntry *var = entry->value;
752 if (!var->is_const) {
753 add_debug_source_node(g, var->decl_node);
754 var->value_ref = LLVMBuildAlloca(g->builder, var->type->type_ref, buf_ptr(&var->name));
755 }
756 }
757 }
758
699759 TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;
700760 gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type);
701761
src/parser.cpp+44-7
......@@ -33,6 +33,7 @@ static const char *bin_op_str(BinOpType bin_op) {
3333 case BinOpTypeMult: return "*";
3434 case BinOpTypeDiv: return "/";
3535 case BinOpTypeMod: return "%";
36 case BinOpTypeAssign: return "=";
3637 }
3738 zig_unreachable();
3839}
......@@ -1096,7 +1097,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m
10961097}
10971098
10981099/*
1099VariableDeclaration : token(Let) token(Symbole) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression))
1100VariableDeclaration : token(Let) option(token(Mut)) token(Symbol) (token(Eq) Expression | token(Colon) Type option(token(Eq) Expression))
11001101*/
11011102static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) {
11021103 Token *let_tok = &pc->tokens->at(*token_index);
......@@ -1104,9 +1105,21 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token
11041105 *token_index += 1;
11051106 AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, let_tok);
11061107
1107 Token *name_token = &pc->tokens->at(*token_index);
1108 Token *name_token;
1109 Token *token = &pc->tokens->at(*token_index);
1110 if (token->id == TokenIdKeywordMut) {
1111 node->data.variable_declaration.is_const = false;
1112 *token_index += 1;
1113 name_token = &pc->tokens->at(*token_index);
1114 ast_expect_token(pc, name_token, TokenIdSymbol);
1115 } else if (token->id == TokenIdSymbol) {
1116 node->data.variable_declaration.is_const = true;
1117 name_token = token;
1118 } else {
1119 ast_invalid_token_error(pc, token);
1120 }
1121
11081122 *token_index += 1;
1109 ast_expect_token(pc, name_token, TokenIdSymbol);
11101123 ast_buf_from_token(pc, name_token, &node->data.variable_declaration.symbol);
11111124
11121125 Token *eq_or_colon = &pc->tokens->at(*token_index);
......@@ -1178,7 +1191,30 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma
11781191}
11791192
11801193/*
1181NonBlockExpression : ReturnExpression | VariableDeclaration | BoolOrExpression
1194AssignmentExpression : BoolOrExpression token(Equal) BoolOrExpression | BoolOrExpression
1195*/
1196static AstNode *ast_parse_ass_expr(ParseContext *pc, int *token_index, bool mandatory) {
1197 AstNode *lhs = ast_parse_bool_or_expr(pc, token_index, mandatory);
1198 if (!lhs)
1199 return lhs;
1200
1201 Token *token = &pc->tokens->at(*token_index);
1202 if (token->id != TokenIdEq)
1203 return lhs;
1204 *token_index += 1;
1205
1206 AstNode *rhs = ast_parse_bool_or_expr(pc, token_index, true);
1207
1208 AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
1209 node->data.bin_op_expr.op1 = lhs;
1210 node->data.bin_op_expr.bin_op = BinOpTypeAssign;
1211 node->data.bin_op_expr.op2 = rhs;
1212
1213 return node;
1214}
1215
1216/*
1217NonBlockExpression : ReturnExpression | VariableDeclaration | AssignmentExpression
11821218*/
11831219static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
11841220 Token *token = &pc->tokens->at(*token_index);
......@@ -1191,9 +1227,10 @@ static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, boo
11911227 if (variable_declaration_expr)
11921228 return variable_declaration_expr;
11931229
1194 AstNode *bool_or_expr = ast_parse_bool_or_expr(pc, token_index, false);
1195 if (bool_or_expr)
1196 return bool_or_expr;
1230
1231 AstNode *ass_expr = ast_parse_ass_expr(pc, token_index, false);
1232 if (ass_expr)
1233 return ass_expr;
11971234
11981235 if (mandatory)
11991236 ast_invalid_token_error(pc, token);
src/parser.hpp+2-1
......@@ -101,6 +101,7 @@ struct AstNodeReturnExpr {
101101
102102struct AstNodeVariableDeclaration {
103103 Buf symbol;
104 bool is_const;
104105 // one or both of type and expr will be non null
105106 AstNode *type;
106107 AstNode *expr;
......@@ -108,7 +109,7 @@ struct AstNodeVariableDeclaration {
108109
109110enum BinOpType {
110111 BinOpTypeInvalid,
111 // TODO: include assignment?
112 BinOpTypeAssign,
112113 BinOpTypeBoolOr,
113114 BinOpTypeBoolAnd,
114115 BinOpTypeCmpEq,
src/semantic_info.hpp+9
......@@ -42,6 +42,7 @@ struct LabelTableEntry {
4242 AstNode *label_node;
4343 LLVMBasicBlockRef basic_block;
4444 bool used;
45 bool entered_from_fallthrough;
4546};
4647
4748struct FnTableEntry {
......@@ -116,6 +117,8 @@ struct LocalVariableTableEntry {
116117 Buf name;
117118 TypeTableEntry *type;
118119 LLVMValueRef value_ref;
120 bool is_const;
121 AstNode *decl_node;
119122};
120123
121124struct BlockContext {
......@@ -137,6 +140,7 @@ struct FnDefNode {
137140 TypeTableEntry *implicit_return_type;
138141 BlockContext *block_context;
139142 bool skip;
143 ZigList<BlockContext *> all_block_contexts;
140144};
141145
142146struct ExprNode {
......@@ -146,12 +150,17 @@ struct ExprNode {
146150 BlockContext *block_context;
147151};
148152
153struct AssignNode {
154 LocalVariableTableEntry *var_entry;
155};
156
149157struct CodeGenNode {
150158 union {
151159 TypeNode type_node; // for NodeTypeType
152160 FnDefNode fn_def_node; // for NodeTypeFnDef
153161 FnProtoNode fn_proto_node; // for NodeTypeFnProto
154162 LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel
163 AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign
155164 } data;
156165 ExprNode expr_node; // for all the expression nodes
157166};
test/run_tests.cpp+41-1
......@@ -331,6 +331,27 @@ fn void_fun(a : i32, b : void, c : i32) {
331331 return vv;
332332}
333333 )SOURCE", "OK\n");
334
335 add_simple_case("void parameters", R"SOURCE(
336#link("c")
337extern {
338 fn puts(s: *const u8) -> i32;
339 fn exit(code: i32) -> unreachable;
340}
341
342export fn _start() -> unreachable {
343 let mut i = 0;
344loop_start:
345 if i == 3 {
346 goto done;
347 }
348 puts("loop");
349 i = i + 1;
350 goto loop_start;
351done:
352 exit(0);
353}
354 )SOURCE", "loop\nloop\nloop\n");
334355}
335356
336357static void add_compile_failure_test_cases(void) {
......@@ -467,10 +488,29 @@ export fn f(a : void) {}
467488 )SOURCE", 1, ".tmp_source.zig:2:17: error: parameter of type 'void' not allowed on exported functions");
468489
469490 add_compile_fail_case("unused label", R"SOURCE(
470export fn f() {
491fn f() {
471492a_label:
472493}
473494 )SOURCE", 1, ".tmp_source.zig:3:1: error: label 'a_label' defined but not used");
495
496 add_compile_fail_case("expected bare identifier", R"SOURCE(
497fn f() {
498 3 = 3;
499}
500 )SOURCE", 1, ".tmp_source.zig:3:5: error: expected a bare identifier");
501
502 add_compile_fail_case("assign to constant variable", R"SOURCE(
503fn f() {
504 let a = 3;
505 a = 4;
506}
507 )SOURCE", 1, ".tmp_source.zig:4:5: error: cannot assign to constant variable");
508
509 add_compile_fail_case("use of undeclared identifier", R"SOURCE(
510fn f() {
511 b = 3;
512}
513 )SOURCE", 1, ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'");
474514}
475515
476516static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) {