diff --git a/doc/langref.md b/doc/langref.md index 1a36202099..451643fa4e 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -35,7 +35,7 @@ Directive = "#" "Symbol" "(" "String" ")" VisibleMod = "pub" | "export" -FnDef = FnProto Block +FnDef = option("inline") FnProto Block ParamDeclList = "(" list(ParamDecl, ",") ")" diff --git a/src/all_types.hpp b/src/all_types.hpp index 3842621c1f..49d2db1322 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -174,13 +174,14 @@ enum VisibMod { }; struct AstNodeFnProto { - ZigList *directives; + ZigList *directives; // can be null if no directives VisibMod visib_mod; Buf name; ZigList params; AstNode *return_type; bool is_var_args; bool is_extern; + bool is_inline; // populated by semantic analyzer: diff --git a/src/analyze.cpp b/src/analyze.cpp index be67b9c933..c9123912fe 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -714,29 +714,32 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t return; } - bool is_naked = false; - for (int i = 0; i < fn_proto->directives->length; i += 1) { - AstNode *directive_node = fn_proto->directives->at(i); - Buf *name = &directive_node->data.directive.name; + fn_table_entry->is_inline = fn_proto->is_inline; - if (buf_eql_str(name, "attribute")) { - Buf *attr_name = &directive_node->data.directive.param; - if (fn_table_entry->fn_def_node) { - if (buf_eql_str(attr_name, "naked")) { - is_naked = true; - } else if (buf_eql_str(attr_name, "inline")) { - fn_table_entry->is_inline = true; + bool is_naked = false; + + if (fn_proto->directives) { + for (int i = 0; i < fn_proto->directives->length; i += 1) { + AstNode *directive_node = fn_proto->directives->at(i); + Buf *name = &directive_node->data.directive.name; + + if (buf_eql_str(name, "attribute")) { + Buf *attr_name = &directive_node->data.directive.param; + if (fn_table_entry->fn_def_node) { + if (buf_eql_str(attr_name, "naked")) { + is_naked = true; + } else { + add_node_error(g, directive_node, + buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); + } } else { add_node_error(g, directive_node, buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); } } else { add_node_error(g, directive_node, - buf_sprintf("invalid function attribute: '%s'", buf_ptr(name))); + buf_sprintf("invalid directive: '%s'", buf_ptr(name))); } - } else { - add_node_error(g, directive_node, - buf_sprintf("invalid directive: '%s'", buf_ptr(name))); } } @@ -5174,11 +5177,13 @@ void semantic_analyze(CodeGen *g) { for (int i = 0; i < import->root->data.root.top_level_decls.length; i += 1) { AstNode *child = import->root->data.root.top_level_decls.at(i); if (child->type == NodeTypeImport) { - for (int i = 0; i < child->data.import.directives->length; i += 1) { - AstNode *directive_node = child->data.import.directives->at(i); - Buf *name = &directive_node->data.directive.name; - add_node_error(g, directive_node, - buf_sprintf("invalid directive: '%s'", buf_ptr(name))); + if (child->data.import.directives) { + for (int i = 0; i < child->data.import.directives->length; i += 1) { + AstNode *directive_node = child->data.import.directives->at(i); + Buf *name = &directive_node->data.directive.name; + add_node_error(g, directive_node, + buf_sprintf("invalid directive: '%s'", buf_ptr(name))); + } } ImportTableEntry *target_import = child->data.import.import; diff --git a/src/ast_render.cpp b/src/ast_render.cpp index beacd823d6..c4268d01c5 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -77,6 +77,10 @@ static const char *extern_string(bool is_extern) { return is_extern ? "extern " : ""; } +static const char *inline_string(bool is_inline) { + return is_inline ? "inline " : ""; +} + static const char *const_or_var_string(bool is_const) { return is_const ? "const" : "var"; } @@ -553,7 +557,8 @@ static void render_node(AstRender *ar, AstNode *node) { const char *fn_name = buf_ptr(&node->data.fn_proto.name); const char *pub_str = visib_mod_string(node->data.fn_proto.visib_mod); const char *extern_str = extern_string(node->data.fn_proto.is_extern); - fprintf(ar->f, "%s%sfn %s(", pub_str, extern_str, fn_name); + const char *inline_str = inline_string(node->data.fn_proto.is_inline); + fprintf(ar->f, "%s%s%sfn %s(", pub_str, inline_str, extern_str, fn_name); int arg_count = node->data.fn_proto.params.length; bool is_var_args = node->data.fn_proto.is_var_args; for (int arg_i = 0; arg_i < arg_count; arg_i += 1) { @@ -583,8 +588,10 @@ static void render_node(AstRender *ar, AstNode *node) { break; } case NodeTypeFnDef: - for (int i = 0; i < node->data.fn_def.fn_proto->data.fn_proto.directives->length; i += 1) { - render_node(ar, node->data.fn_def.fn_proto->data.fn_proto.directives->at(i)); + if (node->data.fn_def.fn_proto->data.fn_proto.directives) { + for (int i = 0; i < node->data.fn_def.fn_proto->data.fn_proto.directives->length; i += 1) { + render_node(ar, node->data.fn_def.fn_proto->data.fn_proto.directives->at(i)); + } } render_node(ar, node->data.fn_def.fn_proto); fprintf(ar->f, " "); diff --git a/src/codegen.cpp b/src/codegen.cpp index b443a6fc9a..f534519062 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3338,20 +3338,23 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, add_node_error(g, top_level_decl, buf_sprintf("root export declaration only valid in root source file")); } else { - for (int i = 0; i < top_level_decl->data.root_export_decl.directives->length; i += 1) { - AstNode *directive_node = top_level_decl->data.root_export_decl.directives->at(i); - Buf *name = &directive_node->data.directive.name; - Buf *param = &directive_node->data.directive.param; - if (buf_eql_str(name, "version")) { - set_root_export_version(g, param, directive_node); - } else if (buf_eql_str(name, "link")) { - g->link_table.put(param, true); - if (buf_eql_str(param, "c")) { - g->link_libc = true; + ZigList *directives = top_level_decl->data.root_export_decl.directives; + if (directives) { + for (int i = 0; i < directives->length; i += 1) { + AstNode *directive_node = directives->at(i); + Buf *name = &directive_node->data.directive.name; + Buf *param = &directive_node->data.directive.param; + if (buf_eql_str(name, "version")) { + set_root_export_version(g, param, directive_node); + } else if (buf_eql_str(name, "link")) { + g->link_table.put(param, true); + if (buf_eql_str(param, "c")) { + g->link_libc = true; + } + } else { + add_node_error(g, directive_node, + buf_sprintf("invalid directive: '%s'", buf_ptr(name))); } - } else { - add_node_error(g, directive_node, - buf_sprintf("invalid directive: '%s'", buf_ptr(name))); } } diff --git a/src/parseh.cpp b/src/parseh.cpp index 1e1bcacaee..cf577f0f0a 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -115,10 +115,6 @@ static AstNode *create_field_access_node(Context *c, const char *lhs, const char return node; } -static ZigList *create_empty_directives(Context *c) { - return allocate>(1); -} - static AstNode *create_typed_var_decl_node(Context *c, bool is_const, const char *var_name, AstNode *type_node, AstNode *init_node) { @@ -127,7 +123,7 @@ static AstNode *create_typed_var_decl_node(Context *c, bool is_const, const char node->data.variable_declaration.is_const = is_const; node->data.variable_declaration.visib_mod = c->visib_mod; node->data.variable_declaration.expr = init_node; - node->data.variable_declaration.directives = create_empty_directives(c); + node->data.variable_declaration.directives = nullptr; node->data.variable_declaration.type = type_node; normalize_parent_ptrs(node); return node; @@ -150,7 +146,6 @@ static AstNode *create_struct_field_node(Context *c, const char *name, AstNode * assert(type_node); AstNode *node = create_node(c, NodeTypeStructField); buf_init_from_str(&node->data.struct_field.name, name); - node->data.struct_field.directives = create_empty_directives(c); node->data.struct_field.visib_mod = VisibModPub; node->data.struct_field.type = type_node; @@ -197,18 +192,10 @@ static AstNode *create_num_lit_signed(Context *c, int64_t x) { return create_prefix_node(c, PrefixOpNegation, num_lit_node); } -static AstNode *create_directive_node(Context *c, const char *name, const char *value) { - AstNode *node = create_node(c, NodeTypeDirective); - buf_init_from_str(&node->data.directive.name, name); - buf_init_from_str(&node->data.directive.param, value); - return node; -} - static AstNode *create_type_decl_node(Context *c, const char *name, AstNode *child_type_node) { AstNode *node = create_node(c, NodeTypeTypeDecl); buf_init_from_str(&node->data.type_decl.symbol, name); node->data.type_decl.visib_mod = c->visib_mod; - node->data.type_decl.directives = create_empty_directives(c); node->data.type_decl.child_type = child_type_node; normalize_parent_ptrs(node); @@ -224,8 +211,7 @@ static AstNode *make_type_node(Context *c, TypeTableEntry *type_entry) { static AstNode *create_fn_proto_node(Context *c, Buf *name, TypeTableEntry *fn_type) { assert(fn_type->id == TypeTableEntryIdFn); AstNode *node = create_node(c, NodeTypeFnProto); - node->data.fn_proto.directives = create_empty_directives(c); - node->data.fn_proto.directives->append(create_directive_node(c, "attribute", "inline")); + node->data.fn_proto.is_inline = true; node->data.fn_proto.visib_mod = c->visib_mod; buf_init_from_buf(&node->data.fn_proto.name, name); 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) { node->data.fn_proto.is_extern = fn_type->data.fn.fn_type_id.is_extern; node->data.fn_proto.visib_mod = c->visib_mod; - node->data.fn_proto.directives = create_empty_directives(c); node->data.fn_proto.is_var_args = fn_type->data.fn.fn_type_id.is_var_args; node->data.fn_proto.return_type = make_type_node(c, fn_type->data.fn.fn_type_id.return_type); @@ -826,7 +811,6 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) { buf_init_from_buf(&enum_node->data.struct_decl.name, full_type_name); enum_node->data.struct_decl.kind = ContainerKindEnum; enum_node->data.struct_decl.visib_mod = VisibModExport; - enum_node->data.struct_decl.directives = create_empty_directives(c); enum_node->data.struct_decl.type_entry = enum_type; 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) { buf_init_from_buf(&struct_node->data.struct_decl.name, &struct_type->name); struct_node->data.struct_decl.kind = ContainerKindStruct; struct_node->data.struct_decl.visib_mod = VisibModExport; - struct_node->data.struct_decl.directives = create_empty_directives(c); struct_node->data.struct_decl.type_entry = struct_type; for (uint32_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { diff --git a/src/parser.cpp b/src/parser.cpp index 7e7f073722..fe17495fc4 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -82,13 +82,6 @@ static AstNode *ast_create_node(ParseContext *pc, NodeType type, Token *first_to return node; } -static AstNode *ast_create_node_with_node(ParseContext *pc, NodeType type, AstNode *other_node) { - AstNode *node = ast_create_node_no_line_info(pc, type); - node->line = other_node->line; - node->column = other_node->column; - return node; -} - static AstNode *ast_create_void_type_node(ParseContext *pc, Token *token) { AstNode *node = ast_create_node(pc, NodeTypeSymbol, token); 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 } /* -FnDef : FnProto Block +FnDef = option("inline") FnProto Block */ static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandatory, ZigList *directives, VisibMod visib_mod) { + Token *first_token = &pc->tokens->at(*token_index); + bool is_inline; + if (first_token->id == TokenIdKeywordInline) { + *token_index += 1; + is_inline = true; + } else { + is_inline = false; + } + AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, directives, visib_mod); if (!fn_proto) return nullptr; - AstNode *node = ast_create_node_with_node(pc, NodeTypeFnDef, fn_proto); + AstNode *node = ast_create_node(pc, NodeTypeFnDef, first_token); + + fn_proto->data.fn_proto.is_inline = is_inline; node->data.fn_def.fn_proto = fn_proto; node->data.fn_def.body = ast_parse_block(pc, token_index, true); @@ -2646,8 +2650,10 @@ static void set_field(AstNode **field) { } static void set_list_fields(ZigList *list) { - for (int i = 0; i < list->length; i += 1) { - set_field(&list->at(i)); + if (list) { + for (int i = 0; i < list->length; i += 1) { + set_field(&list->at(i)); + } } } @@ -2661,9 +2667,7 @@ void normalize_parent_ptrs(AstNode *node) { break; case NodeTypeFnProto: set_field(&node->data.fn_proto.return_type); - if (node->data.fn_proto.directives) { - set_list_fields(node->data.fn_proto.directives); - } + set_list_fields(node->data.fn_proto.directives); set_list_fields(&node->data.fn_proto.params); break; case NodeTypeFnDef: @@ -2686,9 +2690,7 @@ void normalize_parent_ptrs(AstNode *node) { set_field(&node->data.return_expr.expr); break; case NodeTypeVariableDeclaration: - if (node->data.variable_declaration.directives) { - set_list_fields(node->data.variable_declaration.directives); - } + set_list_fields(node->data.variable_declaration.directives); set_field(&node->data.variable_declaration.type); set_field(&node->data.variable_declaration.expr); break; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 6d8c55f383..aacb5cbee8 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -101,7 +101,7 @@ const char * zig_keywords[] = { "true", "false", "null", "fn", "return", "var", "const", "extern", "pub", "export", "import", "c_import", "if", "else", "goto", "asm", "volatile", "struct", "enum", "while", "for", "continue", "break", - "null", "noalias", "switch", "undefined", "error", "type" + "null", "noalias", "switch", "undefined", "error", "type", "inline", }; bool is_zig_keyword(Buf *buf) { @@ -273,6 +273,8 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordError; } else if (mem_eql_str(token_mem, token_len, "type")) { t->cur_tok->id = TokenIdKeywordType; + } else if (mem_eql_str(token_mem, token_len, "inline")) { + t->cur_tok->id = TokenIdKeywordInline; } t->cur_tok = nullptr; @@ -1087,6 +1089,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordUndefined: return "undefined"; case TokenIdKeywordError: return "error"; case TokenIdKeywordType: return "type"; + case TokenIdKeywordInline: return "inline"; case TokenIdLParen: return "("; case TokenIdRParen: return ")"; case TokenIdComma: return ","; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 627e045201..5fd59a572c 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -41,6 +41,7 @@ enum TokenId { TokenIdKeywordUndefined, TokenIdKeywordError, TokenIdKeywordType, + TokenIdKeywordInline, TokenIdLParen, TokenIdRParen, TokenIdComma, diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 83fb14c12a..da8e011b3d 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -2114,8 +2114,7 @@ extern void (*fn_ptr)(void); #define foo fn_ptr )SOURCE", 2, "pub extern var fn_ptr: ?extern fn();", - R"SOURCE(#attribute("inline") -pub fn foo() { + R"SOURCE(pub inline fn foo() { (??fn_ptr)() })SOURCE"); }