| author | |
| committer | |
| log | a5c2de5fee67e35c8173b7051675d49648086cbb |
| tree | e6ee63d6daca6d6456e58f866d47e2c426cd9315 |
| parent | 2bb2e61ee288a02e184e5b8422859a4afcbb4813 |
closes #145 files changed, 170 insertions(+), 70 deletions(-)
doc/langref.md+1-1| ... | ... | @@ -141,7 +141,7 @@ StructLiteralField = "." "Symbol" "=" Expression |
| 141 | 141 | |
| 142 | 142 | PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const")) | "?" | "%" | "%%" |
| 143 | 143 | |
| 144 | PrimaryExpression = "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." "Symbol") | |
| 144 | PrimaryExpression = "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." "Symbol") | |
| 145 | 145 | |
| 146 | 146 | ArrayType = "[" option(Expression) "]" option("const") PrefixOpExpression |
| 147 | 147 |
src/all_types.hpp+2| ... | ... | @@ -189,6 +189,7 @@ struct AstNodeFnProto { |
| 189 | 189 | FnTableEntry *fn_table_entry; |
| 190 | 190 | bool skip; |
| 191 | 191 | TopLevelDecl top_level_decl; |
| 192 | Expr resolved_expr; | |
| 192 | 193 | }; |
| 193 | 194 | |
| 194 | 195 | struct AstNodeFnDef { |
| ... | ... | @@ -828,6 +829,7 @@ struct TypeTableEntryFn { |
| 828 | 829 | bool is_var_args; |
| 829 | 830 | int gen_param_count; |
| 830 | 831 | LLVMCallConv calling_convention; |
| 832 | bool is_extern; | |
| 831 | 833 | bool is_naked; |
| 832 | 834 | }; |
| 833 | 835 |
src/analyze.cpp+114-50| ... | ... | @@ -451,44 +451,20 @@ static TypeTableEntry *analyze_type_expr(CodeGen *g, ImportTableEntry *import, B |
| 451 | 451 | return resolve_type(g, *node_ptr); |
| 452 | 452 | } |
| 453 | 453 | |
| 454 | static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry, | |
| 455 | ImportTableEntry *import) | |
| 454 | static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 455 | TypeTableEntry *expected_type, AstNode *node, bool is_naked) | |
| 456 | 456 | { |
| 457 | 457 | assert(node->type == NodeTypeFnProto); |
| 458 | 458 | AstNodeFnProto *fn_proto = &node->data.fn_proto; |
| 459 | 459 | |
| 460 | 460 | if (fn_proto->skip) { |
| 461 | return; | |
| 461 | return g->builtin_types.entry_invalid; | |
| 462 | 462 | } |
| 463 | 463 | |
| 464 | 464 | TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn); |
| 465 | fn_table_entry->type_entry = fn_type; | |
| 466 | fn_type->data.fn.calling_convention = fn_table_entry->internal_linkage ? LLVMFastCallConv : LLVMCCallConv; | |
| 467 | ||
| 468 | for (int i = 0; i < fn_proto->directives->length; i += 1) { | |
| 469 | AstNode *directive_node = fn_proto->directives->at(i); | |
| 470 | Buf *name = &directive_node->data.directive.name; | |
| 471 | ||
| 472 | if (buf_eql_str(name, "attribute")) { | |
| 473 | Buf *attr_name = &directive_node->data.directive.param; | |
| 474 | if (fn_table_entry->fn_def_node) { | |
| 475 | if (buf_eql_str(attr_name, "naked")) { | |
| 476 | fn_type->data.fn.is_naked = true; | |
| 477 | } else if (buf_eql_str(attr_name, "inline")) { | |
| 478 | fn_table_entry->is_inline = true; | |
| 479 | } else { | |
| 480 | add_node_error(g, directive_node, | |
| 481 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | |
| 482 | } | |
| 483 | } else { | |
| 484 | add_node_error(g, directive_node, | |
| 485 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | |
| 486 | } | |
| 487 | } else { | |
| 488 | add_node_error(g, directive_node, | |
| 489 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 490 | } | |
| 491 | } | |
| 465 | fn_type->data.fn.is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport); | |
| 466 | fn_type->data.fn.is_naked = is_naked; | |
| 467 | fn_type->data.fn.calling_convention = fn_proto->is_extern ? LLVMCCallConv : LLVMFastCallConv; | |
| 492 | 468 | |
| 493 | 469 | int src_param_count = node->data.fn_proto.params.length; |
| 494 | 470 | fn_type->size_in_bits = g->pointer_size_bytes * 8; |
| ... | ... | @@ -499,10 +475,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 499 | 475 | // first, analyze the parameters and return type in order they appear in |
| 500 | 476 | // source code in order for error messages to be in the best order. |
| 501 | 477 | buf_resize(&fn_type->name, 0); |
| 502 | const char *export_str = fn_table_entry->internal_linkage ? "" : "export "; | |
| 503 | const char *inline_str = fn_table_entry->is_inline ? "inline " : ""; | |
| 478 | const char *extern_str = fn_type->data.fn.is_extern ? "extern " : ""; | |
| 504 | 479 | const char *naked_str = fn_type->data.fn.is_naked ? "naked " : ""; |
| 505 | buf_appendf(&fn_type->name, "%s%s%sfn(", export_str, inline_str, naked_str); | |
| 480 | buf_appendf(&fn_type->name, "%s%sfn(", extern_str, naked_str); | |
| 506 | 481 | for (int i = 0; i < src_param_count; i += 1) { |
| 507 | 482 | AstNode *child = node->data.fn_proto.params.at(i); |
| 508 | 483 | assert(child->type == NodeTypeParamDecl); |
| ... | ... | @@ -525,10 +500,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 525 | 500 | const char *comma = (src_param_count == 0) ? "" : ", "; |
| 526 | 501 | buf_appendf(&fn_type->name, "%s...", comma); |
| 527 | 502 | } |
| 528 | ||
| 529 | 503 | buf_appendf(&fn_type->name, ")"); |
| 530 | 504 | if (return_type->id != TypeTableEntryIdVoid) { |
| 531 | buf_appendf(&fn_type->name, " %s", buf_ptr(&return_type->name)); | |
| 505 | buf_appendf(&fn_type->name, " -> %s", buf_ptr(&return_type->name)); | |
| 532 | 506 | } |
| 533 | 507 | |
| 534 | 508 | |
| ... | ... | @@ -593,13 +567,12 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 593 | 567 | fn_type->data.fn.gen_param_count = gen_param_index; |
| 594 | 568 | |
| 595 | 569 | if (fn_proto->skip) { |
| 596 | return; | |
| 570 | return g->builtin_types.entry_invalid; | |
| 597 | 571 | } |
| 598 | 572 | |
| 599 | 573 | auto table_entry = import->fn_type_table.maybe_get(&fn_type->name); |
| 600 | 574 | if (table_entry) { |
| 601 | fn_type = table_entry->value; | |
| 602 | fn_table_entry->type_entry = fn_type; | |
| 575 | return table_entry->value; | |
| 603 | 576 | } else { |
| 604 | 577 | fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, |
| 605 | 578 | gen_param_types, gen_param_index, fn_type->data.fn.is_var_args); |
| ... | ... | @@ -608,8 +581,56 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 608 | 581 | param_di_types, gen_param_index + 1, 0); |
| 609 | 582 | |
| 610 | 583 | import->fn_type_table.put(&fn_type->name, fn_type); |
| 584 | ||
| 585 | return fn_type; | |
| 586 | } | |
| 587 | } | |
| 588 | ||
| 589 | ||
| 590 | static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry, | |
| 591 | ImportTableEntry *import) | |
| 592 | { | |
| 593 | assert(node->type == NodeTypeFnProto); | |
| 594 | AstNodeFnProto *fn_proto = &node->data.fn_proto; | |
| 595 | ||
| 596 | if (fn_proto->skip) { | |
| 597 | return; | |
| 611 | 598 | } |
| 612 | 599 | |
| 600 | bool is_naked = false; | |
| 601 | for (int i = 0; i < fn_proto->directives->length; i += 1) { | |
| 602 | AstNode *directive_node = fn_proto->directives->at(i); | |
| 603 | Buf *name = &directive_node->data.directive.name; | |
| 604 | ||
| 605 | if (buf_eql_str(name, "attribute")) { | |
| 606 | Buf *attr_name = &directive_node->data.directive.param; | |
| 607 | if (fn_table_entry->fn_def_node) { | |
| 608 | if (buf_eql_str(attr_name, "naked")) { | |
| 609 | is_naked = true; | |
| 610 | } else if (buf_eql_str(attr_name, "inline")) { | |
| 611 | fn_table_entry->is_inline = true; | |
| 612 | } else { | |
| 613 | add_node_error(g, directive_node, | |
| 614 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | |
| 615 | } | |
| 616 | } else { | |
| 617 | add_node_error(g, directive_node, | |
| 618 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | |
| 619 | } | |
| 620 | } else { | |
| 621 | add_node_error(g, directive_node, | |
| 622 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 623 | } | |
| 624 | } | |
| 625 | ||
| 626 | TypeTableEntry *fn_type = analyze_fn_proto_type(g, import, import->block_context, nullptr, node, is_naked); | |
| 627 | ||
| 628 | if (fn_type->id == TypeTableEntryIdInvalid) { | |
| 629 | fn_proto->skip = true; | |
| 630 | return; | |
| 631 | } | |
| 632 | ||
| 633 | fn_table_entry->type_entry = fn_type; | |
| 613 | 634 | |
| 614 | 635 | fn_table_entry->fn_value = LLVMAddFunction(g->module, buf_ptr(&fn_table_entry->symbol_name), |
| 615 | 636 | fn_type->data.fn.raw_type_ref); |
| ... | ... | @@ -624,7 +645,7 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 624 | 645 | LLVMSetLinkage(fn_table_entry->fn_value, fn_table_entry->internal_linkage ? |
| 625 | 646 | LLVMInternalLinkage : LLVMExternalLinkage); |
| 626 | 647 | |
| 627 | if (return_type->id == TypeTableEntryIdUnreachable) { | |
| 648 | if (fn_type->data.fn.src_return_type->id == TypeTableEntryIdUnreachable) { | |
| 628 | 649 | LLVMAddFunctionAttr(fn_table_entry->fn_value, LLVMNoReturnAttribute); |
| 629 | 650 | } |
| 630 | 651 | LLVMSetFunctionCallConv(fn_table_entry->fn_value, fn_type->data.fn.calling_convention); |
| ... | ... | @@ -1353,7 +1374,29 @@ static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTable |
| 1353 | 1374 | if (expected_type->id == TypeTableEntryIdFn && |
| 1354 | 1375 | actual_type->id == TypeTableEntryIdFn) |
| 1355 | 1376 | { |
| 1356 | zig_panic("TODO types_match_const_cast_only for fns"); | |
| 1377 | if (expected_type->data.fn.is_extern != actual_type->data.fn.is_extern) { | |
| 1378 | return false; | |
| 1379 | } | |
| 1380 | if (expected_type->data.fn.is_naked != actual_type->data.fn.is_naked) { | |
| 1381 | return false; | |
| 1382 | } | |
| 1383 | if (!types_match_const_cast_only(expected_type->data.fn.src_return_type, | |
| 1384 | actual_type->data.fn.src_return_type)) | |
| 1385 | { | |
| 1386 | return false; | |
| 1387 | } | |
| 1388 | if (expected_type->data.fn.src_param_count != actual_type->data.fn.src_param_count) { | |
| 1389 | return false; | |
| 1390 | } | |
| 1391 | for (int i = 0; i < expected_type->data.fn.src_param_count; i += 1) { | |
| 1392 | // note it's reversed for parameters | |
| 1393 | if (types_match_const_cast_only(actual_type->data.fn.param_types[i], | |
| 1394 | expected_type->data.fn.param_types[i])) | |
| 1395 | { | |
| 1396 | return false; | |
| 1397 | } | |
| 1398 | } | |
| 1399 | return true; | |
| 1357 | 1400 | } |
| 1358 | 1401 | |
| 1359 | 1402 | |
| ... | ... | @@ -2902,6 +2945,18 @@ static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, |
| 2902 | 2945 | } |
| 2903 | 2946 | } |
| 2904 | 2947 | |
| 2948 | static TypeTableEntry *analyze_fn_proto_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 2949 | TypeTableEntry *expected_type, AstNode *node) | |
| 2950 | { | |
| 2951 | TypeTableEntry *type_entry = analyze_fn_proto_type(g, import, context, expected_type, node, false); | |
| 2952 | ||
| 2953 | if (type_entry->id == TypeTableEntryIdInvalid) { | |
| 2954 | return type_entry; | |
| 2955 | } | |
| 2956 | ||
| 2957 | return resolve_expr_const_val_as_type(g, node, type_entry); | |
| 2958 | } | |
| 2959 | ||
| 2905 | 2960 | static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 2906 | 2961 | TypeTableEntry *expected_type, AstNode *node) |
| 2907 | 2962 | { |
| ... | ... | @@ -4240,6 +4295,9 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 4240 | 4295 | case NodeTypeArrayType: |
| 4241 | 4296 | return_type = analyze_array_type(g, import, context, expected_type, node); |
| 4242 | 4297 | break; |
| 4298 | case NodeTypeFnProto: | |
| 4299 | return_type = analyze_fn_proto_expr(g, import, context, expected_type, node); | |
| 4300 | break; | |
| 4243 | 4301 | case NodeTypeErrorType: |
| 4244 | 4302 | return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_pure_error); |
| 4245 | 4303 | break; |
| ... | ... | @@ -4250,7 +4308,6 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 4250 | 4308 | case NodeTypeSwitchRange: |
| 4251 | 4309 | case NodeTypeDirective: |
| 4252 | 4310 | case NodeTypeFnDecl: |
| 4253 | case NodeTypeFnProto: | |
| 4254 | 4311 | case NodeTypeParamDecl: |
| 4255 | 4312 | case NodeTypeRoot: |
| 4256 | 4313 | case NodeTypeRootExportDecl: |
| ... | ... | @@ -4555,13 +4612,23 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode |
| 4555 | 4612 | collect_expr_decl_deps(g, import, node->data.switch_range.start, decl_node); |
| 4556 | 4613 | collect_expr_decl_deps(g, import, node->data.switch_range.end, decl_node); |
| 4557 | 4614 | break; |
| 4558 | case NodeTypeVariableDeclaration: | |
| 4559 | 4615 | case NodeTypeFnProto: |
| 4616 | // remember that fn proto node is used for function definitions as well | |
| 4617 | // as types | |
| 4618 | for (int i = 0; i < node->data.fn_proto.params.length; i += 1) { | |
| 4619 | AstNode *param = node->data.fn_proto.params.at(i); | |
| 4620 | collect_expr_decl_deps(g, import, param, decl_node); | |
| 4621 | } | |
| 4622 | collect_expr_decl_deps(g, import, node->data.fn_proto.return_type, decl_node); | |
| 4623 | break; | |
| 4624 | case NodeTypeParamDecl: | |
| 4625 | collect_expr_decl_deps(g, import, node->data.param_decl.type, decl_node); | |
| 4626 | break; | |
| 4627 | case NodeTypeVariableDeclaration: | |
| 4560 | 4628 | case NodeTypeRootExportDecl: |
| 4561 | 4629 | case NodeTypeFnDef: |
| 4562 | 4630 | case NodeTypeRoot: |
| 4563 | 4631 | case NodeTypeFnDecl: |
| 4564 | case NodeTypeParamDecl: | |
| 4565 | 4632 | case NodeTypeDirective: |
| 4566 | 4633 | case NodeTypeImport: |
| 4567 | 4634 | case NodeTypeCImport: |
| ... | ... | @@ -4705,12 +4772,8 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast |
| 4705 | 4772 | // determine which other top level declarations this function prototype depends on. |
| 4706 | 4773 | TopLevelDecl *decl_node = &node->data.fn_proto.top_level_decl; |
| 4707 | 4774 | decl_node->deps.init(1); |
| 4708 | for (int i = 0; i < node->data.fn_proto.params.length; i += 1) { | |
| 4709 | AstNode *param_node = node->data.fn_proto.params.at(i); | |
| 4710 | assert(param_node->type == NodeTypeParamDecl); | |
| 4711 | collect_expr_decl_deps(g, import, param_node->data.param_decl.type, decl_node); | |
| 4712 | } | |
| 4713 | collect_expr_decl_deps(g, import, node->data.fn_proto.return_type, decl_node); | |
| 4775 | ||
| 4776 | collect_expr_decl_deps(g, import, node, decl_node); | |
| 4714 | 4777 | |
| 4715 | 4778 | decl_node->name = name; |
| 4716 | 4779 | decl_node->import = import; |
| ... | ... | @@ -4999,11 +5062,12 @@ Expr *get_resolved_expr(AstNode *node) { |
| 4999 | 5062 | return &node->data.error_type.resolved_expr; |
| 5000 | 5063 | case NodeTypeSwitchExpr: |
| 5001 | 5064 | return &node->data.switch_expr.resolved_expr; |
| 5065 | case NodeTypeFnProto: | |
| 5066 | return &node->data.fn_proto.resolved_expr; | |
| 5002 | 5067 | case NodeTypeSwitchProng: |
| 5003 | 5068 | case NodeTypeSwitchRange: |
| 5004 | 5069 | case NodeTypeRoot: |
| 5005 | 5070 | case NodeTypeRootExportDecl: |
| 5006 | case NodeTypeFnProto: | |
| 5007 | 5071 | case NodeTypeFnDef: |
| 5008 | 5072 | case NodeTypeFnDecl: |
| 5009 | 5073 | case NodeTypeParamDecl: |
src/parser.cpp+36-19| ... | ... | @@ -503,6 +503,8 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda |
| 503 | 503 | static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory); |
| 504 | 504 | static AstNode *ast_parse_unwrap_expr(ParseContext *pc, int *token_index, bool mandatory); |
| 505 | 505 | static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory); |
| 506 | static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory, | |
| 507 | ZigList<AstNode*> *directives, VisibMod visib_mod); | |
| 506 | 508 | |
| 507 | 509 | static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) { |
| 508 | 510 | if (token->id == token_id) { |
| ... | ... | @@ -671,7 +673,7 @@ static AstNode *ast_parse_grouped_expr(ParseContext *pc, int *token_index, bool |
| 671 | 673 | Token *l_paren = &pc->tokens->at(*token_index); |
| 672 | 674 | if (l_paren->id != TokenIdLParen) { |
| 673 | 675 | if (mandatory) { |
| 674 | ast_invalid_token_error(pc, l_paren); | |
| 676 | ast_expect_token(pc, l_paren, TokenIdLParen); | |
| 675 | 677 | } else { |
| 676 | 678 | return nullptr; |
| 677 | 679 | } |
| ... | ... | @@ -695,7 +697,7 @@ static AstNode *ast_parse_array_type_expr(ParseContext *pc, int *token_index, bo |
| 695 | 697 | Token *l_bracket = &pc->tokens->at(*token_index); |
| 696 | 698 | if (l_bracket->id != TokenIdLBracket) { |
| 697 | 699 | if (mandatory) { |
| 698 | ast_invalid_token_error(pc, l_bracket); | |
| 700 | ast_expect_token(pc, l_bracket, TokenIdLBracket); | |
| 699 | 701 | } else { |
| 700 | 702 | return nullptr; |
| 701 | 703 | } |
| ... | ... | @@ -865,7 +867,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand |
| 865 | 867 | |
| 866 | 868 | if (asm_token->id != TokenIdKeywordAsm) { |
| 867 | 869 | if (mandatory) { |
| 868 | ast_invalid_token_error(pc, asm_token); | |
| 870 | ast_expect_token(pc, asm_token, TokenIdKeywordAsm); | |
| 869 | 871 | } else { |
| 870 | 872 | return nullptr; |
| 871 | 873 | } |
| ... | ... | @@ -905,7 +907,7 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand |
| 905 | 907 | } |
| 906 | 908 | |
| 907 | 909 | /* |
| 908 | PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("error" "." "Symbol") | |
| 910 | PrimaryExpression = "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." "Symbol") | |
| 909 | 911 | KeywordLiteral : "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" |
| 910 | 912 | */ |
| 911 | 913 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| ... | ... | @@ -956,6 +958,11 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 956 | 958 | AstNode *node = ast_create_node(pc, NodeTypeErrorType, token); |
| 957 | 959 | *token_index += 1; |
| 958 | 960 | return node; |
| 961 | } else if (token->id == TokenIdKeywordExtern) { | |
| 962 | *token_index += 1; | |
| 963 | AstNode *node = ast_parse_fn_proto(pc, token_index, true, nullptr, VisibModPrivate); | |
| 964 | node->data.fn_proto.is_extern = true; | |
| 965 | return node; | |
| 959 | 966 | } else if (token->id == TokenIdAtSign) { |
| 960 | 967 | *token_index += 1; |
| 961 | 968 | Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); |
| ... | ... | @@ -1002,6 +1009,11 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 1002 | 1009 | return array_type_node; |
| 1003 | 1010 | } |
| 1004 | 1011 | |
| 1012 | AstNode *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, nullptr, VisibModPrivate); | |
| 1013 | if (fn_proto_node) { | |
| 1014 | return fn_proto_node; | |
| 1015 | } | |
| 1016 | ||
| 1005 | 1017 | AstNode *asm_expr = ast_parse_asm_expr(pc, token_index, false); |
| 1006 | 1018 | if (asm_expr) { |
| 1007 | 1019 | return asm_expr; |
| ... | ... | @@ -1055,7 +1067,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, int *token_index, |
| 1055 | 1067 | token = &pc->tokens->at(*token_index); |
| 1056 | 1068 | continue; |
| 1057 | 1069 | } else if (comma_tok->id != TokenIdRBrace) { |
| 1058 | ast_invalid_token_error(pc, comma_tok); | |
| 1070 | ast_expect_token(pc, comma_tok, TokenIdRBrace); | |
| 1059 | 1071 | } else { |
| 1060 | 1072 | *token_index += 1; |
| 1061 | 1073 | break; |
| ... | ... | @@ -1084,7 +1096,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, int *token_index, |
| 1084 | 1096 | token = &pc->tokens->at(*token_index); |
| 1085 | 1097 | continue; |
| 1086 | 1098 | } else if (comma_tok->id != TokenIdRBrace) { |
| 1087 | ast_invalid_token_error(pc, comma_tok); | |
| 1099 | ast_expect_token(pc, comma_tok, TokenIdRBrace); | |
| 1088 | 1100 | } else { |
| 1089 | 1101 | *token_index += 1; |
| 1090 | 1102 | break; |
| ... | ... | @@ -1555,7 +1567,7 @@ static AstNode *ast_parse_else(ParseContext *pc, int *token_index, bool mandator |
| 1555 | 1567 | |
| 1556 | 1568 | if (else_token->id != TokenIdKeywordElse) { |
| 1557 | 1569 | if (mandatory) { |
| 1558 | ast_invalid_token_error(pc, else_token); | |
| 1570 | ast_expect_token(pc, else_token, TokenIdKeywordElse); | |
| 1559 | 1571 | } else { |
| 1560 | 1572 | return nullptr; |
| 1561 | 1573 | } |
| ... | ... | @@ -1574,7 +1586,7 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda |
| 1574 | 1586 | Token *if_tok = &pc->tokens->at(*token_index); |
| 1575 | 1587 | if (if_tok->id != TokenIdKeywordIf) { |
| 1576 | 1588 | if (mandatory) { |
| 1577 | ast_invalid_token_error(pc, if_tok); | |
| 1589 | ast_expect_token(pc, if_tok, TokenIdKeywordIf); | |
| 1578 | 1590 | } else { |
| 1579 | 1591 | return nullptr; |
| 1580 | 1592 | } |
| ... | ... | @@ -1637,7 +1649,8 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m |
| 1637 | 1649 | kind = ReturnKindError; |
| 1638 | 1650 | *token_index += 2; |
| 1639 | 1651 | } else if (mandatory) { |
| 1640 | ast_invalid_token_error(pc, token); | |
| 1652 | ast_expect_token(pc, next_token, TokenIdKeywordReturn); | |
| 1653 | zig_unreachable(); | |
| 1641 | 1654 | } else { |
| 1642 | 1655 | return nullptr; |
| 1643 | 1656 | } |
| ... | ... | @@ -1647,7 +1660,8 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m |
| 1647 | 1660 | kind = ReturnKindMaybe; |
| 1648 | 1661 | *token_index += 2; |
| 1649 | 1662 | } else if (mandatory) { |
| 1650 | ast_invalid_token_error(pc, token); | |
| 1663 | ast_expect_token(pc, next_token, TokenIdKeywordReturn); | |
| 1664 | zig_unreachable(); | |
| 1651 | 1665 | } else { |
| 1652 | 1666 | return nullptr; |
| 1653 | 1667 | } |
| ... | ... | @@ -1655,7 +1669,8 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m |
| 1655 | 1669 | kind = ReturnKindUnconditional; |
| 1656 | 1670 | *token_index += 1; |
| 1657 | 1671 | } else if (mandatory) { |
| 1658 | ast_invalid_token_error(pc, token); | |
| 1672 | ast_expect_token(pc, token, TokenIdKeywordReturn); | |
| 1673 | zig_unreachable(); | |
| 1659 | 1674 | } else { |
| 1660 | 1675 | return nullptr; |
| 1661 | 1676 | } |
| ... | ... | @@ -1756,7 +1771,7 @@ static AstNode *ast_parse_while_expr(ParseContext *pc, int *token_index, bool ma |
| 1756 | 1771 | |
| 1757 | 1772 | if (token->id != TokenIdKeywordWhile) { |
| 1758 | 1773 | if (mandatory) { |
| 1759 | ast_invalid_token_error(pc, token); | |
| 1774 | ast_expect_token(pc, token, TokenIdKeywordWhile); | |
| 1760 | 1775 | } else { |
| 1761 | 1776 | return nullptr; |
| 1762 | 1777 | } |
| ... | ... | @@ -1791,7 +1806,7 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, int *token_index, bool mand |
| 1791 | 1806 | |
| 1792 | 1807 | if (token->id != TokenIdKeywordFor) { |
| 1793 | 1808 | if (mandatory) { |
| 1794 | ast_invalid_token_error(pc, token); | |
| 1809 | ast_expect_token(pc, token, TokenIdKeywordFor); | |
| 1795 | 1810 | } else { |
| 1796 | 1811 | return nullptr; |
| 1797 | 1812 | } |
| ... | ... | @@ -1829,7 +1844,7 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m |
| 1829 | 1844 | |
| 1830 | 1845 | if (token->id != TokenIdKeywordSwitch) { |
| 1831 | 1846 | if (mandatory) { |
| 1832 | ast_invalid_token_error(pc, token); | |
| 1847 | ast_expect_token(pc, token, TokenIdKeywordSwitch); | |
| 1833 | 1848 | } else { |
| 1834 | 1849 | return nullptr; |
| 1835 | 1850 | } |
| ... | ... | @@ -2082,7 +2097,7 @@ static AstNode *ast_parse_label(ParseContext *pc, int *token_index, bool mandato |
| 2082 | 2097 | Token *symbol_token = &pc->tokens->at(*token_index); |
| 2083 | 2098 | if (symbol_token->id != TokenIdSymbol) { |
| 2084 | 2099 | if (mandatory) { |
| 2085 | ast_invalid_token_error(pc, symbol_token); | |
| 2100 | ast_expect_token(pc, symbol_token, TokenIdSymbol); | |
| 2086 | 2101 | } else { |
| 2087 | 2102 | return nullptr; |
| 2088 | 2103 | } |
| ... | ... | @@ -2091,7 +2106,7 @@ static AstNode *ast_parse_label(ParseContext *pc, int *token_index, bool mandato |
| 2091 | 2106 | Token *colon_token = &pc->tokens->at(*token_index + 1); |
| 2092 | 2107 | if (colon_token->id != TokenIdColon) { |
| 2093 | 2108 | if (mandatory) { |
| 2094 | ast_invalid_token_error(pc, colon_token); | |
| 2109 | ast_expect_token(pc, colon_token, TokenIdColon); | |
| 2095 | 2110 | } else { |
| 2096 | 2111 | return nullptr; |
| 2097 | 2112 | } |
| ... | ... | @@ -2122,7 +2137,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato |
| 2122 | 2137 | |
| 2123 | 2138 | if (last_token->id != TokenIdLBrace) { |
| 2124 | 2139 | if (mandatory) { |
| 2125 | ast_invalid_token_error(pc, last_token); | |
| 2140 | ast_expect_token(pc, last_token, TokenIdLBrace); | |
| 2126 | 2141 | } else { |
| 2127 | 2142 | return nullptr; |
| 2128 | 2143 | } |
| ... | ... | @@ -2245,7 +2260,7 @@ static AstNode *ast_parse_extern_decl(ParseContext *pc, int *token_index, bool m |
| 2245 | 2260 | Token *extern_kw = &pc->tokens->at(*token_index); |
| 2246 | 2261 | if (extern_kw->id != TokenIdKeywordExtern) { |
| 2247 | 2262 | if (mandatory) { |
| 2248 | ast_invalid_token_error(pc, extern_kw); | |
| 2263 | ast_expect_token(pc, extern_kw, TokenIdKeywordExtern); | |
| 2249 | 2264 | } else { |
| 2250 | 2265 | return nullptr; |
| 2251 | 2266 | } |
| ... | ... | @@ -2591,7 +2606,9 @@ void normalize_parent_ptrs(AstNode *node) { |
| 2591 | 2606 | break; |
| 2592 | 2607 | case NodeTypeFnProto: |
| 2593 | 2608 | set_field(&node->data.fn_proto.return_type); |
| 2594 | set_list_fields(node->data.fn_proto.directives); | |
| 2609 | if (node->data.fn_proto.directives) { | |
| 2610 | set_list_fields(node->data.fn_proto.directives); | |
| 2611 | } | |
| 2595 | 2612 | set_list_fields(&node->data.fn_proto.params); |
| 2596 | 2613 | break; |
| 2597 | 2614 | case NodeTypeFnDef: |
test/run_tests.cpp+17| ... | ... | @@ -1866,6 +1866,23 @@ fn f(i32) {} |
| 1866 | 1866 | )SOURCE", 2, |
| 1867 | 1867 | ".tmp_source.zig:2:1: error: missing function name", |
| 1868 | 1868 | ".tmp_source.zig:3:6: error: missing parameter name"); |
| 1869 | ||
| 1870 | add_compile_fail_case("wrong function type", R"SOURCE( | |
| 1871 | const fns = []fn(){ a, b, c }; | |
| 1872 | fn a() -> i32 {0} | |
| 1873 | fn b() -> i32 {1} | |
| 1874 | fn c() -> i32 {2} | |
| 1875 | )SOURCE", 3, | |
| 1876 | ".tmp_source.zig:2:21: error: expected type 'fn()', got 'fn() -> i32'", | |
| 1877 | ".tmp_source.zig:2:24: error: expected type 'fn()', got 'fn() -> i32'", | |
| 1878 | ".tmp_source.zig:2:27: error: expected type 'fn()', got 'fn() -> i32'"); | |
| 1879 | ||
| 1880 | add_compile_fail_case("extern function pointer mismatch", R"SOURCE( | |
| 1881 | const fns = [](fn(i32)->i32){ a, b, c }; | |
| 1882 | pub fn a(x: i32) -> i32 {x + 0} | |
| 1883 | pub fn b(x: i32) -> i32 {x + 1} | |
| 1884 | export fn c(x: i32) -> i32 {x + 2} | |
| 1885 | )SOURCE", 1, ".tmp_source.zig:2:37: error: expected type 'fn(i32) -> i32', got 'extern fn(i32) -> i32'"); | |
| 1869 | 1886 | } |
| 1870 | 1887 | |
| 1871 | 1888 | ////////////////////////////////////////////////////////////////////////////// |