diff --git a/doc/langref.md b/doc/langref.md index 035b32f817..9a704d0cd0 100644 --- a/doc/langref.md +++ b/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, ",") ")" diff --git a/src/analyze.cpp b/src/analyze.cpp index cdb1e11a69..71bc19fabd 100644 --- a/src/analyze.cpp +++ b/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(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); } diff --git a/src/parser.cpp b/src/parser.cpp index 311bbe093a..603bb990b9 100644 --- a/src/parser.cpp +++ b/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 *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); diff --git a/test/run_tests.cpp b/test/run_tests.cpp index fefec2637f..40b0824092 100644 --- a/test/run_tests.cpp +++ b/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;