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 @@...@@ -1,3 +1,5 @@
1export executable "expressions";
2
1#link("c")3#link("c")
2extern {4extern {
3 fn puts(s: *const u8) -> i32;5 fn puts(s: *const u8) -> i32;
...@@ -28,6 +30,8 @@ export fn _start() -> unreachable {...@@ -28,6 +30,8 @@ export fn _start() -> unreachable {
2830
29 void_fun(1, void, 2);31 void_fun(1, void, 2);
3032
33 test_mutable_vars();
34
31 other_exit();35 other_exit();
32}36}
3337
...@@ -38,3 +42,15 @@ fn void_fun(a : i32, b : void, c : i32) -> void {...@@ -38,3 +42,15 @@ fn void_fun(a : i32, b : void, c : i32) -> void {
38 let w : void = z; // void42 let w : void = z; // void
39 if (x + y == 4) { return w; }43 if (x + y == 4) { return w; }
40}44}
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) {...@@ -362,6 +362,13 @@ static BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
362 else362 else
363 context->root = context;363 context->root = context;
364 context->variable_table.init(8);364 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
365 return context;372 return context;
366}373}
367374
...@@ -388,8 +395,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -388,8 +395,13 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
388 return_type = g->builtin_types.entry_void;395 return_type = g->builtin_types.entry_void;
389 for (int i = 0; i < node->data.block.statements.length; i += 1) {396 for (int i = 0; i < node->data.block.statements.length; i += 1) {
390 AstNode *child = node->data.block.statements.at(i);397 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;
392 continue;403 continue;
404 }
393 if (return_type == g->builtin_types.entry_unreachable) {405 if (return_type == g->builtin_types.entry_unreachable) {
394 if (child->type == NodeTypeVoid) {406 if (child->type == NodeTypeVoid) {
395 // {unreachable;void;void} is allowed.407 // {unreachable;void;void} is allowed.
...@@ -457,6 +469,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -457,6 +469,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
457 LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);469 LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
458 buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol);470 buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol);
459 variable_entry->type = type;471 variable_entry->type = type;
472 variable_entry->is_const = variable_declaration->is_const;
473 variable_entry->decl_node = node;
460 context->variable_table.put(&variable_entry->name, variable_entry);474 context->variable_table.put(&variable_entry->name, variable_entry);
461 }475 }
462 return_type = g->builtin_types.entry_void;476 return_type = g->builtin_types.entry_void;
...@@ -482,6 +496,32 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -482,6 +496,32 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
482 case NodeTypeBinOpExpr:496 case NodeTypeBinOpExpr:
483 {497 {
484 switch (node->data.bin_op_expr.bin_op) {498 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 }
485 case BinOpTypeBoolOr:525 case BinOpTypeBoolOr:
486 case BinOpTypeBoolAnd:526 case BinOpTypeBoolAnd:
487 analyze_expression(g, import, context, g->builtin_types.entry_bool,527 analyze_expression(g, import, context, g->builtin_types.entry_bool,
...@@ -721,7 +761,10 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,...@@ -721,7 +761,10 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
721 AstNode *fn_proto_node = node->data.fn_def.fn_proto;761 AstNode *fn_proto_node = node->data.fn_def.fn_proto;
722 assert(fn_proto_node->type == NodeTypeFnProto);762 assert(fn_proto_node->type == NodeTypeFnProto);
723763
764 assert(!node->codegen_node);
765 node->codegen_node = allocate<CodeGenNode>(1);
724 BlockContext *context = new_block_context(node, nullptr);766 BlockContext *context = new_block_context(node, nullptr);
767 node->codegen_node->data.fn_def_node.block_context = context;
725768
726 AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;769 AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto;
727 for (int i = 0; i < fn_proto->params.length; i += 1) {770 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,...@@ -736,6 +779,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
736 LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);779 LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
737 buf_init_from_buf(&variable_entry->name, &param_decl->name);780 buf_init_from_buf(&variable_entry->name, &param_decl->name);
738 variable_entry->type = type;781 variable_entry->type = type;
782 variable_entry->is_const = true;
783 variable_entry->decl_node = param_decl_node;
739784
740 LocalVariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name);785 LocalVariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name);
741 if (!existing_entry) {786 if (!existing_entry) {
...@@ -756,9 +801,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,...@@ -756,9 +801,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
756 TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry;801 TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry;
757 TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);802 TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body);
758803
759 node->codegen_node = allocate<CodeGenNode>(1);
760 node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type;804 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
763 {806 {
764 FnTableEntry *fn_table_entry = fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry;807 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) {...@@ -102,6 +102,7 @@ static int count_non_void_params(CodeGen *g, ZigList<AstNode *> *params) {
102}102}
103103
104static void add_debug_source_node(CodeGen *g, AstNode *node) {104static 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
105 LLVMZigSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, g->block_scopes.last());106 LLVMZigSetCurrentDebugLocation(g->builder, node->line + 1, node->column + 1, g->block_scopes.last());
106}107}
107108
...@@ -210,6 +211,8 @@ static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {...@@ -210,6 +211,8 @@ static LLVMValueRef gen_arithmetic_bin_op_expr(CodeGen *g, AstNode *node) {
210 LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);211 LLVMValueRef val2 = gen_expr(g, node->data.bin_op_expr.op2);
211212
212 switch (node->data.bin_op_expr.bin_op) {213 switch (node->data.bin_op_expr.bin_op) {
214 case BinOpTypeAssign:
215 zig_panic("TODO assignment");
213 case BinOpTypeBinOr:216 case BinOpTypeBinOr:
214 add_debug_source_node(g, node);217 add_debug_source_node(g, node);
215 return LLVMBuildOr(g->builder, val1, val2, "");218 return LLVMBuildOr(g->builder, val1, val2, "");
...@@ -358,8 +361,28 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {...@@ -358,8 +361,28 @@ static LLVMValueRef gen_bool_or_expr(CodeGen *g, AstNode *expr_node) {
358 return phi;361 return phi;
359}362}
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
361static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {382static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
362 switch (node->data.bin_op_expr.bin_op) {383 switch (node->data.bin_op_expr.bin_op) {
384 case BinOpTypeAssign:
385 return gen_assign_expr(g, node);
363 case BinOpTypeInvalid:386 case BinOpTypeInvalid:
364 zig_unreachable();387 zig_unreachable();
365 case BinOpTypeBoolOr:388 case BinOpTypeBoolOr:
...@@ -498,9 +521,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {...@@ -498,9 +521,20 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
498 case NodeTypeVariableDeclaration:521 case NodeTypeVariableDeclaration:
499 {522 {
500 LocalVariableTableEntry *variable = find_local_variable(node->codegen_node->expr_node.block_context, &node->data.variable_declaration.symbol);523 LocalVariableTableEntry *variable = find_local_variable(node->codegen_node->expr_node.block_context, &node->data.variable_declaration.symbol);
501 assert(node->data.variable_declaration.expr);524 if (variable->is_const) {
502 variable->value_ref = gen_expr(g, node->data.variable_declaration.expr);525 assert(node->data.variable_declaration.expr);
503 return nullptr;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 }
504 }538 }
505 case NodeTypeCastExpr:539 case NodeTypeCastExpr:
506 return gen_cast_expr(g, node);540 return gen_cast_expr(g, node);
...@@ -542,7 +576,11 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {...@@ -542,7 +576,11 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
542 case NodeTypeSymbol:576 case NodeTypeSymbol:
543 {577 {
544 LocalVariableTableEntry *variable = find_local_variable(node->codegen_node->expr_node.block_context, &node->data.symbol);578 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 }
546 }584 }
547 case NodeTypeBlock:585 case NodeTypeBlock:
548 return gen_block(g, node, nullptr);586 return gen_block(g, node, nullptr);
...@@ -551,11 +589,15 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {...@@ -551,11 +589,15 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
551 return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block);589 return LLVMBuildBr(g->builder, node->codegen_node->data.label_entry->basic_block);
552 case NodeTypeLabel:590 case NodeTypeLabel:
553 {591 {
554 LLVMBasicBlockRef basic_block = node->codegen_node->data.label_entry->basic_block;592 LabelTableEntry *label_entry = node->codegen_node->data.label_entry;
555 add_debug_source_node(g, node);593 assert(label_entry);
556 LLVMValueRef result = LLVMBuildBr(g->builder, basic_block);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 }
557 LLVMPositionBuilderAtEnd(g->builder, basic_block);599 LLVMPositionBuilderAtEnd(g->builder, basic_block);
558 return result;600 return nullptr;
559 }601 }
560 case NodeTypeRoot:602 case NodeTypeRoot:
561 case NodeTypeRootExportDecl:603 case NodeTypeRootExportDecl:
...@@ -696,6 +738,24 @@ static void do_code_gen(CodeGen *g) {...@@ -696,6 +738,24 @@ static void do_code_gen(CodeGen *g) {
696738
697 build_label_blocks(g, fn_def_node->data.fn_def.body);739 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
699 TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;759 TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;
700 gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type);760 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) {...@@ -33,6 +33,7 @@ static const char *bin_op_str(BinOpType bin_op) {
33 case BinOpTypeMult: return "*";33 case BinOpTypeMult: return "*";
34 case BinOpTypeDiv: return "/";34 case BinOpTypeDiv: return "/";
35 case BinOpTypeMod: return "%";35 case BinOpTypeMod: return "%";
36 case BinOpTypeAssign: return "=";
36 }37 }
37 zig_unreachable();38 zig_unreachable();
38}39}
...@@ -1096,7 +1097,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m...@@ -1096,7 +1097,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m
1096}1097}
10971098
1098/*1099/*
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))
1100*/1101*/
1101static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) {1102static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token_index, bool mandatory) {
1102 Token *let_tok = &pc->tokens->at(*token_index);1103 Token *let_tok = &pc->tokens->at(*token_index);
...@@ -1104,9 +1105,21 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token...@@ -1104,9 +1105,21 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, int *token
1104 *token_index += 1;1105 *token_index += 1;
1105 AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, let_tok);1106 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
1108 *token_index += 1;1122 *token_index += 1;
1109 ast_expect_token(pc, name_token, TokenIdSymbol);
1110 ast_buf_from_token(pc, name_token, &node->data.variable_declaration.symbol);1123 ast_buf_from_token(pc, name_token, &node->data.variable_declaration.symbol);
11111124
1112 Token *eq_or_colon = &pc->tokens->at(*token_index);1125 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...@@ -1178,7 +1191,30 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma
1178}1191}
11791192
1180/*1193/*
1181NonBlockExpression : ReturnExpression | VariableDeclaration | BoolOrExpression1194AssignmentExpression : 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
1182*/1218*/
1183static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) {1219static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) {
1184 Token *token = &pc->tokens->at(*token_index);1220 Token *token = &pc->tokens->at(*token_index);
...@@ -1191,9 +1227,10 @@ static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, boo...@@ -1191,9 +1227,10 @@ static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, boo
1191 if (variable_declaration_expr)1227 if (variable_declaration_expr)
1192 return variable_declaration_expr;1228 return variable_declaration_expr;
11931229
1194 AstNode *bool_or_expr = ast_parse_bool_or_expr(pc, token_index, false);1230
1195 if (bool_or_expr)1231 AstNode *ass_expr = ast_parse_ass_expr(pc, token_index, false);
1196 return bool_or_expr;1232 if (ass_expr)
1233 return ass_expr;
11971234
1198 if (mandatory)1235 if (mandatory)
1199 ast_invalid_token_error(pc, token);1236 ast_invalid_token_error(pc, token);
src/parser.hpp+2-1
...@@ -101,6 +101,7 @@ struct AstNodeReturnExpr {...@@ -101,6 +101,7 @@ struct AstNodeReturnExpr {
101101
102struct AstNodeVariableDeclaration {102struct AstNodeVariableDeclaration {
103 Buf symbol;103 Buf symbol;
104 bool is_const;
104 // one or both of type and expr will be non null105 // one or both of type and expr will be non null
105 AstNode *type;106 AstNode *type;
106 AstNode *expr;107 AstNode *expr;
...@@ -108,7 +109,7 @@ struct AstNodeVariableDeclaration {...@@ -108,7 +109,7 @@ struct AstNodeVariableDeclaration {
108109
109enum BinOpType {110enum BinOpType {
110 BinOpTypeInvalid,111 BinOpTypeInvalid,
111 // TODO: include assignment?112 BinOpTypeAssign,
112 BinOpTypeBoolOr,113 BinOpTypeBoolOr,
113 BinOpTypeBoolAnd,114 BinOpTypeBoolAnd,
114 BinOpTypeCmpEq,115 BinOpTypeCmpEq,
src/semantic_info.hpp+9
...@@ -42,6 +42,7 @@ struct LabelTableEntry {...@@ -42,6 +42,7 @@ struct LabelTableEntry {
42 AstNode *label_node;42 AstNode *label_node;
43 LLVMBasicBlockRef basic_block;43 LLVMBasicBlockRef basic_block;
44 bool used;44 bool used;
45 bool entered_from_fallthrough;
45};46};
4647
47struct FnTableEntry {48struct FnTableEntry {
...@@ -116,6 +117,8 @@ struct LocalVariableTableEntry {...@@ -116,6 +117,8 @@ struct LocalVariableTableEntry {
116 Buf name;117 Buf name;
117 TypeTableEntry *type;118 TypeTableEntry *type;
118 LLVMValueRef value_ref;119 LLVMValueRef value_ref;
120 bool is_const;
121 AstNode *decl_node;
119};122};
120123
121struct BlockContext {124struct BlockContext {
...@@ -137,6 +140,7 @@ struct FnDefNode {...@@ -137,6 +140,7 @@ struct FnDefNode {
137 TypeTableEntry *implicit_return_type;140 TypeTableEntry *implicit_return_type;
138 BlockContext *block_context;141 BlockContext *block_context;
139 bool skip;142 bool skip;
143 ZigList<BlockContext *> all_block_contexts;
140};144};
141145
142struct ExprNode {146struct ExprNode {
...@@ -146,12 +150,17 @@ struct ExprNode {...@@ -146,12 +150,17 @@ struct ExprNode {
146 BlockContext *block_context;150 BlockContext *block_context;
147};151};
148152
153struct AssignNode {
154 LocalVariableTableEntry *var_entry;
155};
156
149struct CodeGenNode {157struct CodeGenNode {
150 union {158 union {
151 TypeNode type_node; // for NodeTypeType159 TypeNode type_node; // for NodeTypeType
152 FnDefNode fn_def_node; // for NodeTypeFnDef160 FnDefNode fn_def_node; // for NodeTypeFnDef
153 FnProtoNode fn_proto_node; // for NodeTypeFnProto161 FnProtoNode fn_proto_node; // for NodeTypeFnProto
154 LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel162 LabelTableEntry *label_entry; // for NodeTypeGoto and NodeTypeLabel
163 AssignNode assign_node; // for NodeTypeBinOpExpr where op is BinOpTypeAssign
155 } data;164 } data;
156 ExprNode expr_node; // for all the expression nodes165 ExprNode expr_node; // for all the expression nodes
157};166};
test/run_tests.cpp+41-1
...@@ -331,6 +331,27 @@ fn void_fun(a : i32, b : void, c : i32) {...@@ -331,6 +331,27 @@ fn void_fun(a : i32, b : void, c : i32) {
331 return vv;331 return vv;
332}332}
333 )SOURCE", "OK\n");333 )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");
334}355}
335356
336static void add_compile_failure_test_cases(void) {357static void add_compile_failure_test_cases(void) {
...@@ -467,10 +488,29 @@ export fn f(a : void) {}...@@ -467,10 +488,29 @@ export fn f(a : void) {}
467 )SOURCE", 1, ".tmp_source.zig:2:17: error: parameter of type 'void' not allowed on exported functions");488 )SOURCE", 1, ".tmp_source.zig:2:17: error: parameter of type 'void' not allowed on exported functions");
468489
469 add_compile_fail_case("unused label", R"SOURCE(490 add_compile_fail_case("unused label", R"SOURCE(
470export fn f() {491fn f() {
471a_label:492a_label:
472}493}
473 )SOURCE", 1, ".tmp_source.zig:3:1: error: label 'a_label' defined but not used");494 )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'");
474}514}
475515
476static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) {516static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) {