| author | |
| committer | |
| log | 1ed926c3216016d73e83377a295869d8579e2fde |
| tree | dfc72c0a73844cf440a83e8b67d6c16683c63c2c |
| parent | c6a9ab107bb383759f3627ed828ea2a779844f2b |
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) |
| 104 | 104 | ||
| 105 | PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type | 105 | PointerType : token(Star) token(Const) Type | token(Star) token(Mut) Type |
| 106 | 106 | ||
| 107 | Block : token(LBrace) many(Statement) token(RBrace) | 107 | Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace) |
| 108 | |||
| 109 | Statement : ExpressionStatement | ||
| 110 | |||
| 111 | ExpressionStatement : Expression token(Semicolon) | ||
| 112 | 108 | ||
| 113 | Expression : BoolOrExpression | ReturnExpression | 109 | Expression : BoolOrExpression | ReturnExpression |
| 114 | 110 |
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; |
| 417 | 422 | ||
| 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 table | 428 | // 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 | } |
| 441 | 450 | ||
| 442 | static 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 | |||
| 495 | static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *node) { | 451 | static 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 parameters | 468 | // TODO: define local variables for parameters |
| 513 | } | 469 | } |
| 514 | 470 | ||
| 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; |
| 525 | 482 | ||
| ... | @@ -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 | } |
| 443 | 445 | ||
| 444 | static void gen_block(CodeGen *g, ImportTableEntry *import, AstNode *block_node, bool add_implicit_return) { | 446 | static 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); |
| 446 | 448 | ||
| 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, |
| 450 | 452 | ||
| 451 | add_debug_source_node(g, block_node); | 453 | add_debug_source_node(g, block_node); |
| 452 | 454 | ||
| 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 | } |
| 457 | 460 | ||
| 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 | } |
| 461 | 466 | ||
| 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); |
| 554 | 559 | ||
| 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); |
| 557 | 562 | ||
| 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 | } |
| 238 | 243 | ||
| ... | @@ -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 | } |
| 1025 | 1037 | ||
| 1026 | /* | 1038 | /* |
| 1027 | ExpressionStatement : Expression token(Semicolon) | 1039 | Block : token(LBrace) list(option(Expression), token(Semicolon)) token(RBrace) |
| 1028 | */ | ||
| 1029 | static 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 | /* | ||
| 1040 | Statement : ExpressionStatement | ||
| 1041 | */ | ||
| 1042 | static AstNode *ast_parse_statement(ParseContext *pc, int *token_index) { | ||
| 1043 | return ast_parse_expression_statement(pc, token_index); | ||
| 1044 | } | ||
| 1045 | |||
| 1046 | /* | ||
| 1047 | Block : token(LBrace) many(Statement) token(RBrace); | ||
| 1048 | */ | 1040 | */ |
| 1049 | static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandatory) { | 1041 | static 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); |
| 1051 | 1043 | ||
| 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; |
| 1060 | 1052 | ||
| 1061 | AstNode *node = ast_create_node(pc, NodeTypeBlock, l_brace); | 1053 | AstNode *node = ast_create_node(pc, NodeTypeBlock, last_token); |
| 1062 | 1054 | ||
| 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 | }; |
| 42 | 43 | ||
| 43 | struct AstNodeRoot { | 44 | struct AstNodeRoot { |
src/semantic_info.hpp+1-1| ... | @@ -106,7 +106,7 @@ struct TypeNode { | ... | @@ -106,7 +106,7 @@ struct TypeNode { |
| 106 | }; | 106 | }; |
| 107 | 107 | ||
| 108 | struct FnDefNode { | 108 | struct 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 | } |
| 185 | 187 | ||
| 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() {} |
| 209 | 209 | ||
| 210 | add_compile_fail_case("unreachable with return", R"SOURCE( | 210 | add_compile_fail_case("unreachable with return", R"SOURCE( |
| 211 | fn a() -> unreachable {return;} | 211 | fn 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"); |
| 213 | 213 | ||
| 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( |
| 215 | fn a() -> i32 {} | 215 | fn 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"); |
| 217 | 217 | ||
| 218 | add_compile_fail_case("undefined function call", R"SOURCE( | 218 | add_compile_fail_case("undefined function call", R"SOURCE( |
| 219 | fn a() { | 219 | fn a() { |