| author | |
| committer | |
| log | 06f6acb4b19cab55dd8637a41c11501bf8e7158e |
| tree | 9d8226f9f2128a91b3038d9b6d739c3b36ee15cf |
| parent | 122b7b99660f2ce354930749ca545b363bf7849d |
10 files changed, 81 insertions(+), 77 deletions(-)
doc/langref.md+1-1| ... | ... | @@ -35,7 +35,7 @@ Directive = "#" "Symbol" "(" "String" ")" |
| 35 | 35 | |
| 36 | 36 | VisibleMod = "pub" | "export" |
| 37 | 37 | |
| 38 | FnDef = FnProto Block | |
| 38 | FnDef = option("inline") FnProto Block | |
| 39 | 39 | |
| 40 | 40 | ParamDeclList = "(" list(ParamDecl, ",") ")" |
| 41 | 41 |
src/all_types.hpp+2-1| ... | ... | @@ -174,13 +174,14 @@ enum VisibMod { |
| 174 | 174 | }; |
| 175 | 175 | |
| 176 | 176 | struct AstNodeFnProto { |
| 177 | ZigList<AstNode *> *directives; | |
| 177 | ZigList<AstNode *> *directives; // can be null if no directives | |
| 178 | 178 | VisibMod visib_mod; |
| 179 | 179 | Buf name; |
| 180 | 180 | ZigList<AstNode *> params; |
| 181 | 181 | AstNode *return_type; |
| 182 | 182 | bool is_var_args; |
| 183 | 183 | bool is_extern; |
| 184 | bool is_inline; | |
| 184 | 185 | |
| 185 | 186 | // populated by semantic analyzer: |
| 186 | 187 |
src/analyze.cpp+25-20| ... | ... | @@ -714,29 +714,32 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t |
| 714 | 714 | return; |
| 715 | 715 | } |
| 716 | 716 | |
| 717 | fn_table_entry->is_inline = fn_proto->is_inline; | |
| 718 | ||
| 717 | 719 | bool is_naked = false; |
| 718 | for (int i = 0; i < fn_proto->directives->length; i += 1) { | |
| 719 | AstNode *directive_node = fn_proto->directives->at(i); | |
| 720 | Buf *name = &directive_node->data.directive.name; | |
| 721 | ||
| 722 | if (buf_eql_str(name, "attribute")) { | |
| 723 | Buf *attr_name = &directive_node->data.directive.param; | |
| 724 | if (fn_table_entry->fn_def_node) { | |
| 725 | if (buf_eql_str(attr_name, "naked")) { | |
| 726 | is_naked = true; | |
| 727 | } else if (buf_eql_str(attr_name, "inline")) { | |
| 728 | fn_table_entry->is_inline = true; | |
| 720 | ||
| 721 | if (fn_proto->directives) { | |
| 722 | for (int i = 0; i < fn_proto->directives->length; i += 1) { | |
| 723 | AstNode *directive_node = fn_proto->directives->at(i); | |
| 724 | Buf *name = &directive_node->data.directive.name; | |
| 725 | ||
| 726 | if (buf_eql_str(name, "attribute")) { | |
| 727 | Buf *attr_name = &directive_node->data.directive.param; | |
| 728 | if (fn_table_entry->fn_def_node) { | |
| 729 | if (buf_eql_str(attr_name, "naked")) { | |
| 730 | is_naked = true; | |
| 731 | } else { | |
| 732 | add_node_error(g, directive_node, | |
| 733 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | |
| 734 | } | |
| 729 | 735 | } else { |
| 730 | 736 | add_node_error(g, directive_node, |
| 731 | 737 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); |
| 732 | 738 | } |
| 733 | 739 | } else { |
| 734 | 740 | add_node_error(g, directive_node, |
| 735 | buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); | |
| 741 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 736 | 742 | } |
| 737 | } else { | |
| 738 | add_node_error(g, directive_node, | |
| 739 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 740 | 743 | } |
| 741 | 744 | } |
| 742 | 745 | |
| ... | ... | @@ -5174,11 +5177,13 @@ void semantic_analyze(CodeGen *g) { |
| 5174 | 5177 | for (int i = 0; i < import->root->data.root.top_level_decls.length; i += 1) { |
| 5175 | 5178 | AstNode *child = import->root->data.root.top_level_decls.at(i); |
| 5176 | 5179 | if (child->type == NodeTypeImport) { |
| 5177 | for (int i = 0; i < child->data.import.directives->length; i += 1) { | |
| 5178 | AstNode *directive_node = child->data.import.directives->at(i); | |
| 5179 | Buf *name = &directive_node->data.directive.name; | |
| 5180 | add_node_error(g, directive_node, | |
| 5181 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 5180 | if (child->data.import.directives) { | |
| 5181 | for (int i = 0; i < child->data.import.directives->length; i += 1) { | |
| 5182 | AstNode *directive_node = child->data.import.directives->at(i); | |
| 5183 | Buf *name = &directive_node->data.directive.name; | |
| 5184 | add_node_error(g, directive_node, | |
| 5185 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 5186 | } | |
| 5182 | 5187 | } |
| 5183 | 5188 | |
| 5184 | 5189 | ImportTableEntry *target_import = child->data.import.import; |
src/ast_render.cpp+10-3| ... | ... | @@ -77,6 +77,10 @@ static const char *extern_string(bool is_extern) { |
| 77 | 77 | return is_extern ? "extern " : ""; |
| 78 | 78 | } |
| 79 | 79 | |
| 80 | static const char *inline_string(bool is_inline) { | |
| 81 | return is_inline ? "inline " : ""; | |
| 82 | } | |
| 83 | ||
| 80 | 84 | static const char *const_or_var_string(bool is_const) { |
| 81 | 85 | return is_const ? "const" : "var"; |
| 82 | 86 | } |
| ... | ... | @@ -553,7 +557,8 @@ static void render_node(AstRender *ar, AstNode *node) { |
| 553 | 557 | const char *fn_name = buf_ptr(&node->data.fn_proto.name); |
| 554 | 558 | const char *pub_str = visib_mod_string(node->data.fn_proto.visib_mod); |
| 555 | 559 | const char *extern_str = extern_string(node->data.fn_proto.is_extern); |
| 556 | fprintf(ar->f, "%s%sfn %s(", pub_str, extern_str, fn_name); | |
| 560 | const char *inline_str = inline_string(node->data.fn_proto.is_inline); | |
| 561 | fprintf(ar->f, "%s%s%sfn %s(", pub_str, inline_str, extern_str, fn_name); | |
| 557 | 562 | int arg_count = node->data.fn_proto.params.length; |
| 558 | 563 | bool is_var_args = node->data.fn_proto.is_var_args; |
| 559 | 564 | for (int arg_i = 0; arg_i < arg_count; arg_i += 1) { |
| ... | ... | @@ -583,8 +588,10 @@ static void render_node(AstRender *ar, AstNode *node) { |
| 583 | 588 | break; |
| 584 | 589 | } |
| 585 | 590 | case NodeTypeFnDef: |
| 586 | for (int i = 0; i < node->data.fn_def.fn_proto->data.fn_proto.directives->length; i += 1) { | |
| 587 | render_node(ar, node->data.fn_def.fn_proto->data.fn_proto.directives->at(i)); | |
| 591 | if (node->data.fn_def.fn_proto->data.fn_proto.directives) { | |
| 592 | for (int i = 0; i < node->data.fn_def.fn_proto->data.fn_proto.directives->length; i += 1) { | |
| 593 | render_node(ar, node->data.fn_def.fn_proto->data.fn_proto.directives->at(i)); | |
| 594 | } | |
| 588 | 595 | } |
| 589 | 596 | render_node(ar, node->data.fn_def.fn_proto); |
| 590 | 597 | fprintf(ar->f, " "); |
src/codegen.cpp+16-13| ... | ... | @@ -3338,20 +3338,23 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, |
| 3338 | 3338 | add_node_error(g, top_level_decl, |
| 3339 | 3339 | buf_sprintf("root export declaration only valid in root source file")); |
| 3340 | 3340 | } else { |
| 3341 | for (int i = 0; i < top_level_decl->data.root_export_decl.directives->length; i += 1) { | |
| 3342 | AstNode *directive_node = top_level_decl->data.root_export_decl.directives->at(i); | |
| 3343 | Buf *name = &directive_node->data.directive.name; | |
| 3344 | Buf *param = &directive_node->data.directive.param; | |
| 3345 | if (buf_eql_str(name, "version")) { | |
| 3346 | set_root_export_version(g, param, directive_node); | |
| 3347 | } else if (buf_eql_str(name, "link")) { | |
| 3348 | g->link_table.put(param, true); | |
| 3349 | if (buf_eql_str(param, "c")) { | |
| 3350 | g->link_libc = true; | |
| 3341 | ZigList<AstNode *> *directives = top_level_decl->data.root_export_decl.directives; | |
| 3342 | if (directives) { | |
| 3343 | for (int i = 0; i < directives->length; i += 1) { | |
| 3344 | AstNode *directive_node = directives->at(i); | |
| 3345 | Buf *name = &directive_node->data.directive.name; | |
| 3346 | Buf *param = &directive_node->data.directive.param; | |
| 3347 | if (buf_eql_str(name, "version")) { | |
| 3348 | set_root_export_version(g, param, directive_node); | |
| 3349 | } else if (buf_eql_str(name, "link")) { | |
| 3350 | g->link_table.put(param, true); | |
| 3351 | if (buf_eql_str(param, "c")) { | |
| 3352 | g->link_libc = true; | |
| 3353 | } | |
| 3354 | } else { | |
| 3355 | add_node_error(g, directive_node, | |
| 3356 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 3351 | 3357 | } |
| 3352 | } else { | |
| 3353 | add_node_error(g, directive_node, | |
| 3354 | buf_sprintf("invalid directive: '%s'", buf_ptr(name))); | |
| 3355 | 3358 | } |
| 3356 | 3359 | } |
| 3357 | 3360 |
src/parseh.cpp+2-19| ... | ... | @@ -115,10 +115,6 @@ static AstNode *create_field_access_node(Context *c, const char *lhs, const char |
| 115 | 115 | return node; |
| 116 | 116 | } |
| 117 | 117 | |
| 118 | static ZigList<AstNode *> *create_empty_directives(Context *c) { | |
| 119 | return allocate<ZigList<AstNode*>>(1); | |
| 120 | } | |
| 121 | ||
| 122 | 118 | static AstNode *create_typed_var_decl_node(Context *c, bool is_const, const char *var_name, |
| 123 | 119 | AstNode *type_node, AstNode *init_node) |
| 124 | 120 | { |
| ... | ... | @@ -127,7 +123,7 @@ static AstNode *create_typed_var_decl_node(Context *c, bool is_const, const char |
| 127 | 123 | node->data.variable_declaration.is_const = is_const; |
| 128 | 124 | node->data.variable_declaration.visib_mod = c->visib_mod; |
| 129 | 125 | node->data.variable_declaration.expr = init_node; |
| 130 | node->data.variable_declaration.directives = create_empty_directives(c); | |
| 126 | node->data.variable_declaration.directives = nullptr; | |
| 131 | 127 | node->data.variable_declaration.type = type_node; |
| 132 | 128 | normalize_parent_ptrs(node); |
| 133 | 129 | return node; |
| ... | ... | @@ -150,7 +146,6 @@ static AstNode *create_struct_field_node(Context *c, const char *name, AstNode * |
| 150 | 146 | assert(type_node); |
| 151 | 147 | AstNode *node = create_node(c, NodeTypeStructField); |
| 152 | 148 | buf_init_from_str(&node->data.struct_field.name, name); |
| 153 | node->data.struct_field.directives = create_empty_directives(c); | |
| 154 | 149 | node->data.struct_field.visib_mod = VisibModPub; |
| 155 | 150 | node->data.struct_field.type = type_node; |
| 156 | 151 | |
| ... | ... | @@ -197,18 +192,10 @@ static AstNode *create_num_lit_signed(Context *c, int64_t x) { |
| 197 | 192 | return create_prefix_node(c, PrefixOpNegation, num_lit_node); |
| 198 | 193 | } |
| 199 | 194 | |
| 200 | static AstNode *create_directive_node(Context *c, const char *name, const char *value) { | |
| 201 | AstNode *node = create_node(c, NodeTypeDirective); | |
| 202 | buf_init_from_str(&node->data.directive.name, name); | |
| 203 | buf_init_from_str(&node->data.directive.param, value); | |
| 204 | return node; | |
| 205 | } | |
| 206 | ||
| 207 | 195 | static AstNode *create_type_decl_node(Context *c, const char *name, AstNode *child_type_node) { |
| 208 | 196 | AstNode *node = create_node(c, NodeTypeTypeDecl); |
| 209 | 197 | buf_init_from_str(&node->data.type_decl.symbol, name); |
| 210 | 198 | node->data.type_decl.visib_mod = c->visib_mod; |
| 211 | node->data.type_decl.directives = create_empty_directives(c); | |
| 212 | 199 | node->data.type_decl.child_type = child_type_node; |
| 213 | 200 | |
| 214 | 201 | normalize_parent_ptrs(node); |
| ... | ... | @@ -224,8 +211,7 @@ static AstNode *make_type_node(Context *c, TypeTableEntry *type_entry) { |
| 224 | 211 | static AstNode *create_fn_proto_node(Context *c, Buf *name, TypeTableEntry *fn_type) { |
| 225 | 212 | assert(fn_type->id == TypeTableEntryIdFn); |
| 226 | 213 | AstNode *node = create_node(c, NodeTypeFnProto); |
| 227 | node->data.fn_proto.directives = create_empty_directives(c); | |
| 228 | node->data.fn_proto.directives->append(create_directive_node(c, "attribute", "inline")); | |
| 214 | node->data.fn_proto.is_inline = true; | |
| 229 | 215 | node->data.fn_proto.visib_mod = c->visib_mod; |
| 230 | 216 | buf_init_from_buf(&node->data.fn_proto.name, name); |
| 231 | 217 | node->data.fn_proto.return_type = make_type_node(c, fn_type->data.fn.fn_type_id.return_type); |
| ... | ... | @@ -642,7 +628,6 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { |
| 642 | 628 | |
| 643 | 629 | node->data.fn_proto.is_extern = fn_type->data.fn.fn_type_id.is_extern; |
| 644 | 630 | node->data.fn_proto.visib_mod = c->visib_mod; |
| 645 | node->data.fn_proto.directives = create_empty_directives(c); | |
| 646 | 631 | node->data.fn_proto.is_var_args = fn_type->data.fn.fn_type_id.is_var_args; |
| 647 | 632 | node->data.fn_proto.return_type = make_type_node(c, fn_type->data.fn.fn_type_id.return_type); |
| 648 | 633 | |
| ... | ... | @@ -826,7 +811,6 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) { |
| 826 | 811 | buf_init_from_buf(&enum_node->data.struct_decl.name, full_type_name); |
| 827 | 812 | enum_node->data.struct_decl.kind = ContainerKindEnum; |
| 828 | 813 | enum_node->data.struct_decl.visib_mod = VisibModExport; |
| 829 | enum_node->data.struct_decl.directives = create_empty_directives(c); | |
| 830 | 814 | enum_node->data.struct_decl.type_entry = enum_type; |
| 831 | 815 | |
| 832 | 816 | for (uint32_t i = 0; i < field_count; i += 1) { |
| ... | ... | @@ -998,7 +982,6 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) { |
| 998 | 982 | buf_init_from_buf(&struct_node->data.struct_decl.name, &struct_type->name); |
| 999 | 983 | struct_node->data.struct_decl.kind = ContainerKindStruct; |
| 1000 | 984 | struct_node->data.struct_decl.visib_mod = VisibModExport; |
| 1001 | struct_node->data.struct_decl.directives = create_empty_directives(c); | |
| 1002 | 985 | struct_node->data.struct_decl.type_entry = struct_type; |
| 1003 | 986 | |
| 1004 | 987 | for (uint32_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { |
src/parser.cpp+19-17| ... | ... | @@ -82,13 +82,6 @@ static AstNode *ast_create_node(ParseContext *pc, NodeType type, Token *first_to |
| 82 | 82 | return node; |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | static AstNode *ast_create_node_with_node(ParseContext *pc, NodeType type, AstNode *other_node) { | |
| 86 | AstNode *node = ast_create_node_no_line_info(pc, type); | |
| 87 | node->line = other_node->line; | |
| 88 | node->column = other_node->column; | |
| 89 | return node; | |
| 90 | } | |
| 91 | ||
| 92 | 85 | static AstNode *ast_create_void_type_node(ParseContext *pc, Token *token) { |
| 93 | 86 | AstNode *node = ast_create_node(pc, NodeTypeSymbol, token); |
| 94 | 87 | buf_init_from_str(&node->data.symbol_expr.symbol, "void"); |
| ... | ... | @@ -2240,15 +2233,26 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand |
| 2240 | 2233 | } |
| 2241 | 2234 | |
| 2242 | 2235 | /* |
| 2243 | FnDef : FnProto Block | |
| 2236 | FnDef = option("inline") FnProto Block | |
| 2244 | 2237 | */ |
| 2245 | 2238 | static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandatory, |
| 2246 | 2239 | ZigList<AstNode*> *directives, VisibMod visib_mod) |
| 2247 | 2240 | { |
| 2241 | Token *first_token = &pc->tokens->at(*token_index); | |
| 2242 | bool is_inline; | |
| 2243 | if (first_token->id == TokenIdKeywordInline) { | |
| 2244 | *token_index += 1; | |
| 2245 | is_inline = true; | |
| 2246 | } else { | |
| 2247 | is_inline = false; | |
| 2248 | } | |
| 2249 | ||
| 2248 | 2250 | AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, directives, visib_mod); |
| 2249 | 2251 | if (!fn_proto) |
| 2250 | 2252 | return nullptr; |
| 2251 | AstNode *node = ast_create_node_with_node(pc, NodeTypeFnDef, fn_proto); | |
| 2253 | AstNode *node = ast_create_node(pc, NodeTypeFnDef, first_token); | |
| 2254 | ||
| 2255 | fn_proto->data.fn_proto.is_inline = is_inline; | |
| 2252 | 2256 | |
| 2253 | 2257 | node->data.fn_def.fn_proto = fn_proto; |
| 2254 | 2258 | node->data.fn_def.body = ast_parse_block(pc, token_index, true); |
| ... | ... | @@ -2646,8 +2650,10 @@ static void set_field(AstNode **field) { |
| 2646 | 2650 | } |
| 2647 | 2651 | |
| 2648 | 2652 | static void set_list_fields(ZigList<AstNode*> *list) { |
| 2649 | for (int i = 0; i < list->length; i += 1) { | |
| 2650 | set_field(&list->at(i)); | |
| 2653 | if (list) { | |
| 2654 | for (int i = 0; i < list->length; i += 1) { | |
| 2655 | set_field(&list->at(i)); | |
| 2656 | } | |
| 2651 | 2657 | } |
| 2652 | 2658 | } |
| 2653 | 2659 | |
| ... | ... | @@ -2661,9 +2667,7 @@ void normalize_parent_ptrs(AstNode *node) { |
| 2661 | 2667 | break; |
| 2662 | 2668 | case NodeTypeFnProto: |
| 2663 | 2669 | set_field(&node->data.fn_proto.return_type); |
| 2664 | if (node->data.fn_proto.directives) { | |
| 2665 | set_list_fields(node->data.fn_proto.directives); | |
| 2666 | } | |
| 2670 | set_list_fields(node->data.fn_proto.directives); | |
| 2667 | 2671 | set_list_fields(&node->data.fn_proto.params); |
| 2668 | 2672 | break; |
| 2669 | 2673 | case NodeTypeFnDef: |
| ... | ... | @@ -2686,9 +2690,7 @@ void normalize_parent_ptrs(AstNode *node) { |
| 2686 | 2690 | set_field(&node->data.return_expr.expr); |
| 2687 | 2691 | break; |
| 2688 | 2692 | case NodeTypeVariableDeclaration: |
| 2689 | if (node->data.variable_declaration.directives) { | |
| 2690 | set_list_fields(node->data.variable_declaration.directives); | |
| 2691 | } | |
| 2693 | set_list_fields(node->data.variable_declaration.directives); | |
| 2692 | 2694 | set_field(&node->data.variable_declaration.type); |
| 2693 | 2695 | set_field(&node->data.variable_declaration.expr); |
| 2694 | 2696 | break; |
src/tokenizer.cpp+4-1| ... | ... | @@ -101,7 +101,7 @@ const char * zig_keywords[] = { |
| 101 | 101 | "true", "false", "null", "fn", "return", "var", "const", "extern", |
| 102 | 102 | "pub", "export", "import", "c_import", "if", "else", "goto", "asm", |
| 103 | 103 | "volatile", "struct", "enum", "while", "for", "continue", "break", |
| 104 | "null", "noalias", "switch", "undefined", "error", "type" | |
| 104 | "null", "noalias", "switch", "undefined", "error", "type", "inline", | |
| 105 | 105 | }; |
| 106 | 106 | |
| 107 | 107 | bool is_zig_keyword(Buf *buf) { |
| ... | ... | @@ -273,6 +273,8 @@ static void end_token(Tokenize *t) { |
| 273 | 273 | t->cur_tok->id = TokenIdKeywordError; |
| 274 | 274 | } else if (mem_eql_str(token_mem, token_len, "type")) { |
| 275 | 275 | t->cur_tok->id = TokenIdKeywordType; |
| 276 | } else if (mem_eql_str(token_mem, token_len, "inline")) { | |
| 277 | t->cur_tok->id = TokenIdKeywordInline; | |
| 276 | 278 | } |
| 277 | 279 | |
| 278 | 280 | t->cur_tok = nullptr; |
| ... | ... | @@ -1087,6 +1089,7 @@ const char * token_name(TokenId id) { |
| 1087 | 1089 | case TokenIdKeywordUndefined: return "undefined"; |
| 1088 | 1090 | case TokenIdKeywordError: return "error"; |
| 1089 | 1091 | case TokenIdKeywordType: return "type"; |
| 1092 | case TokenIdKeywordInline: return "inline"; | |
| 1090 | 1093 | case TokenIdLParen: return "("; |
| 1091 | 1094 | case TokenIdRParen: return ")"; |
| 1092 | 1095 | case TokenIdComma: return ","; |
src/tokenizer.hpp+1| ... | ... | @@ -41,6 +41,7 @@ enum TokenId { |
| 41 | 41 | TokenIdKeywordUndefined, |
| 42 | 42 | TokenIdKeywordError, |
| 43 | 43 | TokenIdKeywordType, |
| 44 | TokenIdKeywordInline, | |
| 44 | 45 | TokenIdLParen, |
| 45 | 46 | TokenIdRParen, |
| 46 | 47 | TokenIdComma, |
test/run_tests.cpp+1-2| ... | ... | @@ -2114,8 +2114,7 @@ extern void (*fn_ptr)(void); |
| 2114 | 2114 | #define foo fn_ptr |
| 2115 | 2115 | )SOURCE", 2, |
| 2116 | 2116 | "pub extern var fn_ptr: ?extern fn();", |
| 2117 | R"SOURCE(#attribute("inline") | |
| 2118 | pub fn foo() { | |
| 2117 | R"SOURCE(pub inline fn foo() { | |
| 2119 | 2118 | (??fn_ptr)() |
| 2120 | 2119 | })SOURCE"); |
| 2121 | 2120 | } |