authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-11-27 10:52:31-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-11-27 10:52:31-07:00
log9ca9a2c5540683a54bae597c59152d06d095beef
tree6a0b9c01c5bfaeaeaa230ea184baf9561f3ed03c
parent821907317eb77a96dc53adf20ac705b4501e2ab8

allow empty function and return with no expression


4 files changed, 107 insertions(+), 23 deletions(-)

README.md+4-2
...@@ -31,7 +31,9 @@ readable, safe, optimal, and concise code to solve any computing problem....@@ -31,7 +31,9 @@ readable, safe, optimal, and concise code to solve any computing problem.
3131
32## Roadmap32## Roadmap
3333
34 * empty function and return with no expression34 * pub/private/export functions
35 * make sure that release mode optimizes out empty private functions
36 * test framework to test for compile errors
35 * Simple .so library37 * Simple .so library
36 * Multiple files38 * Multiple files
37 * figure out integers39 * figure out integers
...@@ -87,7 +89,7 @@ Statement : ExpressionStatement | ReturnStatement...@@ -87,7 +89,7 @@ Statement : ExpressionStatement | ReturnStatement
8789
88ExpressionStatement : Expression token(Semicolon)90ExpressionStatement : Expression token(Semicolon)
8991
90ReturnStatement : token(Return) Expression token(Semicolon)92ReturnStatement : token(Return) option(Expression) token(Semicolon)
9193
92Expression : token(Number) | token(String) | token(Unreachable) | FnCall94Expression : token(Number) | token(String) | token(Unreachable) | FnCall
9395
src/codegen.cpp+81-6
...@@ -80,9 +80,14 @@ struct TypeNode {...@@ -80,9 +80,14 @@ struct TypeNode {
80 TypeTableEntry *entry;80 TypeTableEntry *entry;
81};81};
8282
83struct FnDefNode {
84 bool add_implicit_return;
85};
86
83struct CodeGenNode {87struct CodeGenNode {
84 union {88 union {
85 TypeNode type_node; // for NodeTypeType89 TypeNode type_node; // for NodeTypeType
90 FnDefNode fn_def_node; // for NodeTypeFnDef
86 } data;91 } data;
87};92};
8893
...@@ -275,6 +280,60 @@ static void find_declarations(CodeGen *g, AstNode *node) {...@@ -275,6 +280,60 @@ static void find_declarations(CodeGen *g, AstNode *node) {
275 }280 }
276}281}
277282
283static void check_fn_def_control_flow(CodeGen *g, AstNode *node) {
284 // Follow the execution flow and make sure the code returns appropriately.
285 // * A `return` statement in an unreachable type function should be an error.
286 // * Control flow should not be able to reach the end of an unreachable type function.
287 // * Functions that have a type other than void should not return without a value.
288 // * void functions without explicit return statements at the end need the
289 // add_implicit_return flag set on the codegen node.
290 assert(node->type == NodeTypeFnDef);
291 AstNode *proto_node = node->data.fn_def.fn_proto;
292 assert(proto_node->type == NodeTypeFnProto);
293 AstNode *return_type_node = proto_node->data.fn_proto.return_type;
294 assert(return_type_node->type == NodeTypeType);
295
296 node->codegen_node = allocate<CodeGenNode>(1);
297 FnDefNode *codegen_fn_def = &node->codegen_node->data.fn_def_node;
298
299 assert(return_type_node->codegen_node);
300 TypeTableEntry *type_entry = return_type_node->codegen_node->data.type_node.entry;
301 assert(type_entry);
302 TypeId type_id = type_entry->id;
303
304 AstNode *body_node = node->data.fn_def.body;
305 assert(body_node->type == NodeTypeBlock);
306
307 // TODO once we understand types, do this pass after type checking, and
308 // if an expression has an unreachable value then stop looking at statements after
309 // it. then we can remove the check to `unreachable` in the end of this function.
310 bool prev_statement_return = false;
311 for (int i = 0; i < body_node->data.block.statements.length; i += 1) {
312 AstNode *statement_node = body_node->data.block.statements.at(i);
313 if (statement_node->type == NodeTypeStatementReturn) {
314 if (type_id == TypeIdUnreachable) {
315 add_node_error(g, statement_node,
316 buf_sprintf("return statement in function with unreachable return type"));
317 return;
318 } else {
319 prev_statement_return = true;
320 }
321 } else if (prev_statement_return) {
322 add_node_error(g, statement_node,
323 buf_sprintf("unreachable code"));
324 }
325 }
326
327 if (!prev_statement_return) {
328 if (type_id == TypeIdVoid) {
329 codegen_fn_def->add_implicit_return = true;
330 } else if (type_id != TypeIdUnreachable) {
331 add_node_error(g, node,
332 buf_sprintf("control reaches end of non-void function"));
333 }
334 }
335}
336
278static void analyze_node(CodeGen *g, AstNode *node) {337static void analyze_node(CodeGen *g, AstNode *node) {
279 switch (node->type) {338 switch (node->type) {
280 case NodeTypeRoot:339 case NodeTypeRoot:
...@@ -299,6 +358,8 @@ static void analyze_node(CodeGen *g, AstNode *node) {...@@ -299,6 +358,8 @@ static void analyze_node(CodeGen *g, AstNode *node) {
299 AstNode *proto_node = node->data.fn_def.fn_proto;358 AstNode *proto_node = node->data.fn_def.fn_proto;
300 assert(proto_node->type == NodeTypeFnProto);359 assert(proto_node->type == NodeTypeFnProto);
301 analyze_node(g, proto_node);360 analyze_node(g, proto_node);
361
362 check_fn_def_control_flow(g, node);
302 break;363 break;
303 }364 }
304 case NodeTypeFnDecl:365 case NodeTypeFnDecl:
...@@ -331,7 +392,9 @@ static void analyze_node(CodeGen *g, AstNode *node) {...@@ -331,7 +392,9 @@ static void analyze_node(CodeGen *g, AstNode *node) {
331 }392 }
332 break;393 break;
333 case NodeTypeStatementReturn:394 case NodeTypeStatementReturn:
334 analyze_node(g, node->data.statement_return.expression);395 if (node->data.statement_return.expression) {
396 analyze_node(g, node->data.statement_return.expression);
397 }
335 break;398 break;
336 case NodeTypeExpression:399 case NodeTypeExpression:
337 switch (node->data.expression.type) {400 switch (node->data.expression.type) {
...@@ -545,7 +608,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node) {...@@ -545,7 +608,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node) {
545 zig_unreachable();608 zig_unreachable();
546}609}
547610
548static void gen_block(CodeGen *g, AstNode *block_node) {611static void gen_block(CodeGen *g, AstNode *block_node, bool add_implicit_return) {
549 assert(block_node->type == NodeTypeBlock);612 assert(block_node->type == NodeTypeBlock);
550613
551 llvm::DILexicalBlock *di_block = g->dbuilder->createLexicalBlock(g->block_scopes.last(),614 llvm::DILexicalBlock *di_block = g->dbuilder->createLexicalBlock(g->block_scopes.last(),
...@@ -558,10 +621,15 @@ static void gen_block(CodeGen *g, AstNode *block_node) {...@@ -558,10 +621,15 @@ static void gen_block(CodeGen *g, AstNode *block_node) {
558 case NodeTypeStatementReturn:621 case NodeTypeStatementReturn:
559 {622 {
560 AstNode *expr_node = statement_node->data.statement_return.expression;623 AstNode *expr_node = statement_node->data.statement_return.expression;
561 LLVMValueRef value = gen_expr(g, expr_node);624 if (expr_node) {
625 LLVMValueRef value = gen_expr(g, expr_node);
562626
563 add_debug_source_node(g, statement_node);627 add_debug_source_node(g, statement_node);
564 LLVMBuildRet(g->builder, value);628 LLVMBuildRet(g->builder, value);
629 } else {
630 add_debug_source_node(g, statement_node);
631 LLVMBuildRetVoid(g->builder);
632 }
565 break;633 break;
566 }634 }
567 case NodeTypeExpression:635 case NodeTypeExpression:
...@@ -583,6 +651,10 @@ static void gen_block(CodeGen *g, AstNode *block_node) {...@@ -583,6 +651,10 @@ static void gen_block(CodeGen *g, AstNode *block_node) {
583 }651 }
584 }652 }
585653
654 if (add_implicit_return) {
655 LLVMBuildRetVoid(g->builder);
656 }
657
586 g->block_scopes.pop();658 g->block_scopes.pop();
587}659}
588660
...@@ -685,7 +757,10 @@ void code_gen(CodeGen *g) {...@@ -685,7 +757,10 @@ void code_gen(CodeGen *g) {
685 LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn, "entry");757 LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn, "entry");
686 LLVMPositionBuilderAtEnd(g->builder, entry_block);758 LLVMPositionBuilderAtEnd(g->builder, entry_block);
687759
688 gen_block(g, fn_def_node->data.fn_def.body);760 CodeGenNode *codegen_node = fn_def_node->codegen_node;
761 assert(codegen_node);
762 bool add_implicit_return = codegen_node->data.fn_def_node.add_implicit_return;
763 gen_block(g, fn_def_node->data.fn_def.body, add_implicit_return);
689764
690 g->block_scopes.pop();765 g->block_scopes.pop();
691 }766 }
src/parser.cpp+16-14
...@@ -128,7 +128,8 @@ void ast_print(AstNode *node, int indent) {...@@ -128,7 +128,8 @@ void ast_print(AstNode *node, int indent) {
128 break;128 break;
129 case NodeTypeStatementReturn:129 case NodeTypeStatementReturn:
130 fprintf(stderr, "ReturnStatement\n");130 fprintf(stderr, "ReturnStatement\n");
131 ast_print(node->data.statement_return.expression, indent + 2);131 if (node->data.statement_return.expression)
132 ast_print(node->data.statement_return.expression, indent + 2);
132 break;133 break;
133 case NodeTypeExternBlock:134 case NodeTypeExternBlock:
134 {135 {
...@@ -258,7 +259,7 @@ void ast_invalid_token_error(ParseContext *pc, Token *token) {...@@ -258,7 +259,7 @@ void ast_invalid_token_error(ParseContext *pc, Token *token) {
258 ast_error(token, "invalid token: '%s'", buf_ptr(&token_value));259 ast_error(token, "invalid token: '%s'", buf_ptr(&token_value));
259}260}
260261
261static AstNode *ast_parse_expression(ParseContext *pc, int token_index, int *new_token_index);262static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory);
262263
263264
264static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {265static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
...@@ -374,7 +375,7 @@ static void ast_parse_fn_call_param_list(ParseContext *pc, int token_index, int...@@ -374,7 +375,7 @@ static void ast_parse_fn_call_param_list(ParseContext *pc, int token_index, int
374 }375 }
375376
376 for (;;) {377 for (;;) {
377 AstNode *expr = ast_parse_expression(pc, token_index, &token_index);378 AstNode *expr = ast_parse_expression(pc, &token_index, true);
378 params->append(expr);379 params->append(expr);
379380
380 Token *token = &pc->tokens->at(token_index);381 Token *token = &pc->tokens->at(token_index);
...@@ -411,28 +412,29 @@ static AstNode *ast_parse_fn_call(ParseContext *pc, int token_index, int *new_to...@@ -411,28 +412,29 @@ static AstNode *ast_parse_fn_call(ParseContext *pc, int token_index, int *new_to
411/*412/*
412Expression : token(Number) | token(String) | token(Unreachable) | FnCall413Expression : token(Number) | token(String) | token(Unreachable) | FnCall
413*/414*/
414static AstNode *ast_parse_expression(ParseContext *pc, int token_index, int *new_token_index) {415static AstNode *ast_parse_expression(ParseContext *pc, int *token_index, bool mandatory) {
415 Token *token = &pc->tokens->at(token_index);416 Token *token = &pc->tokens->at(*token_index);
416 AstNode *node = ast_create_node(NodeTypeExpression, token);417 AstNode *node = ast_create_node(NodeTypeExpression, token);
417 if (token->id == TokenIdKeywordUnreachable) {418 if (token->id == TokenIdKeywordUnreachable) {
418 node->data.expression.type = AstNodeExpressionTypeUnreachable;419 node->data.expression.type = AstNodeExpressionTypeUnreachable;
419 token_index += 1;420 *token_index += 1;
420 } else if (token->id == TokenIdSymbol) {421 } else if (token->id == TokenIdSymbol) {
421 node->data.expression.type = AstNodeExpressionTypeFnCall;422 node->data.expression.type = AstNodeExpressionTypeFnCall;
422 node->data.expression.data.fn_call = ast_parse_fn_call(pc, token_index, &token_index);423 node->data.expression.data.fn_call = ast_parse_fn_call(pc, *token_index, token_index);
423 } else if (token->id == TokenIdNumberLiteral) {424 } else if (token->id == TokenIdNumberLiteral) {
424 node->data.expression.type = AstNodeExpressionTypeNumber;425 node->data.expression.type = AstNodeExpressionTypeNumber;
425 ast_buf_from_token(pc, token, &node->data.expression.data.number);426 ast_buf_from_token(pc, token, &node->data.expression.data.number);
426 token_index += 1;427 *token_index += 1;
427 } else if (token->id == TokenIdStringLiteral) {428 } else if (token->id == TokenIdStringLiteral) {
428 node->data.expression.type = AstNodeExpressionTypeString;429 node->data.expression.type = AstNodeExpressionTypeString;
429 parse_string_literal(pc, token, &node->data.expression.data.string);430 parse_string_literal(pc, token, &node->data.expression.data.string);
430 token_index += 1;431 *token_index += 1;
431 } else {432 } else if (mandatory) {
432 ast_invalid_token_error(pc, token);433 ast_invalid_token_error(pc, token);
434 } else {
435 return nullptr;
433 }436 }
434437
435 *new_token_index = token_index;
436 return node;438 return node;
437}439}
438440
...@@ -441,14 +443,14 @@ Statement : ExpressionStatement | ReturnStatement ;...@@ -441,14 +443,14 @@ Statement : ExpressionStatement | ReturnStatement ;
441443
442ExpressionStatement : Expression token(Semicolon) ;444ExpressionStatement : Expression token(Semicolon) ;
443445
444ReturnStatement : token(Return) Expression token(Semicolon) ;446ReturnStatement : token(Return) option(Expression) token(Semicolon) ;
445*/447*/
446static AstNode *ast_parse_statement(ParseContext *pc, int token_index, int *new_token_index) {448static AstNode *ast_parse_statement(ParseContext *pc, int token_index, int *new_token_index) {
447 Token *token = &pc->tokens->at(token_index);449 Token *token = &pc->tokens->at(token_index);
448 if (token->id == TokenIdKeywordReturn) {450 if (token->id == TokenIdKeywordReturn) {
449 AstNode *node = ast_create_node(NodeTypeStatementReturn, token);451 AstNode *node = ast_create_node(NodeTypeStatementReturn, token);
450 token_index += 1;452 token_index += 1;
451 node->data.statement_return.expression = ast_parse_expression(pc, token_index, &token_index);453 node->data.statement_return.expression = ast_parse_expression(pc, &token_index, false);
452454
453 Token *semicolon = &pc->tokens->at(token_index);455 Token *semicolon = &pc->tokens->at(token_index);
454 token_index += 1;456 token_index += 1;
...@@ -460,7 +462,7 @@ static AstNode *ast_parse_statement(ParseContext *pc, int token_index, int *new_...@@ -460,7 +462,7 @@ static AstNode *ast_parse_statement(ParseContext *pc, int token_index, int *new_
460 token->id == TokenIdKeywordUnreachable ||462 token->id == TokenIdKeywordUnreachable ||
461 token->id == TokenIdNumberLiteral)463 token->id == TokenIdNumberLiteral)
462 {464 {
463 AstNode *node = ast_parse_expression(pc, token_index, &token_index);465 AstNode *node = ast_parse_expression(pc, &token_index, true);
464466
465 Token *semicolon = &pc->tokens->at(token_index);467 Token *semicolon = &pc->tokens->at(token_index);
466 token_index += 1;468 token_index += 1;
test/standalone.cpp+6-1
...@@ -66,7 +66,12 @@ static void add_all_test_cases(void) {...@@ -66,7 +66,12 @@ static void add_all_test_cases(void) {
66 fn exit(code: i32) -> unreachable;66 fn exit(code: i32) -> unreachable;
67 }67 }
6868
69 fn empty_function_1() {}
70 fn empty_function_2() { return; }
71
69 fn _start() -> unreachable {72 fn _start() -> unreachable {
73 empty_function_1();
74 empty_function_2();
70 this_is_a_function();75 this_is_a_function();
71 }76 }
7277
...@@ -86,7 +91,7 @@ static void add_all_test_cases(void) {...@@ -86,7 +91,7 @@ static void add_all_test_cases(void) {
86 /**91 /**
87 * multi line doc comment92 * multi line doc comment
88 */93 */
89 fn another_function() -> i32 { return 0; }94 fn another_function() {}
9095
91 /// this is a documentation comment96 /// this is a documentation comment
92 /// doc comment line 297 /// doc comment line 2