Commit 46b0b84b90

Andrew Kelley <superjoe30@gmail.com>
2016-04-29 01:04:44
ability to specify body of an extern function
closes #101
1 parent a299de2
Changed files (4)
doc/langref.md
@@ -31,7 +31,7 @@ Directive = "#" "Symbol" "(" Expression ")"
 
 VisibleMod = "pub" | "export"
 
-FnDef = option("inline") FnProto Block
+FnDef = option("inline" | "extern") FnProto Block
 
 ParamDeclList = "(" list(ParamDecl, ",") ")"
 
src/analyze.cpp
@@ -994,9 +994,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
                             buf_sprintf("invalid function attribute: '%s'", buf_ptr(name)));
                 }
             } else if (buf_eql_str(name, "debug_safety")) {
-                if (fn_table_entry->is_extern) {
+                if (!fn_table_entry->fn_def_node) {
                     add_node_error(g, directive_node,
-                        buf_sprintf("#debug_safety invalid on extern functions"));
+                        buf_sprintf("#debug_safety valid only on function definitions"));
                 } else {
                     bool enable;
                     bool ok = resolve_const_expr_bool(g, import, import->block_context,
@@ -1018,9 +1018,9 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
                         buf_sprintf("#condition valid only on exported symbols"));
                 }
             } else if (buf_eql_str(name, "static_eval_enable")) {
-                if (fn_table_entry->is_extern) {
+                if (!fn_table_entry->fn_def_node) {
                     add_node_error(g, directive_node,
-                        buf_sprintf("#static_val_enable invalid on extern functions"));
+                        buf_sprintf("#static_val_enable valid only on function definitions"));
                 } else {
                     bool enable;
                     bool ok = resolve_const_expr_bool(g, import, import->block_context,
@@ -1470,9 +1470,9 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN
 
     assert(!is_extern || !is_generic_instance);
 
-    if (!is_extern && proto_node->data.fn_proto.is_var_args) {
+    if (fn_def_node && proto_node->data.fn_proto.is_var_args) {
         add_node_error(g, proto_node,
-                buf_sprintf("variadic arguments only allowed in extern functions"));
+                buf_sprintf("variadic arguments only allowed in extern function declarations"));
     }
 
     FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
@@ -1480,13 +1480,13 @@ static void preview_fn_proto_instance(CodeGen *g, ImportTableEntry *import, AstN
     fn_table_entry->proto_node = proto_node;
     fn_table_entry->fn_def_node = fn_def_node;
     fn_table_entry->is_extern = is_extern;
-    fn_table_entry->is_pure = !is_extern;
+    fn_table_entry->is_pure = fn_def_node != nullptr;
 
     get_fully_qualified_decl_name(&fn_table_entry->symbol_name, proto_node, '_');
 
     g->fn_protos.append(fn_table_entry);
 
-    if (!is_extern) {
+    if (fn_def_node) {
         g->fn_defs.append(fn_table_entry);
     }
 
src/parser.cpp
@@ -2414,27 +2414,46 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand
 }
 
 /*
-FnDef = option("inline") FnProto Block
+FnDef = option("inline" | "extern") FnProto Block
 */
 static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandatory,
         ZigList<AstNode*> *directives, VisibMod visib_mod)
 {
     Token *first_token = &pc->tokens->at(*token_index);
     bool is_inline;
+    bool is_extern;
     if (first_token->id == TokenIdKeywordInline) {
         *token_index += 1;
         is_inline = true;
+        is_extern = false;
+    } else if (first_token->id == TokenIdKeywordExtern) {
+        *token_index += 1;
+        is_extern = true;
+        is_inline = false;
     } else {
         is_inline = false;
+        is_extern = false;
     }
 
     AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, directives, visib_mod);
-    if (!fn_proto)
+    if (!fn_proto) {
+        if (is_inline || is_extern) {
+            *token_index -= 1;
+        }
         return nullptr;
-    AstNode *node = ast_create_node(pc, NodeTypeFnDef, first_token);
+    }
 
     fn_proto->data.fn_proto.is_inline = is_inline;
+    fn_proto->data.fn_proto.is_extern = is_extern;
 
+    Token *semi_token = &pc->tokens->at(*token_index);
+    if (semi_token->id == TokenIdSemicolon) {
+        *token_index += 1;
+        normalize_parent_ptrs(fn_proto);
+        return fn_proto;
+    }
+
+    AstNode *node = ast_create_node(pc, NodeTypeFnDef, first_token);
     node->data.fn_def.fn_proto = fn_proto;
     node->data.fn_def.body = ast_parse_block(pc, token_index, true);
     normalize_parent_ptrs(node);
test/run_tests.cpp
@@ -819,7 +819,7 @@ fn f() {
 
     add_compile_fail_case("variadic functions only allowed in extern", R"SOURCE(
 fn f(...) {}
-    )SOURCE", 1, ".tmp_source.zig:2:1: error: variadic arguments only allowed in extern functions");
+    )SOURCE", 1, ".tmp_source.zig:2:1: error: variadic arguments only allowed in extern function declarations");
 
     add_compile_fail_case("write to const global variable", R"SOURCE(
 const x : i32 = 99;