Commit fabf45f5fc

LemonBoy <thatlemon@gmail.com>
2019-09-05 12:22:02
Add the noinline keyword for function declarations
1 parent a7fd140
doc/docgen.zig
@@ -786,6 +786,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok
             .Keyword_for,
             .Keyword_if,
             .Keyword_inline,
+            .Keyword_noinline,
             .Keyword_nakedcc,
             .Keyword_noalias,
             .Keyword_or,
src/all_types.hpp
@@ -603,6 +603,12 @@ enum CallingConvention {
     CallingConventionAsync,
 };
 
+enum FnInline {
+    FnInlineAuto,
+    FnInlineAlways,
+    FnInlineNever,
+};
+
 struct AstNodeFnProto {
     VisibMod visib_mod;
     Buf *name;
@@ -612,7 +618,7 @@ struct AstNodeFnProto {
     bool is_var_args;
     bool is_extern;
     bool is_export;
-    bool is_inline;
+    FnInline fn_inline;
     CallingConvention cc;
     AstNode *fn_def_node;
     // populated if this is an extern declaration
@@ -1453,12 +1459,6 @@ enum FnAnalState {
     FnAnalStateInvalid,
 };
 
-enum FnInline {
-    FnInlineAuto,
-    FnInlineAlways,
-    FnInlineNever,
-};
-
 struct GlobalExport {
     Buf name;
     GlobalLinkageId linkage;
src/analyze.cpp
@@ -3066,8 +3066,7 @@ ZigFn *create_fn(CodeGen *g, AstNode *proto_node) {
     assert(proto_node->type == NodeTypeFnProto);
     AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
 
-    FnInline inline_value = fn_proto->is_inline ? FnInlineAlways : FnInlineAuto;
-    ZigFn *fn_entry = create_fn_raw(g, inline_value);
+    ZigFn *fn_entry = create_fn_raw(g, fn_proto->fn_inline);
 
     fn_entry->proto_node = proto_node;
     fn_entry->body_node = (proto_node->data.fn_proto.fn_def_node == nullptr) ? nullptr :
src/ast_render.cpp
@@ -124,8 +124,13 @@ static const char *export_string(bool is_export) {
 //    zig_unreachable();
 //}
 
-static const char *inline_string(bool is_inline) {
-    return is_inline ? "inline " : "";
+static const char *inline_string(FnInline fn_inline) {
+    switch (fn_inline) {
+        case FnInlineAlways: return "inline ";
+        case FnInlineNever:  return "noinline ";
+        case FnInlineAuto:   return "";
+    }
+    zig_unreachable();
 }
 
 static const char *const_or_var_string(bool is_const) {
@@ -436,7 +441,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 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);
                 const char *export_str = export_string(node->data.fn_proto.is_export);
-                const char *inline_str = inline_string(node->data.fn_proto.is_inline);
+                const char *inline_str = inline_string(node->data.fn_proto.fn_inline);
                 fprintf(ar->f, "%s%s%s%sfn ", pub_str, inline_str, export_str, extern_str);
                 if (node->data.fn_proto.name != nullptr) {
                     print_symbol(ar, node->data.fn_proto.name);
src/parser.cpp
@@ -578,7 +578,7 @@ static AstNode *ast_parse_top_level_comptime(ParseContext *pc) {
 }
 
 // TopLevelDecl
-//     <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block)
+//     <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
 //      / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl
 //      / KEYWORD_use Expr SEMICOLON
 static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
@@ -587,12 +587,14 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
         first = eat_token_if(pc, TokenIdKeywordExtern);
     if (first == nullptr)
         first = eat_token_if(pc, TokenIdKeywordInline);
+    if (first == nullptr)
+        first = eat_token_if(pc, TokenIdKeywordNoInline);
     if (first != nullptr) {
         Token *lib_name = nullptr;
         if (first->id == TokenIdKeywordExtern)
             lib_name = eat_token_if(pc, TokenIdStringLiteral);
 
-        if (first->id != TokenIdKeywordInline) {
+        if (first->id != TokenIdKeywordInline && first->id != TokenIdKeywordNoInline) {
             Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
             AstNode *var_decl = ast_parse_var_decl(pc);
             if (var_decl != nullptr) {
@@ -623,8 +625,19 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
             fn_proto->data.fn_proto.visib_mod = visib_mod;
             fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern;
             fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport;
-            fn_proto->data.fn_proto.is_inline = first->id == TokenIdKeywordInline;
+            switch (first->id) {
+                case TokenIdKeywordInline:
+                    fn_proto->data.fn_proto.fn_inline = FnInlineAlways;
+                    break;
+                case TokenIdKeywordNoInline:
+                    fn_proto->data.fn_proto.fn_inline = FnInlineNever;
+                    break;
+                default:
+                    fn_proto->data.fn_proto.fn_inline = FnInlineAuto;
+                    break;
+            }
             fn_proto->data.fn_proto.lib_name = token_buf(lib_name);
+
             AstNode *res = fn_proto;
             if (body != nullptr) {
                 res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto);
src/tokenizer.cpp
@@ -130,6 +130,7 @@ static const struct ZigKeyword zig_keywords[] = {
     {"for", TokenIdKeywordFor},
     {"if", TokenIdKeywordIf},
     {"inline", TokenIdKeywordInline},
+    {"noinline", TokenIdKeywordNoInline},
     {"nakedcc", TokenIdKeywordNakedCC},
     {"noalias", TokenIdKeywordNoAlias},
     {"null", TokenIdKeywordNull},
@@ -1551,6 +1552,7 @@ const char * token_name(TokenId id) {
         case TokenIdKeywordFor: return "for";
         case TokenIdKeywordIf: return "if";
         case TokenIdKeywordInline: return "inline";
+        case TokenIdKeywordNoInline: return "noinline";
         case TokenIdKeywordNakedCC: return "nakedcc";
         case TokenIdKeywordNoAlias: return "noalias";
         case TokenIdKeywordNull: return "null";
src/tokenizer.hpp
@@ -74,6 +74,7 @@ enum TokenId {
     TokenIdKeywordFor,
     TokenIdKeywordIf,
     TokenIdKeywordInline,
+    TokenIdKeywordNoInline,
     TokenIdKeywordLinkSection,
     TokenIdKeywordNakedCC,
     TokenIdKeywordNoAlias,
src/translate_c.cpp
@@ -432,7 +432,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r
     AstNode *fn_proto = trans_create_node(c, NodeTypeFnProto);
     fn_proto->data.fn_proto.visib_mod = c->visib_mod;
     fn_proto->data.fn_proto.name = fn_name;
-    fn_proto->data.fn_proto.is_inline = true;
+    fn_proto->data.fn_proto.fn_inline = FnInlineAlways;
     fn_proto->data.fn_proto.return_type = src_proto_node->data.fn_proto.return_type; // TODO ok for these to alias?
 
     fn_def->data.fn_def.fn_proto = fn_proto;
std/zig/parse.zig
@@ -201,7 +201,7 @@ fn parseTopLevelComptime(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*
 }
 
 /// TopLevelDecl
-///     <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block)
+///     <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
 ///      / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl
 ///      / KEYWORD_usingnamespace Expr SEMICOLON
 fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
@@ -213,6 +213,7 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
             break :blk token;
         }
         if (eatToken(it, .Keyword_inline)) |token| break :blk token;
+        if (eatToken(it, .Keyword_noinline)) |token| break :blk token;
         break :blk null;
     };
 
@@ -232,7 +233,8 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
     }
 
     if (extern_export_inline_token) |token| {
-        if (tree.tokens.at(token).id == .Keyword_inline) {
+        if (tree.tokens.at(token).id == .Keyword_inline or
+            tree.tokens.at(token).id == .Keyword_noinline) {
             putBackToken(it, token);
             return null;
         }
std/zig/parser_test.zig
@@ -1677,14 +1677,17 @@ test "zig fmt: functions" {
         \\extern "c" fn puts(s: *const u8) c_int;
         \\export fn puts(s: *const u8) c_int;
         \\inline fn puts(s: *const u8) c_int;
+        \\noinline fn puts(s: *const u8) c_int;
         \\pub extern fn puts(s: *const u8) c_int;
         \\pub extern "c" fn puts(s: *const u8) c_int;
         \\pub export fn puts(s: *const u8) c_int;
         \\pub inline fn puts(s: *const u8) c_int;
+        \\pub noinline fn puts(s: *const u8) c_int;
         \\pub extern fn puts(s: *const u8) align(2 + 2) c_int;
         \\pub extern "c" fn puts(s: *const u8) align(2 + 2) c_int;
         \\pub export fn puts(s: *const u8) align(2 + 2) c_int;
         \\pub inline fn puts(s: *const u8) align(2 + 2) c_int;
+        \\pub noinline fn puts(s: *const u8) align(2 + 2) c_int;
         \\
     );
 }
std/zig/tokenizer.zig
@@ -36,6 +36,7 @@ pub const Token = struct {
         Keyword{ .bytes = "for", .id = Id.Keyword_for },
         Keyword{ .bytes = "if", .id = Id.Keyword_if },
         Keyword{ .bytes = "inline", .id = Id.Keyword_inline },
+        Keyword{ .bytes = "noinline", .id = Id.Keyword_noinline },
         Keyword{ .bytes = "nakedcc", .id = Id.Keyword_nakedcc },
         Keyword{ .bytes = "noalias", .id = Id.Keyword_noalias },
         Keyword{ .bytes = "null", .id = Id.Keyword_null },
@@ -166,6 +167,7 @@ pub const Token = struct {
         Keyword_for,
         Keyword_if,
         Keyword_inline,
+        Keyword_noinline,
         Keyword_nakedcc,
         Keyword_noalias,
         Keyword_null,