authorgravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2015-12-01 15:54:46-07:00
committergravatar for thejoshwolfe@gmail.comJosh Wolfe <thejoshwolfe@gmail.com> 2015-12-01 15:54:46-07:00
log1ed926c3216016d73e83377a295869d8579e2fde
treedfc72c0a73844cf440a83e8b67d6c16683c63c2c
parentc6a9ab107bb383759f3627ed828ea2a779844f2b

implicit void statements and all tests pass with type checking


9 files changed, 68 insertions(+), 100 deletions(-)

README.md+1-5
...@@ -104,11 +104,7 @@ Type : token(Symbol) | PointerType | token(Unreachable)...@@ -104,11 +104,7 @@ Type : token(Symbol) | PointerType | token(Unreachable)
104104
105PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type105PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type
106106
107Block : token(LBrace) many(Statement) token(RBrace)107Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace)
108
109Statement : ExpressionStatement
110
111ExpressionStatement : Expression token(Semicolon)
112108
113Expression : BoolOrExpression | ReturnExpression109Expression : BoolOrExpression | ReturnExpression
114110
src/analyze.cpp+16-58
...@@ -270,6 +270,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,...@@ -270,6 +270,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
270 case NodeTypeNumberLiteral:270 case NodeTypeNumberLiteral:
271 case NodeTypeStringLiteral:271 case NodeTypeStringLiteral:
272 case NodeTypeUnreachable:272 case NodeTypeUnreachable:
273 case NodeTypeVoid:
273 case NodeTypeSymbol:274 case NodeTypeSymbol:
274 case NodeTypeCastExpr:275 case NodeTypeCastExpr:
275 case NodeTypePrefixOpExpr:276 case NodeTypePrefixOpExpr:
...@@ -311,8 +312,12 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -311,8 +312,12 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
311 for (int i = 0; i < node->data.block.statements.length; i += 1) {312 for (int i = 0; i < node->data.block.statements.length; i += 1) {
312 AstNode *child = node->data.block.statements.at(i);313 AstNode *child = node->data.block.statements.at(i);
313 if (return_type == g->builtin_types.entry_unreachable) {314 if (return_type == g->builtin_types.entry_unreachable) {
314 add_node_error(g, child,315 if (child->type == NodeTypeVoid) {
315 buf_sprintf("unreachable code"));316 // {unreachable;void;void} is allowed.
317 // ignore void statements once we enter unreachable land.
318 continue;
319 }
320 add_node_error(g, child, buf_sprintf("unreachable code"));
316 break;321 break;
317 }322 }
318 return_type = analyze_expression(g, import, context, nullptr, child);323 return_type = analyze_expression(g, import, context, nullptr, child);
...@@ -415,6 +420,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -415,6 +420,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
415 return_type = g->builtin_types.entry_unreachable;420 return_type = g->builtin_types.entry_unreachable;
416 break;421 break;
417422
423 case NodeTypeVoid:
424 return_type = g->builtin_types.entry_void;
425 break;
426
418 case NodeTypeSymbol:427 case NodeTypeSymbol:
419 // look up symbol in symbol table428 // look up symbol in symbol table
420 zig_panic("TODO");429 zig_panic("TODO");
...@@ -439,59 +448,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,...@@ -439,59 +448,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
439 return return_type;448 return return_type;
440}449}
441450
442static void check_fn_def_control_flow(CodeGen *g, AstNode *node) {
443 // Follow the execution flow and make sure the code returns appropriately.
444 // * A `return` statement in an unreachable type function should be an error.
445 // * Control flow should not be able to reach the end of an unreachable type function.
446 // * Functions that have a type other than void should not return without a value.
447 // * void functions without explicit return statements at the end need the
448 // add_implicit_return flag set on the codegen node.
449 assert(node->type == NodeTypeFnDef);
450 AstNode *proto_node = node->data.fn_def.fn_proto;
451 assert(proto_node->type == NodeTypeFnProto);
452 AstNode *return_type_node = proto_node->data.fn_proto.return_type;
453 assert(return_type_node->type == NodeTypeType);
454
455 node->codegen_node = allocate<CodeGenNode>(1);
456 FnDefNode *codegen_fn_def = &node->codegen_node->data.fn_def_node;
457
458 assert(return_type_node->codegen_node);
459 TypeTableEntry *type_entry = return_type_node->codegen_node->data.type_node.entry;
460 assert(type_entry);
461
462 AstNode *body_node = node->data.fn_def.body;
463 assert(body_node->type == NodeTypeBlock);
464
465 // TODO once we understand types, do this pass after type checking, and
466 // if an expression has an unreachable value then stop looking at statements after
467 // it. then we can remove the check to `unreachable` in the end of this function.
468 bool prev_statement_return = false;
469 for (int i = 0; i < body_node->data.block.statements.length; i += 1) {
470 AstNode *statement_node = body_node->data.block.statements.at(i);
471 if (statement_node->type == NodeTypeReturnExpr) {
472 if (type_entry == g->builtin_types.entry_unreachable) {
473 add_node_error(g, statement_node,
474 buf_sprintf("return statement in function with unreachable return type"));
475 return;
476 } else {
477 prev_statement_return = true;
478 }
479 } else if (prev_statement_return) {
480 add_node_error(g, statement_node,
481 buf_sprintf("unreachable code"));
482 }
483 }
484
485 if (!prev_statement_return) {
486 if (type_entry == g->builtin_types.entry_void) {
487 codegen_fn_def->add_implicit_return = true;
488 } else if (type_entry != g->builtin_types.entry_unreachable) {
489 add_node_error(g, node,
490 buf_sprintf("control reaches end of non-void function"));
491 }
492 }
493}
494
495static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *node) {451static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *node) {
496 switch (node->type) {452 switch (node->type) {
497 case NodeTypeFnDef:453 case NodeTypeFnDef:
...@@ -512,14 +468,15 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,...@@ -512,14 +468,15 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
512 // TODO: define local variables for parameters468 // TODO: define local variables for parameters
513 }469 }
514470
515 check_fn_def_control_flow(g, node);
516
517 BlockContext context;471 BlockContext context;
518 context.node = node;472 context.node = node;
519 context.root = &context;473 context.root = &context;
520 context.parent = nullptr;474 context.parent = nullptr;
521 TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry;475 TypeTableEntry *expected_type = fn_proto->return_type->codegen_node->data.type_node.entry;
522 analyze_expression(g, import, &context, expected_type, node->data.fn_def.body);476 TypeTableEntry *block_return_type = analyze_expression(g, import, &context, expected_type, node->data.fn_def.body);
477
478 node->codegen_node = allocate<CodeGenNode>(1);
479 node->codegen_node->data.fn_def_node.implicit_return_type = block_return_type;
523 }480 }
524 break;481 break;
525482
...@@ -548,6 +505,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,...@@ -548,6 +505,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
548 case NodeTypeNumberLiteral:505 case NodeTypeNumberLiteral:
549 case NodeTypeStringLiteral:506 case NodeTypeStringLiteral:
550 case NodeTypeUnreachable:507 case NodeTypeUnreachable:
508 case NodeTypeVoid:
551 case NodeTypeSymbol:509 case NodeTypeSymbol:
552 case NodeTypeCastExpr:510 case NodeTypeCastExpr:
553 case NodeTypePrefixOpExpr:511 case NodeTypePrefixOpExpr:
src/codegen.cpp+10-5
...@@ -401,6 +401,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {...@@ -401,6 +401,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
401 case NodeTypeUnreachable:401 case NodeTypeUnreachable:
402 add_debug_source_node(g, node);402 add_debug_source_node(g, node);
403 return LLVMBuildUnreachable(g->builder);403 return LLVMBuildUnreachable(g->builder);
404 case NodeTypeVoid:
405 return nullptr;
404 case NodeTypeNumberLiteral:406 case NodeTypeNumberLiteral:
405 {407 {
406 Buf *number_str = &node->data.number;408 Buf *number_str = &node->data.number;
...@@ -441,7 +443,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {...@@ -441,7 +443,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
441 zig_unreachable();443 zig_unreachable();
442}444}
443445
444static void gen_block(CodeGen *g, ImportTableEntry *import, AstNode *block_node, bool add_implicit_return) {446static void gen_block(CodeGen *g, ImportTableEntry *import, AstNode *block_node, TypeTableEntry *implicit_return_type) {
445 assert(block_node->type == NodeTypeBlock);447 assert(block_node->type == NodeTypeBlock);
446448
447 LLVMZigDILexicalBlock *di_block = LLVMZigCreateLexicalBlock(g->dbuilder, g->block_scopes.last(),449 LLVMZigDILexicalBlock *di_block = LLVMZigCreateLexicalBlock(g->dbuilder, g->block_scopes.last(),
...@@ -450,13 +452,16 @@ static void gen_block(CodeGen *g, ImportTableEntry *import, AstNode *block_node,...@@ -450,13 +452,16 @@ static void gen_block(CodeGen *g, ImportTableEntry *import, AstNode *block_node,
450452
451 add_debug_source_node(g, block_node);453 add_debug_source_node(g, block_node);
452454
455 LLVMValueRef return_value;
453 for (int i = 0; i < block_node->data.block.statements.length; i += 1) {456 for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
454 AstNode *statement_node = block_node->data.block.statements.at(i);457 AstNode *statement_node = block_node->data.block.statements.at(i);
455 gen_expr(g, statement_node);458 return_value = gen_expr(g, statement_node);
456 }459 }
457460
458 if (add_implicit_return) {461 if (implicit_return_type == g->builtin_types.entry_void) {
459 LLVMBuildRetVoid(g->builder);462 LLVMBuildRetVoid(g->builder);
463 } else if (implicit_return_type != g->builtin_types.entry_unreachable) {
464 LLVMBuildRet(g->builder, return_value);
460 }465 }
461466
462 g->block_scopes.pop();467 g->block_scopes.pop();
...@@ -552,8 +557,8 @@ static void do_code_gen(CodeGen *g) {...@@ -552,8 +557,8 @@ static void do_code_gen(CodeGen *g) {
552 codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn));557 codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn));
553 LLVMGetParams(fn, codegen_fn_def->params);558 LLVMGetParams(fn, codegen_fn_def->params);
554559
555 bool add_implicit_return = codegen_fn_def->add_implicit_return;560 TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;
556 gen_block(g, import, fn_def_node->data.fn_def.body, add_implicit_return);561 gen_block(g, import, fn_def_node->data.fn_def.body, implicit_return_type);
557562
558 g->block_scopes.pop();563 g->block_scopes.pop();
559 }564 }
src/parser.cpp+33-29
...@@ -89,6 +89,8 @@ const char *node_type_str(NodeType node_type) {...@@ -89,6 +89,8 @@ const char *node_type_str(NodeType node_type) {
89 return "PrefixOpExpr";89 return "PrefixOpExpr";
90 case NodeTypeUse:90 case NodeTypeUse:
91 return "Use";91 return "Use";
92 case NodeTypeVoid:
93 return "Void";
92 }94 }
93 zig_unreachable();95 zig_unreachable();
94}96}
...@@ -233,6 +235,9 @@ void ast_print(AstNode *node, int indent) {...@@ -233,6 +235,9 @@ void ast_print(AstNode *node, int indent) {
233 case NodeTypeUse:235 case NodeTypeUse:
234 fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path));236 fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path));
235 break;237 break;
238 case NodeTypeVoid:
239 fprintf(stderr, "Void\n");
240 break;
236 }241 }
237}242}
238243
...@@ -416,6 +421,9 @@ static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token...@@ -416,6 +421,9 @@ static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token
416 if (token->id == TokenIdKeywordUnreachable) {421 if (token->id == TokenIdKeywordUnreachable) {
417 node->data.type.type = AstNodeTypeTypePrimitive;422 node->data.type.type = AstNodeTypeTypePrimitive;
418 buf_init_from_str(&node->data.type.primitive_name, "unreachable");423 buf_init_from_str(&node->data.type.primitive_name, "unreachable");
424 } else if (token->id == TokenIdKeywordVoid) {
425 node->data.type.type = AstNodeTypeTypePrimitive;
426 buf_init_from_str(&node->data.type.primitive_name, "void");
419 } else if (token->id == TokenIdSymbol) {427 } else if (token->id == TokenIdSymbol) {
420 node->data.type.type = AstNodeTypeTypePrimitive;428 node->data.type.type = AstNodeTypeTypePrimitive;
421 ast_buf_from_token(pc, token, &node->data.type.primitive_name);429 ast_buf_from_token(pc, token, &node->data.type.primitive_name);
...@@ -569,6 +577,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool...@@ -569,6 +577,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
569 AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token);577 AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token);
570 *token_index += 1;578 *token_index += 1;
571 return node;579 return node;
580 } else if (token->id == TokenIdKeywordVoid) {
581 AstNode *node = ast_create_node(pc, NodeTypeVoid, token);
582 *token_index += 1;
583 return node;
572 } else if (token->id == TokenIdSymbol) {584 } else if (token->id == TokenIdSymbol) {
573 AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);585 AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
574 ast_buf_from_token(pc, token, &node->data.symbol);586 ast_buf_from_token(pc, token, &node->data.symbol);
...@@ -1024,50 +1036,42 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma...@@ -1024,50 +1036,42 @@ static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool ma
1024}1036}
10251037
1026/*1038/*
1027ExpressionStatement : Expression token(Semicolon)1039Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace)
1028*/
1029static AstNode *ast_parse_expression_statement(ParseContext *pc, int *token_index) {
1030 AstNode *expr_node = ast_parse_expression(pc, token_index, true);
1031
1032 Token *semicolon = &pc->tokens->at(*token_index);
1033 *token_index += 1;
1034 ast_expect_token(pc, semicolon, TokenIdSemicolon);
1035
1036 return expr_node;
1037}
1038
1039/*
1040Statement : ExpressionStatement
1041*/
1042static AstNode *ast_parse_statement(ParseContext *pc, int *token_index) {
1043 return ast_parse_expression_statement(pc, token_index);
1044}
1045
1046/*
1047Block : token(LBrace) many(Statement) token(RBrace);
1048*/1040*/
1049static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) {1041static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) {
1050 Token *l_brace = &pc->tokens->at(*token_index);1042 Token *last_token = &pc->tokens->at(*token_index);
10511043
1052 if (l_brace->id != TokenIdLBrace) {1044 if (last_token->id != TokenIdLBrace) {
1053 if (mandatory) {1045 if (mandatory) {
1054 ast_invalid_token_error(pc, l_brace);1046 ast_invalid_token_error(pc, last_token);
1055 } else {1047 } else {
1056 return nullptr;1048 return nullptr;
1057 }1049 }
1058 }1050 }
1059 *token_index += 1;1051 *token_index += 1;
10601052
1061 AstNode *node = ast_create_node(pc, NodeTypeBlock, l_brace);1053 AstNode *node = ast_create_node(pc, NodeTypeBlock, last_token);
10621054
1055 // {} -> {void}
1056 // {;} -> {void;void}
1057 // {2} -> {2}
1058 // {2;} -> {2;void}
1059 // {;2} -> {void;2}
1063 for (;;) {1060 for (;;) {
1064 Token *token = &pc->tokens->at(*token_index);1061 AstNode *expression_node = ast_parse_expression(pc, token_index, false);
1065 if (token->id == TokenIdRBrace) {1062 if (!expression_node) {
1063 expression_node = ast_create_node(pc, NodeTypeVoid, last_token);
1064 }
1065 node->data.block.statements.append(expression_node);
1066
1067 last_token = &pc->tokens->at(*token_index);
1068 if (last_token->id == TokenIdRBrace) {
1066 *token_index += 1;1069 *token_index += 1;
1067 return node;1070 return node;
1071 } else if (last_token->id == TokenIdSemicolon) {
1072 *token_index += 1;
1068 } else {1073 } else {
1069 AstNode *statement_node = ast_parse_statement(pc, token_index);1074 ast_invalid_token_error(pc, last_token);
1070 node->data.block.statements.append(statement_node);
1071 }1075 }
1072 }1076 }
1073 zig_unreachable();1077 zig_unreachable();
src/parser.hpp+1
...@@ -38,6 +38,7 @@ enum NodeType {...@@ -38,6 +38,7 @@ enum NodeType {
38 NodeTypePrefixOpExpr,38 NodeTypePrefixOpExpr,
39 NodeTypeFnCallExpr,39 NodeTypeFnCallExpr,
40 NodeTypeUse,40 NodeTypeUse,
41 NodeTypeVoid,
41};42};
4243
43struct AstNodeRoot {44struct AstNodeRoot {
src/semantic_info.hpp+1-1
...@@ -106,7 +106,7 @@ struct TypeNode {...@@ -106,7 +106,7 @@ struct TypeNode {
106};106};
107107
108struct FnDefNode {108struct FnDefNode {
109 bool add_implicit_return;109 TypeTableEntry *implicit_return_type;
110 bool skip;110 bool skip;
111 LLVMValueRef *params;111 LLVMValueRef *params;
112};112};
src/tokenizer.cpp+3
...@@ -181,6 +181,8 @@ static void end_token(Tokenize *t) {...@@ -181,6 +181,8 @@ static void end_token(Tokenize *t) {
181 t->cur_tok->id = TokenIdKeywordAs;181 t->cur_tok->id = TokenIdKeywordAs;
182 } else if (mem_eql_str(token_mem, token_len, "use")) {182 } else if (mem_eql_str(token_mem, token_len, "use")) {
183 t->cur_tok->id = TokenIdKeywordUse;183 t->cur_tok->id = TokenIdKeywordUse;
184 } else if (mem_eql_str(token_mem, token_len, "void")) {
185 t->cur_tok->id = TokenIdKeywordVoid;
184 }186 }
185187
186 t->cur_tok = nullptr;188 t->cur_tok = nullptr;
...@@ -574,6 +576,7 @@ static const char * token_name(Token *token) {...@@ -574,6 +576,7 @@ static const char * token_name(Token *token) {
574 case TokenIdKeywordExport: return "Export";576 case TokenIdKeywordExport: return "Export";
575 case TokenIdKeywordAs: return "As";577 case TokenIdKeywordAs: return "As";
576 case TokenIdKeywordUse: return "Use";578 case TokenIdKeywordUse: return "Use";
579 case TokenIdKeywordVoid: return "Void";
577 case TokenIdLParen: return "LParen";580 case TokenIdLParen: return "LParen";
578 case TokenIdRParen: return "RParen";581 case TokenIdRParen: return "RParen";
579 case TokenIdComma: return "Comma";582 case TokenIdComma: return "Comma";
src/tokenizer.hpp+1
...@@ -23,6 +23,7 @@ enum TokenId {...@@ -23,6 +23,7 @@ enum TokenId {
23 TokenIdKeywordExport,23 TokenIdKeywordExport,
24 TokenIdKeywordAs,24 TokenIdKeywordAs,
25 TokenIdKeywordUse,25 TokenIdKeywordUse,
26 TokenIdKeywordVoid,
26 TokenIdLParen,27 TokenIdLParen,
27 TokenIdRParen,28 TokenIdRParen,
28 TokenIdComma,29 TokenIdComma,
test/run_tests.cpp+2-2
...@@ -209,11 +209,11 @@ fn a() {}...@@ -209,11 +209,11 @@ fn a() {}
209209
210 add_compile_fail_case("unreachable with return", R"SOURCE(210 add_compile_fail_case("unreachable with return", R"SOURCE(
211fn a() -> unreachable {return;}211fn a() -> unreachable {return;}
212 )SOURCE", 1, ".tmp_source.zig:2:24: error: return statement in function with unreachable return type");212 )SOURCE", 1, ".tmp_source.zig:2:24: error: type mismatch. expected unreachable. got void");
213213
214 add_compile_fail_case("control reaches end of non-void function", R"SOURCE(214 add_compile_fail_case("control reaches end of non-void function", R"SOURCE(
215fn a() -> i32 {}215fn a() -> i32 {}
216 )SOURCE", 1, ".tmp_source.zig:2:1: error: control reaches end of non-void function");216 )SOURCE", 1, ".tmp_source.zig:2:15: error: type mismatch. expected i32. got void");
217217
218 add_compile_fail_case("undefined function call", R"SOURCE(218 add_compile_fail_case("undefined function call", R"SOURCE(
219fn a() {219fn a() {