Commit 569525f03e

Isaac Freund <ifreund@ifreund.xyz>
2021-05-20 14:51:11
stage1: support inline keyword on function decls
This is an alternative to callconv(.Inline). Using an inline keyword as well as an explicit callconv() is a compile error.
1 parent 9910bfa
src/stage1/all_types.hpp
@@ -714,6 +714,12 @@ enum NodeType {
     NodeTypeAnyTypeField,
 };
 
+enum FnInline {
+    FnInlineAuto,
+    FnInlineAlways,
+    FnInlineNever,
+};
+
 struct AstNodeFnProto {
     Buf *name;
     ZigList<AstNode *> params;
@@ -729,12 +735,16 @@ struct AstNodeFnProto {
     AstNode *callconv_expr;
     Buf doc_comments;
 
+    // This is set based only on the existence of a noinline or inline keyword.
+    // This is then resolved to an is_noinline bool and (potentially .Inline)
+    // calling convention in resolve_decl_fn() in analyze.cpp.
+    FnInline fn_inline;
+
     VisibMod visib_mod;
     bool auto_err_set;
     bool is_var_args;
     bool is_extern;
     bool is_export;
-    bool is_noinline;
 };
 
 struct AstNodeFnDef {
src/stage1/analyze.cpp
@@ -1638,6 +1638,9 @@ CallingConvention cc_from_fn_proto(AstNodeFnProto *fn_proto) {
     if (fn_proto->is_extern || fn_proto->is_export)
         return CallingConventionC;
 
+    if (fn_proto->fn_inline == FnInlineAlways)
+        return CallingConventionInline;
+
     return CallingConventionUnspecified;
 }
 
@@ -3649,7 +3652,7 @@ ZigFn *create_fn(CodeGen *g, AstNode *proto_node) {
     assert(proto_node->type == NodeTypeFnProto);
     AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
 
-    ZigFn *fn_entry = create_fn_raw(g, fn_proto->is_noinline);
+    ZigFn *fn_entry = create_fn_raw(g, fn_proto->fn_inline == FnInlineNever);
 
     fn_entry->proto_node = proto_node;
     fn_entry->body_node = (proto_node->data.fn_proto.fn_def_node == nullptr) ? nullptr :
@@ -3742,6 +3745,9 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
 
         CallingConvention cc;
         if (fn_proto->callconv_expr != nullptr) {
+            if (fn_proto->fn_inline == FnInlineAlways) {
+                add_node_error(g, fn_proto->callconv_expr, buf_sprintf("explicit callconv incompatible with inline keyword"));
+            }
             ZigType *cc_enum_value = get_builtin_type(g, "CallingConvention");
 
             ZigValue *result_val = analyze_const_value(g, child_scope, fn_proto->callconv_expr,
src/stage1/ast_render.cpp
@@ -123,8 +123,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) {
@@ -441,7 +446,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_noinline);
+                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/stage1/parser.cpp
@@ -693,6 +693,8 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
     Token *first = eat_token_if(pc, TokenIdKeywordExport);
     if (first == nullptr)
         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) {
@@ -700,7 +702,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
         if (first->id == TokenIdKeywordExtern)
             lib_name = eat_token_if(pc, TokenIdStringLiteral);
 
-        if (first->id != TokenIdKeywordNoInline) {
+        if (first->id != TokenIdKeywordNoInline && first->id != TokenIdKeywordInline) {
             Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
             AstNode *var_decl = ast_parse_var_decl(pc);
             if (var_decl != nullptr) {
@@ -737,8 +739,17 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B
             if (!fn_proto->data.fn_proto.is_extern)
                 fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern;
             fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport;
-            if (first->id == TokenIdKeywordNoInline)
-                fn_proto->data.fn_proto.is_noinline = true;
+            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;