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)
104104
105105PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type
106106
107Block : token(LBrace) many(Statement) token(RBrace)
108
109Statement : ExpressionStatement
110
111ExpressionStatement : Expression token(Semicolon)
107Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace)
112108
113109Expression : BoolOrExpression | ReturnExpression
114110
src/analyze.cpp+16-58
......@@ -270,6 +270,7 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
270270 case NodeTypeNumberLiteral:
271271 case NodeTypeStringLiteral:
272272 case NodeTypeUnreachable:
273 case NodeTypeVoid:
273274 case NodeTypeSymbol:
274275 case NodeTypeCastExpr:
275276 case NodeTypePrefixOpExpr:
......@@ -311,8 +312,12 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
311312 for (int i = 0; i < node->data.block.statements.length; i += 1) {
312313 AstNode *child = node->data.block.statements.at(i);
313314 if (return_type == g->builtin_types.entry_unreachable) {
314 add_node_error(g, child,
315 buf_sprintf("unreachable code"));
315 if (child->type == NodeTypeVoid) {
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"));
316321 break;
317322 }
318323 return_type = analyze_expression(g, import, context, nullptr, child);
......@@ -415,6 +420,10 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
415420 return_type = g->builtin_types.entry_unreachable;
416421 break;
417422
423 case NodeTypeVoid:
424 return_type = g->builtin_types.entry_void;
425 break;
426
418427 case NodeTypeSymbol:
419428 // look up symbol in symbol table
420429 zig_panic("TODO");
......@@ -439,59 +448,6 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
439448 return return_type;
440449}
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
495451static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *node) {
496452 switch (node->type) {
497453 case NodeTypeFnDef:
......@@ -512,14 +468,15 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
512468 // TODO: define local variables for parameters
513469 }
514470
515 check_fn_def_control_flow(g, node);
516
517471 BlockContext context;
518472 context.node = node;
519473 context.root = &context;
520474 context.parent = nullptr;
521475 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;
523480 }
524481 break;
525482
......@@ -548,6 +505,7 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
548505 case NodeTypeNumberLiteral:
549506 case NodeTypeStringLiteral:
550507 case NodeTypeUnreachable:
508 case NodeTypeVoid:
551509 case NodeTypeSymbol:
552510 case NodeTypeCastExpr:
553511 case NodeTypePrefixOpExpr:
src/codegen.cpp+10-5
......@@ -401,6 +401,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
401401 case NodeTypeUnreachable:
402402 add_debug_source_node(g, node);
403403 return LLVMBuildUnreachable(g->builder);
404 case NodeTypeVoid:
405 return nullptr;
404406 case NodeTypeNumberLiteral:
405407 {
406408 Buf *number_str = &node->data.number;
......@@ -441,7 +443,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
441443 zig_unreachable();
442444}
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) {
445447 assert(block_node->type == NodeTypeBlock);
446448
447449 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,
450452
451453 add_debug_source_node(g, block_node);
452454
455 LLVMValueRef return_value;
453456 for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
454457 AstNode *statement_node = block_node->data.block.statements.at(i);
455 gen_expr(g, statement_node);
458 return_value = gen_expr(g, statement_node);
456459 }
457460
458 if (add_implicit_return) {
461 if (implicit_return_type == g->builtin_types.entry_void) {
459462 LLVMBuildRetVoid(g->builder);
463 } else if (implicit_return_type != g->builtin_types.entry_unreachable) {
464 LLVMBuildRet(g->builder, return_value);
460465 }
461466
462467 g->block_scopes.pop();
......@@ -552,8 +557,8 @@ static void do_code_gen(CodeGen *g) {
552557 codegen_fn_def->params = allocate<LLVMValueRef>(LLVMCountParams(fn));
553558 LLVMGetParams(fn, codegen_fn_def->params);
554559
555 bool add_implicit_return = codegen_fn_def->add_implicit_return;
556 gen_block(g, import, fn_def_node->data.fn_def.body, add_implicit_return);
560 TypeTableEntry *implicit_return_type = codegen_fn_def->implicit_return_type;
561 gen_block(g, import, fn_def_node->data.fn_def.body, implicit_return_type);
557562
558563 g->block_scopes.pop();
559564 }
src/parser.cpp+33-29
......@@ -89,6 +89,8 @@ const char *node_type_str(NodeType node_type) {
8989 return "PrefixOpExpr";
9090 case NodeTypeUse:
9191 return "Use";
92 case NodeTypeVoid:
93 return "Void";
9294 }
9395 zig_unreachable();
9496}
......@@ -233,6 +235,9 @@ void ast_print(AstNode *node, int indent) {
233235 case NodeTypeUse:
234236 fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.use.path));
235237 break;
238 case NodeTypeVoid:
239 fprintf(stderr, "Void\n");
240 break;
236241 }
237242}
238243
......@@ -416,6 +421,9 @@ static AstNode *ast_parse_type(ParseContext *pc, int token_index, int *new_token
416421 if (token->id == TokenIdKeywordUnreachable) {
417422 node->data.type.type = AstNodeTypeTypePrimitive;
418423 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");
419427 } else if (token->id == TokenIdSymbol) {
420428 node->data.type.type = AstNodeTypeTypePrimitive;
421429 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
569577 AstNode *node = ast_create_node(pc, NodeTypeUnreachable, token);
570578 *token_index += 1;
571579 return node;
580 } else if (token->id == TokenIdKeywordVoid) {
581 AstNode *node = ast_create_node(pc, NodeTypeVoid, token);
582 *token_index += 1;
583 return node;
572584 } else if (token->id == TokenIdSymbol) {
573585 AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
574586 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
10241036}
10251037
10261038/*
1027ExpressionStatement : Expression token(Semicolon)
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);
1039Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace)
10481040*/
10491041static 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) {
10531045 if (mandatory) {
1054 ast_invalid_token_error(pc, l_brace);
1046 ast_invalid_token_error(pc, last_token);
10551047 } else {
10561048 return nullptr;
10571049 }
10581050 }
10591051 *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}
10631060 for (;;) {
1064 Token *token = &pc->tokens->at(*token_index);
1065 if (token->id == TokenIdRBrace) {
1061 AstNode *expression_node = ast_parse_expression(pc, token_index, false);
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) {
10661069 *token_index += 1;
10671070 return node;
1071 } else if (last_token->id == TokenIdSemicolon) {
1072 *token_index += 1;
10681073 } else {
1069 AstNode *statement_node = ast_parse_statement(pc, token_index);
1070 node->data.block.statements.append(statement_node);
1074 ast_invalid_token_error(pc, last_token);
10711075 }
10721076 }
10731077 zig_unreachable();
src/parser.hpp+1
......@@ -38,6 +38,7 @@ enum NodeType {
3838 NodeTypePrefixOpExpr,
3939 NodeTypeFnCallExpr,
4040 NodeTypeUse,
41 NodeTypeVoid,
4142};
4243
4344struct AstNodeRoot {
src/semantic_info.hpp+1-1
......@@ -106,7 +106,7 @@ struct TypeNode {
106106};
107107
108108struct FnDefNode {
109 bool add_implicit_return;
109 TypeTableEntry *implicit_return_type;
110110 bool skip;
111111 LLVMValueRef *params;
112112};
src/tokenizer.cpp+3
......@@ -181,6 +181,8 @@ static void end_token(Tokenize *t) {
181181 t->cur_tok->id = TokenIdKeywordAs;
182182 } else if (mem_eql_str(token_mem, token_len, "use")) {
183183 t->cur_tok->id = TokenIdKeywordUse;
184 } else if (mem_eql_str(token_mem, token_len, "void")) {
185 t->cur_tok->id = TokenIdKeywordVoid;
184186 }
185187
186188 t->cur_tok = nullptr;
......@@ -574,6 +576,7 @@ static const char * token_name(Token *token) {
574576 case TokenIdKeywordExport: return "Export";
575577 case TokenIdKeywordAs: return "As";
576578 case TokenIdKeywordUse: return "Use";
579 case TokenIdKeywordVoid: return "Void";
577580 case TokenIdLParen: return "LParen";
578581 case TokenIdRParen: return "RParen";
579582 case TokenIdComma: return "Comma";
src/tokenizer.hpp+1
......@@ -23,6 +23,7 @@ enum TokenId {
2323 TokenIdKeywordExport,
2424 TokenIdKeywordAs,
2525 TokenIdKeywordUse,
26 TokenIdKeywordVoid,
2627 TokenIdLParen,
2728 TokenIdRParen,
2829 TokenIdComma,
test/run_tests.cpp+2-2
......@@ -209,11 +209,11 @@ fn a() {}
209209
210210 add_compile_fail_case("unreachable with return", R"SOURCE(
211211fn 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
214214 add_compile_fail_case("control reaches end of non-void function", R"SOURCE(
215215fn 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
218218 add_compile_fail_case("undefined function call", R"SOURCE(
219219fn a() {