diff --git a/NEWS b/NEWS index 8d65180da..266589626 100644 --- a/NEWS +++ b/NEWS @@ -36,6 +36,14 @@ Changes to the Mercury language: information, see the "Impurity" chapter of the Mercury Language Reference Manual. +* We've added `:- pragma c_import_module' declarations, which are + used to make the C declarations for predicates and functions with + `:- pragma export' declarations in the imported module visible + to any C code in the importing module. `mmake' uses + `:- pragma c_import_module' declarations to make sure that the + header file for the imported module is built before it is needed, + which it can't do if the header file is explicitly #included. + * We've removed the undocumented operators `export_adt', `export_cons', `export_module', `export_op', `export_pred', `export_sym', `export_type', `import_adt', `import_cons', `import_op', `import_pred', `import_sym', diff --git a/compiler/export.m b/compiler/export.m index a2da63bbf..d5a2151e1 100644 --- a/compiler/export.m +++ b/compiler/export.m @@ -18,7 +18,7 @@ :- interface. -:- import_module prog_data, hlds_module, llds. +:- import_module prog_data, hlds_module, foreign. :- import_module io. % From the module_info, get a list of foreign_export_decls, diff --git a/compiler/foreign.m b/compiler/foreign.m index f6f12142f..accf29c6d 100644 --- a/compiler/foreign.m +++ b/compiler/foreign.m @@ -20,9 +20,48 @@ :- import_module prog_data, globals. :- import_module hlds_module, hlds_pred. -:- import_module llds. -:- import_module bool, list, string. +:- import_module bool, list, string, term. + +:- type foreign_decl_info == list(foreign_decl_code). + % in reverse order +:- type foreign_import_module_info == list(foreign_import_module). + % in reverse order +:- type foreign_body_info == list(foreign_body_code). + % in reverse order + +:- type foreign_decl_code ---> + foreign_decl_code(foreign_language, string, prog_context). +:- type foreign_import_module ---> + foreign_import_module(foreign_language, module_name, + prog_context). +:- type foreign_body_code ---> + foreign_body_code(foreign_language, string, prog_context). + +:- type foreign_export_defns == list(foreign_export). +:- type foreign_export_decls == list(foreign_export_decl). + +:- type foreign_export_decl + ---> foreign_export_decl( + foreign_language, % language of the export + string, % return type + string, % function name + string % argument declarations + ). + + % Some code from a `pragma foreign_code' declaration that is not + % associated with a given procedure. +:- type user_foreign_code + ---> user_foreign_code( + foreign_language, % language of this code + string, % code + term__context % source code location + ). + + % the code for `pragma export' is generated directly as strings + % by export.m. +:- type foreign_export == string. + % A type which is used to determine the string representation of a % mercury type for various foreign languages. :- type exported_type. @@ -47,6 +86,13 @@ foreign_decl_info, foreign_decl_info). :- mode foreign__filter_decls(in, in, out, out) is det. + % Filter the module imports for the given foreign language. + % The first return value is the list of matches, the second is + % the list of mis-matches. +:- pred foreign__filter_imports(foreign_language, foreign_import_module_info, + foreign_import_module_info, foreign_import_module_info). +:- mode foreign__filter_imports(in, in, out, out) is det. + % Filter the bodys for the given foreign language. % The first return value is the list of matches, the second is % the list of mis-matches. @@ -179,6 +225,12 @@ foreign__filter_decls(WantedLang, Decls0, LangDecls, NotLangDecls) :- WantedLang = Lang), Decls0, LangDecls, NotLangDecls). +foreign__filter_imports(WantedLang, Imports0, LangImports, NotLangImports) :- + list__filter( + (pred(foreign_import_module(Lang, _, _)::in) is semidet :- + WantedLang = Lang), + Imports0, LangImports, NotLangImports). + foreign__filter_bodys(WantedLang, Bodys0, LangBodys, NotLangBodys) :- list__filter((pred(foreign_body_code(Lang, _, _)::in) is semidet :- WantedLang = Lang), diff --git a/compiler/hlds_module.m b/compiler/hlds_module.m index 836b5a15c..b69da2d2b 100644 --- a/compiler/hlds_module.m +++ b/compiler/hlds_module.m @@ -23,7 +23,7 @@ :- import_module prog_data, module_qual, recompilation. :- import_module hlds_pred, hlds_data, unify_proc, special_pred. -:- import_module globals, llds. +:- import_module globals, foreign. :- import_module relation, map, std_util, list, set, multi_map, counter. :- implementation. @@ -270,13 +270,26 @@ :- pred module_info_get_foreign_body_code(module_info, foreign_body_info). :- mode module_info_get_foreign_body_code(in, out) is det. -:- pred module_info_set_foreign_body_code(module_info, foreign_body_info, module_info). +:- pred module_info_set_foreign_body_code(module_info, + foreign_body_info, module_info). :- mode module_info_set_foreign_body_code(in, in, out) is det. +:- pred module_info_get_foreign_import_module(module_info, + foreign_import_module_info). +:- mode module_info_get_foreign_import_module(in, out) is det. + +:- pred module_info_set_foreign_import_module(module_info, + foreign_import_module_info, module_info). +:- mode module_info_set_foreign_import_module(in, in, out) is det. + :- pred module_add_foreign_decl(foreign_language, string, prog_context, module_info, module_info). :- mode module_add_foreign_decl(in, in, in, in, out) is det. +:- pred module_add_foreign_import_module(foreign_language, + module_name, prog_context, module_info, module_info). +:- mode module_add_foreign_import_module(in, in, in, in, out) is det. + :- pred module_add_foreign_body_code(foreign_language, string, prog_context, module_info, module_info). :- mode module_add_foreign_body_code(in, in, in, in, out) is det. @@ -508,6 +521,7 @@ globals :: globals, foreign_decl_info :: foreign_decl_info, foreign_body_info :: foreign_body_info, + foreign_import_module_info :: foreign_import_module_info, % This dependency info is constrained to be only % for between procedures which have clauses @@ -586,7 +600,7 @@ module_info_init(Name, Items, Globals, QualifierInfo, RecompInfo, map__init(FieldNameTable), map__init(NoTagTypes), - ModuleSubInfo = module_sub(Name, Globals, [], [], no, 0, 0, [], + ModuleSubInfo = module_sub(Name, Globals, [], [], [], no, 0, 0, [], [], StratPreds, UnusedArgInfo, 0, ImportedModules, IndirectlyImportedModules, no_aditi_compilation, TypeSpecInfo, NoTagTypes), @@ -646,6 +660,8 @@ module_info_name(MI, MI ^ sub_info ^ module_name). module_info_globals(MI, MI ^ sub_info ^ globals). module_info_get_foreign_decl(MI, MI ^ sub_info ^ foreign_decl_info). module_info_get_foreign_body_code(MI, MI ^ sub_info ^ foreign_body_info). +module_info_get_foreign_import_module(MI, + MI ^ sub_info ^ foreign_import_module_info). module_info_get_maybe_dependency_info(MI, MI ^ sub_info ^ maybe_dependency_info). module_info_num_errors(MI, MI ^ sub_info ^ num_errors). @@ -677,6 +693,8 @@ module_info_set_foreign_decl(MI, NewVal, MI ^ sub_info ^ foreign_decl_info := NewVal). module_info_set_foreign_body_code(MI, NewVal, MI ^ sub_info ^ foreign_body_info := NewVal). +module_info_set_foreign_import_module(MI, NewVal, + MI ^ sub_info ^ foreign_import_module_info := NewVal). module_info_set_maybe_dependency_info(MI, NewVal, MI ^ sub_info ^ maybe_dependency_info := NewVal). module_info_set_num_errors(MI, NewVal, @@ -891,6 +909,16 @@ module_add_foreign_decl(Lang, ForeignDecl, Context, Module0, Module) :- ForeignDeclIndex0], module_info_set_foreign_decl(Module0, ForeignDeclIndex1, Module). +module_add_foreign_import_module(Lang, ModuleName, Context, Module0, Module) :- + module_info_get_foreign_import_module(Module0, ForeignImportIndex0), + % store the decls in reverse order and reverse them later + % for efficiency + ForeignImportIndex1 = + [foreign_import_module(Lang, ModuleName, Context) | + ForeignImportIndex0], + module_info_set_foreign_import_module(Module0, + ForeignImportIndex1, Module). + module_add_foreign_body_code(Lang, Foreign_Body_Code, Context, Module0, Module) :- module_info_get_foreign_body_code(Module0, Foreign_Body_List0), diff --git a/compiler/intermod.m b/compiler/intermod.m index 16e0ffdbb..a101d17af 100644 --- a/compiler/intermod.m +++ b/compiler/intermod.m @@ -90,7 +90,7 @@ :- import_module hlds_data, hlds_goal, hlds_pred, hlds_out, inlining, llds. :- import_module mercury_to_mercury, mode_util, modules. :- import_module options, passes_aux, prog_data, prog_io, prog_out, prog_util. -:- import_module special_pred, typecheck, type_util, instmap, (inst). +:- import_module special_pred, typecheck, type_util, instmap, (inst), foreign. %-----------------------------------------------------------------------------% @@ -1115,8 +1115,50 @@ intermod__write_intermod_info_2(IntermodInfo) --> globals__io_lookup_string_option(dump_hlds_options, VerboseDump), globals__io_set_option(dump_hlds_options, string("")), ( { WriteHeader = yes } -> - { module_info_get_foreign_decl(ModuleInfo, ForeignDecl) }, - intermod__write_foreign_decl(ForeignDecl) + { module_info_get_foreign_decl(ModuleInfo, RevForeignDecls) }, + { module_info_get_foreign_import_module(ModuleInfo, + RevForeignImports) }, + { module_info_get_pragma_exported_procs(ModuleInfo, + PragmaExportedProcs) }, + { ForeignDecls = list__reverse(RevForeignDecls) }, + { ForeignImports0 = list__reverse(RevForeignImports) }, + + % + % If this module contains `:- pragma export' declarations, + % they may be referred to by the C code we are writing + % to the `.opt' file, so write the implicit + % `:- pragma foreign_import_module("C", ModuleName).' + % to the `.opt' file. + % + % XXX We should do this, but mmake can't handle + % the extra dependencies properly yet, so building + % the standard library fails (mmake attempts to build + % tree234.o before std_util.h is built). + % + { semidet_fail, PragmaExportedProcs \= [] -> + % XXX Currently we only handle procedures + % exported to C. + module_info_name(ModuleInfo, ModuleName), + ForeignImportThisModule = foreign_import_module(c, + ModuleName, term__context_init), + ForeignImports = + [ForeignImportThisModule | ForeignImports0] + ; + ForeignImports = ForeignImports0 + }, + list__foldl( + (pred(ForeignImport::in, di, uo) is det --> + { ForeignImport = foreign_import_module(Lang, + Import, _) }, + mercury_output_pragma_foreign_import_module(Lang, + Import) + ), ForeignImports), + + list__foldl( + (pred(ForeignDecl::in, di, uo) is det --> + { ForeignDecl = foreign_decl_code(Lang, Header, _) }, + mercury_output_pragma_foreign_decl(Lang, Header) + ), ForeignDecls) ; [] ), @@ -1139,15 +1181,6 @@ intermod__write_modules([Module | Rest]) --> intermod__write_modules(Rest) ). -:- pred intermod__write_foreign_decl(list(foreign_decl_code)::in, - io__state::di, io__state::uo) is det. - -intermod__write_foreign_decl([]) --> []. -intermod__write_foreign_decl( - [foreign_decl_code(Language, Header, _) | Headers]) --> - intermod__write_foreign_decl(Headers), - mercury_output_pragma_foreign_decl(Language, Header). - :- pred intermod__write_types(assoc_list(type_id, hlds_type_defn)::in, io__state::di, io__state::uo) is det. @@ -2013,7 +2046,7 @@ intermod__grab_optfiles(Module0, Module, FoundError) --> % Read in the .opt files for imported and ancestor modules. % { Module0 = module_imports(_, ModuleName, Ancestors0, InterfaceDeps0, - ImplementationDeps0, _, _, _, _, _, _, _) }, + ImplementationDeps0, _, _, _, _, _, _, _, _) }, { list__condense([Ancestors0, InterfaceDeps0, ImplementationDeps0], OptFiles) }, read_optimization_interfaces(OptFiles, [], OptItems, no, OptError), diff --git a/compiler/llds.m b/compiler/llds.m index d7d3c97a4..2f772fad4 100644 --- a/compiler/llds.m +++ b/compiler/llds.m @@ -18,7 +18,7 @@ :- import_module prog_data, (inst). :- import_module hlds_pred, hlds_goal, hlds_data. -:- import_module code_model, rtti, layout, builtin_ops. +:- import_module foreign, code_model, rtti, layout, builtin_ops. :- import_module tree. :- import_module bool, assoc_list, list, map, set, std_util, counter, term. @@ -32,37 +32,13 @@ module_name, % info about stuff imported from C: foreign_decl_info, + foreign_import_module_info, foreign_body_info, % info about stuff exported to C: foreign_export_decls, foreign_export_defns ). -:- type foreign_decl_info == list(foreign_decl_code). - % in reverse order -:- type foreign_body_info == list(foreign_body_code). - % in reverse order - -:- type foreign_decl_code ---> - foreign_decl_code(foreign_language, string, prog_context). -:- type foreign_body_code ---> - foreign_body_code(foreign_language, string, prog_context). - -:- type foreign_export_defns == list(foreign_export). -:- type foreign_export_decls == list(foreign_export_decl). - -:- type foreign_export_decl - ---> foreign_export_decl( - foreign_language, % language of the export - string, % return type - string, % function name - string % argument declarations - ). - - % the code for `pragma export' is generated directly as strings - % by export.m. -:- type foreign_export == string. - %-----------------------------------------------------------------------------% :- import_module continuation_info. @@ -120,15 +96,6 @@ list(comp_gen_c_module) ). - % Some code from a `pragma foreign_code' declaration that is not - % associated with a given procedure. -:- type user_foreign_code - ---> user_foreign_code( - foreign_language, % language of this code - string, % code - term__context % source code location - ). - % Global variables generated by the compiler. :- type comp_gen_c_var ---> tabling_pointer_var( diff --git a/compiler/llds_out.m b/compiler/llds_out.m index 324ac2393..f77e1f752 100644 --- a/compiler/llds_out.m +++ b/compiler/llds_out.m @@ -259,7 +259,7 @@ :- import_module rtti, rtti_out, layout, layout_out, options, trace_params. :- import_module exprn_aux, prog_util, prog_out, hlds_pred. :- import_module export, mercury_to_mercury, modules, passes_aux. -:- import_module c_util. +:- import_module c_util, foreign. :- import_module int, char, string, std_util. :- import_module set, bintree_set, assoc_list, require. diff --git a/compiler/make_hlds.m b/compiler/make_hlds.m index 665b887a6..539d0a0c3 100644 --- a/compiler/make_hlds.m +++ b/compiler/make_hlds.m @@ -385,6 +385,10 @@ add_item_decl_pass_2(pragma(Pragma), Context, Status, Module0, Status, Module) { Pragma = foreign_decl(Lang, C_Header) }, { module_add_foreign_decl(Lang, C_Header, Context, Module0, Module) } + ; + { Pragma = foreign_import_module(Lang, Import) }, + { module_add_foreign_import_module(Lang, Import, Context, + Module0, Module) } ; % Handle pragma foreign procs later on (when we process % clauses). diff --git a/compiler/mercury_compile.m b/compiler/mercury_compile.m index 91d53b4f6..edf67b55a 100644 --- a/compiler/mercury_compile.m +++ b/compiler/mercury_compile.m @@ -1300,7 +1300,8 @@ mercury_compile__maybe_grab_optfiles(Imports0, Verbose, MaybeTransOptDeps, { Imports0 = module_imports(_File, _Module, Ancestors, InterfaceImports, ImplementationImports, _IndirectImports, _PublicChildren, _FactDeps, - _ForeignCode, _Items, _Error, _Timestamps) }, + _ForeignCode, _ForeignImports, _Items, + _Error, _Timestamps) }, { list__condense([Ancestors, InterfaceImports, ImplementationImports], TransOptFiles) }, trans_opt__grab_optfiles(Imports1, TransOptFiles, @@ -2954,16 +2955,34 @@ mercury_compile__maybe_generate_stack_layouts(ModuleInfo0, GlobalData0, LLDS0, get_c_interface_info(HLDS, UseForeignLanguage, Foreign_InterfaceInfo) :- module_info_name(HLDS, ModuleName), module_info_get_foreign_decl(HLDS, ForeignDecls), + module_info_get_foreign_import_module(HLDS, ForeignImports), module_info_get_foreign_body_code(HLDS, ForeignBodyCode), foreign__filter_decls(UseForeignLanguage, ForeignDecls, WantedForeignDecls, _OtherDecls), + foreign__filter_imports(UseForeignLanguage, ForeignImports, + WantedForeignImports0, _OtherImports), foreign__filter_bodys(UseForeignLanguage, ForeignBodyCode, WantedForeignBodys, _OtherBodys), export__get_foreign_export_decls(HLDS, Foreign_ExportDecls), export__get_foreign_export_defns(HLDS, Foreign_ExportDefns), + + % If this module contains `:- pragma export' declarations, + % add a "#include .h" declaration. + % XXX pragma export is only supported for C. + ( UseForeignLanguage = c, Foreign_ExportDecls \= [] -> + % We put the new include at the end since the list is + % stored in reverse, and we want this include to come + % first. + Import = foreign_import_module(c, ModuleName, + term__context_init), + WantedForeignImports = WantedForeignImports0 ++ [Import] + ; + WantedForeignImports = WantedForeignImports0 + ), + Foreign_InterfaceInfo = foreign_interface_info(ModuleName, - WantedForeignDecls, WantedForeignBodys, - Foreign_ExportDecls, Foreign_ExportDefns). + WantedForeignDecls, WantedForeignImports, + WantedForeignBodys, Foreign_ExportDecls, Foreign_ExportDefns). %-----------------------------------------------------------------------------% %-----------------------------------------------------------------------------% @@ -3025,7 +3044,7 @@ mercury_compile__output_pass(HLDS0, GlobalData, Procs0, MaybeRLFile, mercury_compile__output_llds(ModuleName, CFile, LayoutLabels, MaybeRLFile, Verbose, Stats), - { C_InterfaceInfo = foreign_interface_info(_, _, _, C_ExportDecls, _) }, + { C_InterfaceInfo = foreign_interface_info(_, _, _, _, C_ExportDecls, _) }, export__produce_header_file(C_ExportDecls, ModuleName), % @@ -3050,7 +3069,8 @@ mercury_compile__output_pass(HLDS0, GlobalData, Procs0, MaybeRLFile, mercury_compile__construct_c_file(C_InterfaceInfo, Procedures, GlobalVars, AllData, CFile, ComponentCount) --> { C_InterfaceInfo = foreign_interface_info(ModuleSymName, - C_HeaderCode0, C_BodyCode0, C_ExportDecls, C_ExportDefns) }, + C_HeaderCode0, C_Includes, C_BodyCode0, + _C_ExportDecls, C_ExportDefns) }, { llds_out__sym_name_mangle(ModuleSymName, MangledModuleName) }, { string__append(MangledModuleName, "_module", ModuleName) }, globals__io_lookup_int_option(procs_per_c_function, ProcsPerFunc), @@ -3065,8 +3085,9 @@ mercury_compile__construct_c_file(C_InterfaceInfo, Procedures, GlobalVars, { mercury_compile__combine_chunks(ChunkedProcs, ModuleName, ChunkedModules) } ), - maybe_add_header_file_include(C_ExportDecls, ModuleSymName, - C_HeaderCode0, C_HeaderCode), + list__map_foldl(make_foreign_import_header_code, C_Includes, + C_HeaderCode1), + { C_HeaderCode = C_HeaderCode0 ++ C_HeaderCode1 }, { CFile = c_file(ModuleSymName, C_HeaderCode, C_BodyCode, C_ExportDefns, GlobalVars, AllData, ChunkedModules) }, { list__length(C_BodyCode, UserCCodeCount) }, @@ -3077,29 +3098,29 @@ mercury_compile__construct_c_file(C_InterfaceInfo, Procedures, GlobalVars, { ComponentCount is UserCCodeCount + ExportCount + CompGenVarCount + CompGenDataCount + CompGenCodeCount }. -:- pred maybe_add_header_file_include(foreign_export_decls, module_name, - foreign_decl_info, foreign_decl_info, io__state, io__state). -:- mode maybe_add_header_file_include(in, in, in, out, di, uo) is det. +:- pred make_foreign_import_header_code(foreign_import_module, + foreign_decl_code, io__state, io__state). +:- mode make_foreign_import_header_code(in, out, di, uo) is det. -maybe_add_header_file_include(C_ExportDecls, ModuleName, - C_HeaderCode0, C_HeaderCode) --> +make_foreign_import_header_code( + foreign_import_module(Lang, ModuleName, Context), + Include) --> ( - { C_ExportDecls = [] }, - { C_HeaderCode = C_HeaderCode0 } - ; - { C_ExportDecls = [_|_] }, + { Lang = c }, module_name_to_file_name(ModuleName, ".h", no, HeaderFileName), { string__append_list( ["#include """, HeaderFileName, """\n"], IncludeString) }, - - { term__context_init(Context) }, - { Include = foreign_decl_code(c, IncludeString, Context) }, - - % We put the new include at the end since the list is - % stored in reverse, and we want this include to come - % first. - { list__append(C_HeaderCode0, [Include], C_HeaderCode) } + { Include = foreign_decl_code(c, IncludeString, Context) } + ; + { Lang = csharp }, + { error("sorry, not yet implemented: `:- pragma foreign_import_module' for C#") } + ; + { Lang = managed_cplusplus }, + { error("sorry, not yet implemented: `:- pragma foreign_import_module' for Managed C++") } + ; + { Lang = il }, + { error("sorry, not yet implemented: `:- pragma foreign_import_module' for IL") } ). :- pred get_c_body_code(foreign_body_info, list(user_foreign_code)). diff --git a/compiler/mercury_to_mercury.m b/compiler/mercury_to_mercury.m index dafab5c41..61f7af43d 100644 --- a/compiler/mercury_to_mercury.m +++ b/compiler/mercury_to_mercury.m @@ -171,6 +171,10 @@ :- func mercury_pragma_foreign_decl_to_string(foreign_language, string) = string. +:- pred mercury_output_pragma_foreign_import_module(foreign_language, + module_name, io__state, io__state). +:- mode mercury_output_pragma_foreign_import_module(in, in, di, uo) is det. + :- pred mercury_output_ctor(constructor, tvarset, io__state, io__state). :- mode mercury_output_ctor(in, in, di, uo) is det. @@ -450,6 +454,9 @@ mercury_output_item(pragma(Pragma), Context) --> ; { Pragma = foreign_decl(Lang, ForeignHeaderString) }, mercury_output_pragma_foreign_decl(Lang, ForeignHeaderString) + ; + { Pragma = foreign_import_module(Lang, ModuleName) }, + mercury_output_pragma_foreign_import_module(Lang, ModuleName) ; { Pragma = foreign_code(Lang, Code) }, mercury_output_pragma_foreign_body_code(Lang, Code) @@ -2391,6 +2398,14 @@ mercury_format_pragma_foreign_decl(Lang, ForeignDeclString) --> mercury_format_foreign_language_string(Lang) --> add_string("""" ++ foreign_language_string(Lang) ++ """"). +mercury_output_pragma_foreign_import_module(Lang, ModuleName) --> + io__write_string(":- pragma foreign_import_module("), + mercury_format_foreign_language_string(Lang), + io__write_string(", "), + mercury_output_bracketed_sym_name(ModuleName, + not_next_to_graphic_token), + io__write_string(").\n"). + %-----------------------------------------------------------------------------% % The code here is similar to the code for term_io__quote_string, diff --git a/compiler/ml_code_gen.m b/compiler/ml_code_gen.m index 3366abc98..93d0cb3a9 100644 --- a/compiler/ml_code_gen.m +++ b/compiler/ml_code_gen.m @@ -805,6 +805,7 @@ ml_code_gen(ModuleInfo, MLDS) --> ml_gen_foreign_code(ModuleInfo, All_MLDS_ForeignCode) --> { module_info_get_foreign_decl(ModuleInfo, ForeignDecls) }, + { module_info_get_foreign_import_module(ModuleInfo, ForeignImports) }, { module_info_get_foreign_body_code(ModuleInfo, ForeignBodys) }, globals__io_get_backend_foreign_languages(BackendForeignLanguages), @@ -812,6 +813,9 @@ ml_gen_foreign_code(ModuleInfo, All_MLDS_ForeignCode) --> foreign__filter_decls(Lang, ForeignDecls, WantedForeignDecls, _OtherForeignDecls), + foreign__filter_imports(Lang, + ForeignImports, WantedForeignImports, + _OtherForeignImports), foreign__filter_bodys(Lang, ForeignBodys, WantedForeignBodys, _OtherForeignBodys), @@ -828,8 +832,8 @@ ml_gen_foreign_code(ModuleInfo, All_MLDS_ForeignCode) --> MLDS_PragmaExports = [] ), MLDS_ForeignCode = mlds__foreign_code( - WantedForeignDecls, MLDSWantedForeignBodys, - MLDS_PragmaExports), + WantedForeignDecls, WantedForeignImports, + MLDSWantedForeignBodys, MLDS_PragmaExports), map__det_insert(Map0, Lang, MLDS_ForeignCode, Map) ), BackendForeignLanguages, map__init, All_MLDS_ForeignCode) }. diff --git a/compiler/mlds.m b/compiler/mlds.m index 956058301..41c58f645 100644 --- a/compiler/mlds.m +++ b/compiler/mlds.m @@ -291,11 +291,6 @@ :- import_module prog_data, builtin_ops, rtti, code_model. :- import_module foreign, type_util. -% To avoid duplication, we use a few things from the LLDS -% (specifically stuff for the C interface). -% It would be nice to avoid this dependency... -:- import_module llds. - :- import_module bool, list, assoc_list, std_util, map. %-----------------------------------------------------------------------------% @@ -759,6 +754,7 @@ :- type mlds__foreign_code ---> mlds__foreign_code( foreign_decl_info, + foreign_import_module_info, list(user_foreign_code), list(mlds__pragma_export) ). diff --git a/compiler/mlds_to_c.m b/compiler/mlds_to_c.m index f83c54d50..8fdef0c31 100644 --- a/compiler/mlds_to_c.m +++ b/compiler/mlds_to_c.m @@ -49,7 +49,6 @@ :- implementation. :- import_module ml_util. -:- import_module llds. % XXX needed for C interface types :- import_module llds_out. % XXX needed for llds_out__name_mangle, % llds_out__sym_name_mangle, % llds_out__make_base_typeclass_info_name, @@ -387,7 +386,7 @@ mlds_get_c_foreign_code(AllForeignCode) = ForeignCode :- ; % this can occur when compiling to a non-C target % using "--mlds-dump all" - ForeignCode = foreign_code([], [], []) + ForeignCode = foreign_code([], [], [], []) ). %-----------------------------------------------------------------------------% @@ -523,8 +522,8 @@ mlds_output_calls_to_register_tci(ModuleName, :- mode mlds_output_c_hdr_decls(in, in, in, di, uo) is det. mlds_output_c_hdr_decls(ModuleName, Indent, ForeignCode) --> - { ForeignCode = mlds__foreign_code(RevHeaderCode, _RevBodyCode, - ExportDefns) }, + { ForeignCode = mlds__foreign_code(RevHeaderCode, _RevImports, + _RevBodyCode, ExportDefns) }, { HeaderCode = list__reverse(RevHeaderCode) }, io__write_list(HeaderCode, "\n", mlds_output_c_hdr_decl(Indent)), io__write_string("\n"), @@ -556,8 +555,20 @@ mlds_output_c_decls(_, _) --> []. :- mode mlds_output_c_defns(in, in, in, di, uo) is det. mlds_output_c_defns(ModuleName, Indent, ForeignCode) --> - { ForeignCode = mlds__foreign_code(_RevHeaderCode, RevBodyCode, - ExportDefns) }, + { ForeignCode = mlds__foreign_code(_RevHeaderCode, RevImports, + RevBodyCode, ExportDefns) }, + { Imports = list__reverse(RevImports) }, + list__foldl( + (pred(ForeignImport::in, di, uo) is det --> + { ForeignImport = foreign_import_module(Lang, Import, _) }, + ( { Lang = c } -> + mlds_output_src_import(Indent, + mercury_import( + mercury_module_name_to_mlds(Import))) + ; + { sorry(this_file, "foreign code other than C") } + ) + ), Imports), { BodyCode = list__reverse(RevBodyCode) }, io__write_list(BodyCode, "\n", mlds_output_c_defn(Indent)), io__write_string("\n"), diff --git a/compiler/mlds_to_csharp.m b/compiler/mlds_to_csharp.m index 3eb81b216..13c84fd01 100644 --- a/compiler/mlds_to_csharp.m +++ b/compiler/mlds_to_csharp.m @@ -29,11 +29,10 @@ :- import_module builtin_ops, c_util, modules, tree. :- import_module hlds_pred. % for `pred_proc_id'. :- import_module prog_data, prog_out. -:- import_module rtti, type_util, error_util. +:- import_module foreign, rtti, type_util, error_util. :- import_module ilds, ilasm, il_peephole. :- import_module ml_util, ml_code_util. -:- use_module llds. /* for user_c_code */ :- import_module bool, int, map, string, list, assoc_list, term, std_util. :- import_module library, require, counter. @@ -153,16 +152,17 @@ generate_csharp_code(MLDS) --> io__nl. - % XXX we don't handle export decls. + % XXX we don't handle export decls or + % `:- pragma foreign_import_module'. :- pred generate_foreign_code(mlds_module_name, mlds__foreign_code, io__state, io__state). :- mode generate_foreign_code(in, in, di, uo) is det. generate_foreign_code(_ModuleName, - mlds__foreign_code(_RevHeaderCode, RevBodyCode, + mlds__foreign_code(_RevHeaderCode, _RevImports, RevBodyCode, _ExportDefns)) --> { BodyCode = list__reverse(RevBodyCode) }, io__write_list(BodyCode, "\n", - (pred(llds__user_foreign_code(Lang, Code, _Context)::in, + (pred(user_foreign_code(Lang, Code, _Context)::in, di, uo) is det --> ( { Lang = csharp } -> io__write_string(Code) @@ -172,16 +172,17 @@ generate_foreign_code(_ModuleName, ) )). - % XXX we don't handle export decls. + % XXX we don't handle export decls or + % `:- pragma foreign_import_module'. :- pred generate_foreign_header_code(mlds_module_name, mlds__foreign_code, io__state, io__state). :- mode generate_foreign_header_code(in, in, di, uo) is det. generate_foreign_header_code(_ModuleName, - mlds__foreign_code(RevHeaderCode, _RevBodyCode, + mlds__foreign_code(RevHeaderCode, _RevImports, _RevBodyCode, _ExportDefns)) --> { HeaderCode = list__reverse(RevHeaderCode) }, io__write_list(HeaderCode, "\n", - (pred(llds__foreign_decl_code(Lang, Code, _Context)::in, + (pred(foreign_decl_code(Lang, Code, _Context)::in, di, uo) is det --> ( { Lang = csharp } -> io__write_string(Code) diff --git a/compiler/mlds_to_gcc.m b/compiler/mlds_to_gcc.m index 81f8dfd45..0ce88a850 100644 --- a/compiler/mlds_to_gcc.m +++ b/compiler/mlds_to_gcc.m @@ -250,7 +250,7 @@ mlds_to_gcc__compile_to_asm(MLDS, ContainsCCode) --> % that were defined in other modules, but to call mlds_to_c % for foreign_decls that were defined in the module that % we're compiling. - { ForeignCode = mlds__foreign_code(_Decls, [], []) }, + { ForeignCode = mlds__foreign_code(_Decls, _Imports, [], []) }, { ForeignDefns = [] } -> { ContainsCCode = no }, diff --git a/compiler/mlds_to_il.m b/compiler/mlds_to_il.m index 24f21c1c2..f2a683c1f 100644 --- a/compiler/mlds_to_il.m +++ b/compiler/mlds_to_il.m @@ -283,7 +283,7 @@ get_il_data_rep(ILDataRep, IO0, IO) :- transform_mlds(MLDS0) = MLDS :- AllExports = list__condense( list__map( - (func(mlds__foreign_code(_, _, Exports)) = Exports), + (func(mlds__foreign_code(_, _, _, Exports)) = Exports), map__values(MLDS0 ^ foreign_code)) ), diff --git a/compiler/mlds_to_mcpp.m b/compiler/mlds_to_mcpp.m index 50916f80b..5b5d660be 100644 --- a/compiler/mlds_to_mcpp.m +++ b/compiler/mlds_to_mcpp.m @@ -44,7 +44,7 @@ :- import_module builtin_ops, c_util, modules, tree. :- import_module hlds_pred. % for `pred_proc_id'. :- import_module prog_data, prog_out, llds_out. -:- import_module rtti, type_util, error_util. +:- import_module foreign, rtti, type_util, error_util. :- import_module ilds, ilasm, il_peephole. :- import_module ml_util, ml_code_util. @@ -172,11 +172,11 @@ generate_mcplusplus_code(MLDS) --> io__state, io__state). :- mode generate_foreign_code(in, in, di, uo) is det. generate_foreign_code(_ModuleName, - mlds__foreign_code(_RevHeaderCode, RevBodyCode, + mlds__foreign_code(_RevHeaderCode, _RevImports, RevBodyCode, _ExportDefns)) --> { BodyCode = list__reverse(RevBodyCode) }, io__write_list(BodyCode, "\n", - (pred(llds__user_foreign_code(Lang, Code, Context)::in, + (pred(user_foreign_code(Lang, Code, Context)::in, di, uo) is det --> ( { Lang = managed_cplusplus } -> mlds_to_c__output_context(mlds__make_context( @@ -193,11 +193,11 @@ generate_foreign_code(_ModuleName, io__state, io__state). :- mode generate_foreign_header_code(in, in, di, uo) is det. generate_foreign_header_code(_ModuleName, - mlds__foreign_code(RevHeaderCode, _RevBodyCode, + mlds__foreign_code(RevHeaderCode, _RevImports, _RevBodyCode, _ExportDefns)) --> { HeaderCode = list__reverse(RevHeaderCode) }, io__write_list(HeaderCode, "\n", - (pred(llds__foreign_decl_code(Lang, Code, _Context)::in, + (pred(foreign_decl_code(Lang, Code, _Context)::in, di, uo) is det --> ( { Lang = managed_cplusplus } -> io__write_string(Code) diff --git a/compiler/module_qual.m b/compiler/module_qual.m index dac54b187..80e3b0fdf 100644 --- a/compiler/module_qual.m +++ b/compiler/module_qual.m @@ -893,6 +893,8 @@ qualify_type(Type0, Type, Info0, Info) --> qualify_pragma(source_file(File), source_file(File), Info, Info) --> []. qualify_pragma(foreign_decl(L, Code), foreign_decl(L, Code), Info, Info) --> []. qualify_pragma(foreign_code(L, C), foreign_code(L, C), Info, Info) --> []. +qualify_pragma(foreign_import_module(L, M), foreign_import_module(L, M), + Info, Info) --> []. qualify_pragma(foreign_type(Backend, Type0, SymName, F), foreign_type(Backend, Type, SymName, F), Info0, Info) --> qualify_type(Type0, Type, Info0, Info). diff --git a/compiler/modules.m b/compiler/modules.m index e4a539c3f..3490067ec 100644 --- a/compiler/modules.m +++ b/compiler/modules.m @@ -39,7 +39,7 @@ :- interface. -:- import_module prog_data, prog_io, globals, timestamp. +:- import_module foreign, prog_data, prog_io, globals, timestamp. :- import_module std_util, bool, list, map, set, io. %-----------------------------------------------------------------------------% @@ -277,6 +277,9 @@ foreign_code :: contains_foreign_code, % Whether or not the module contains % foreign code (and which languages if it does) + foreign_import_module_info :: foreign_import_module_info, + % The `:- pragma foreign_import_module' + % declarations. items :: item_list, % The contents of the module and its imports error :: module_error, @@ -1139,6 +1142,7 @@ split_clauses_and_decls([ItemAndContext0 | Items0], % header file, which currently we don't. pragma_allowed_in_interface(foreign_decl(_, _), no). +pragma_allowed_in_interface(foreign_import_module(_, _), no). pragma_allowed_in_interface(foreign_code(_, _), no). pragma_allowed_in_interface(foreign_proc(_, _, _, _, _, _), no). pragma_allowed_in_interface(foreign_type(_, _, _, _), yes). @@ -1555,7 +1559,7 @@ find_read_module(ReadModules, ModuleName, Suffix, ReturnTimestamp, init_module_imports(SourceFileName, ModuleName, Items, PublicChildren, FactDeps, MaybeTimestamps, Module) :- Module = module_imports(SourceFileName, ModuleName, [], [], [], [], - PublicChildren, FactDeps, unknown, Items, no_module_errors, + PublicChildren, FactDeps, unknown, [], Items, no_module_errors, MaybeTimestamps). module_imports_get_source_file_name(Module, Module ^ source_file_name). @@ -1785,7 +1789,8 @@ warn_if_duplicate_use_import_decls(ModuleName, write_dependency_file(Module, AllDepsSet, MaybeTransOptDeps) --> { Module = module_imports(SourceFileName, ModuleName, ParentDeps, IntDeps, ImplDeps, IndirectDeps, _InclDeps, FactDeps0, - ContainsForeignCode, Items, _Error, _Timestamps) }, + ContainsForeignCode, ForeignImports0, + Items, _Error, _Timestamps) }, globals__io_lookup_bool_option(verbose, Verbose), { module_name_to_make_var_name(ModuleName, MakeVarName) }, module_name_to_file_name(ModuleName, ".d", yes, DependencyFileName), @@ -2006,26 +2011,36 @@ write_dependency_file(Module, AllDepsSet, MaybeTransOptDeps) --> ObjFileName, " ", SplitObjPattern, " :" ]), - write_dependencies_list(AllDeps, ".h", DepStream), - - % - % We also need to tell make how to make the header - % files. The header files are actually built by - % the same command that creates the .c files, so - % we just make them depend on the .c files. - % - module_name_to_file_name(ModuleName, ".c", no, - CFileName), - module_name_to_file_name(ModuleName, ".h", no, - HeaderFileName), - io__write_strings(DepStream, [ - "\n\n", HeaderFileName, - " : ", CFileName - ]) + write_dependencies_list(AllDeps, ".h", DepStream) ; [] ), + % + % We need to tell make how to make the header + % files. The header files are actually built by + % the same command that creates the .c or .s file, + % so we just make them depend on the .c or .s files. + % This is needed for the --high-level-code rule above, + % and for the rules introduced for + % `:- pragma foreign_import_module' declarations. + % In some grades the header file won't actually be built + % (e.g. LLDS grades for modules not containing + % `:- pragma export' declarations), but this + % rule won't do any harm. + % + module_name_to_file_name(ModuleName, ".c", no, CFileName), + module_name_to_file_name(ModuleName, ".s", no, AsmFileName), + module_name_to_file_name(ModuleName, ".h", no, HeaderFileName), + io__write_strings(DepStream, [ + "\n\n", + "ifeq ($(TARGET_ASM),yes)\n", + HeaderFileName, " : ", AsmFileName, "\n", + "else\n", + HeaderFileName, " : ", CFileName, "\n", + "endif" + ]), + module_name_to_file_name(ModuleName, ".date", no, DateFileName), module_name_to_file_name(ModuleName, ".date0", no, @@ -2072,12 +2087,36 @@ write_dependency_file(Module, AllDepsSet, MaybeTransOptDeps) --> [] ), - { ContainsForeignCode = contains_foreign_code(LangSet) + { ContainsForeignCode = contains_foreign_code(LangSet), + ForeignImports = ForeignImports0 ; ContainsForeignCode = unknown, - get_item_list_foreign_code(Globals, Items, LangSet) + get_item_list_foreign_code(Globals, Items, + LangSet, ForeignImports) ; ContainsForeignCode = no_foreign_code, - set__init(LangSet) + set__init(LangSet), + ForeignImports = ForeignImports0 }, + + % + % Handle dependencies introduced by + % `:- pragma foreign_import_module' declarations. + % + { ForeignImportedModules = + list__map( + (func(foreign_import_module(_, ForeignImportModule, _)) + = ForeignImportModule), + ForeignImports) }, + ( { ForeignImports = [] } -> + [] + ; + io__write_string(DepStream, "\n\n"), + io__write_string(DepStream, ObjFileName), + io__write_string(DepStream, " : "), + write_dependencies_list(ForeignImportedModules, ".h", + DepStream), + io__write_string(DepStream, "\n\n") + ), + ( { Target = il }, { not set__empty(LangSet) } @@ -2787,11 +2826,17 @@ generate_deps_map([Module | Modules], DepsMap0, DepsMap) --> ( { Done = no } -> { map__set(DepsMap1, Module, deps(yes, ModuleImports), DepsMap2) }, + { ForeignImportedModules = + list__map( + (func(foreign_import_module(_, ImportedModule, _)) + = ImportedModule), + ModuleImports ^ foreign_import_module_info) }, { list__condense( [ModuleImports ^ parent_deps, ModuleImports ^ int_deps, ModuleImports ^ impl_deps, ModuleImports ^ public_children, % a.k.a. incl_deps + ForeignImportedModules, Modules], Modules1) } ; @@ -3936,15 +3981,15 @@ get_extra_link_objects_2([Module | Modules], DepsMap, Target, ExtraLinkObjs). :- pred get_item_list_foreign_code(globals::in, item_list::in, - set(foreign_language)::out) is det. + set(foreign_language)::out, foreign_import_module_info::out) is det. -get_item_list_foreign_code(Globals, Items, LangSet) :- +get_item_list_foreign_code(Globals, Items, LangSet, ForeignImports) :- globals__get_backend_foreign_languages(Globals, BackendLangs), globals__get_target(Globals, Target), - list__foldl2((pred(Item::in, Set0::in, Set::out, Seen0::in, Seen::out) - is det :- + list__foldl3((pred(Item::in, Set0::in, Set::out, Seen0::in, Seen::out, + Imports0::in, Imports::out) is det :- ( - Item = pragma(Pragma) - _Context + Item = pragma(Pragma) - Context -> % The code here should match the way that mlds_to_gcc.m % decides whether or not to call mlds_to_c.m. XXX Note @@ -3959,7 +4004,8 @@ get_item_list_foreign_code(Globals, Items, LangSet) :- list__member(Lang, BackendLangs) -> set__insert(Set0, Lang, Set), - Seen = Seen0 + Seen = Seen0, + Imports = Imports0 ; Pragma = foreign_proc(Attrs, Name, _, _, _, _) -> @@ -3990,7 +4036,8 @@ get_item_list_foreign_code(Globals, Items, LangSet) :- Seen = Seen0 ) ), - Set = Set0 + Set = Set0, + Imports = Imports0 ; % XXX `pragma export' should not be treated as % foreign, but currently mlds_to_gcc.m doesn't @@ -4005,15 +4052,30 @@ get_item_list_foreign_code(Globals, Items, LangSet) :- % XXX we assume lang = c for exports Lang = c, set__insert(Set0, Lang, Set), - Seen = Seen0 + Seen = Seen0, + Imports = Imports0 + ; + % XXX handle lang \= c for + % `:- pragma foreign_import_module'. + Pragma = foreign_import_module(Lang, Import), + Lang = c, + list__member(c, BackendLangs) + -> + Set = Set0, + Seen = Seen0, + Imports = [foreign_import_module(Lang, + Import, Context) | Imports0] ; Set = Set0, - Seen = Seen0 + Seen = Seen0, + Imports = Imports0 ) ; Set = Set0, - Seen = Seen0 - )), Items, set__init, LangSet0, map__init, LangMap), + Seen = Seen0, + Imports = Imports0 + )), Items, set__init, LangSet0, map__init, LangMap, + [], ForeignImports), Values = map__values(LangMap), LangSet = set__insert_list(LangSet0, Values). @@ -4312,27 +4374,20 @@ init_dependencies(FileName, Error, Globals, ModuleName - Items, get_fact_table_dependencies(Items, FactTableDeps), % Figure out whether the items contain foreign code. - % As an optimization, we do this only if target = asm or target = il - % since those are the only times we'll need that field. - globals__get_target(Globals, Target), + get_item_list_foreign_code(Globals, Items, LangSet, ForeignImports), ContainsForeignCode = - (if (Target = asm ; Target = il) then - (if - get_item_list_foreign_code(Globals, - Items, LangSet), - not set__empty(LangSet) - then - contains_foreign_code(LangSet) - else - no_foreign_code - ) + (if + not set__empty(LangSet) + then + contains_foreign_code(LangSet) else - unknown + no_foreign_code ), ModuleImports = module_imports(FileName, ModuleName, ParentDeps, InterfaceDeps, ImplementationDeps, IndirectDeps, IncludeDeps, - FactTableDeps, ContainsForeignCode, [], Error, no). + FactTableDeps, ContainsForeignCode, ForeignImports, + [], Error, no). %-----------------------------------------------------------------------------% diff --git a/compiler/prog_data.m b/compiler/prog_data.m index bd662c58f..998564422 100644 --- a/compiler/prog_data.m +++ b/compiler/prog_data.m @@ -156,27 +156,17 @@ % whether or not the code is thread-safe % PredName, Predicate or Function, Vars/Mode, % VarNames, Foreign Code Implementation Info - - ; type_spec(sym_name, sym_name, arity, maybe(pred_or_func), - maybe(list(mode)), type_subst, tvarset, set(type_id)) - % PredName, SpecializedPredName, Arity, - % PredOrFunc, Modes if a specific procedure was - % specified, type substitution (using the variable - % names from the pred declaration), TVarSet, - % Equivalence types used ; foreign_type(backend, (type), sym_name, sym_name) % Backend, MercuryType, MercuryTypeName, % ForeignType, ForeignTypeLocation - ; inline(sym_name, arity) - % Predname, Arity - - ; no_inline(sym_name, arity) - % Predname, Arity - - ; obsolete(sym_name, arity) - % Predname, Arity + ; foreign_import_module(foreign_language, module_name) + % Equivalent to + % `:- pragma foreign_decl(Lang, "#include .h").' + % except that the name of the header file is not + % hard-coded, and mmake can use the dependency + % information. ; export(sym_name, pred_or_func, list(mode), string) @@ -190,6 +180,23 @@ % whether or not the foreign code may call Mercury, % whether or not the foreign code is thread-safe % foreign function name. + + ; type_spec(sym_name, sym_name, arity, maybe(pred_or_func), + maybe(list(mode)), type_subst, tvarset, set(type_id)) + % PredName, SpecializedPredName, Arity, + % PredOrFunc, Modes if a specific procedure was + % specified, type substitution (using the variable + % names from the pred declaration), TVarSet, + % Equivalence types used + + ; inline(sym_name, arity) + % Predname, Arity + + ; no_inline(sym_name, arity) + % Predname, Arity + + ; obsolete(sym_name, arity) + % Predname, Arity ; source_file(string) % Source file name. diff --git a/compiler/prog_io_pragma.m b/compiler/prog_io_pragma.m index e3d0696c6..72ecc94d7 100644 --- a/compiler/prog_io_pragma.m +++ b/compiler/prog_io_pragma.m @@ -171,6 +171,41 @@ parse_pragma_type(ModuleName, "c_code", PragmaTerms, ErrorTerm) ). +parse_pragma_type(_ModuleName, "c_import_module", PragmaTerms, + ErrorTerm, _VarSet, Result) :- + ( + PragmaTerms = [ImportTerm], + sym_name_and_args(ImportTerm, Import, []) + -> + Result = ok(pragma(foreign_import_module(c, Import))) + ; + Result = error("wrong number of arguments or invalid module name in `:- pragma c_import_module' declaration", + ErrorTerm) + ). + +parse_pragma_type(_ModuleName, "foreign_import_module", PragmaTerms, + ErrorTerm, _VarSet, Result) :- + ( + PragmaTerms = [LangTerm, ImportTerm], + sym_name_and_args(ImportTerm, Import, []) + -> + ( parse_foreign_language(LangTerm, Language) -> + ( Language = c -> + Result = ok(pragma( + foreign_import_module(Language, Import))) + ; + Result = error("`:- pragma foreign_import_module' not yet supported for languages other than C", LangTerm) + ) + ; + Result = error("invalid foreign language in `:- pragma foreign_import_module' declaration", + LangTerm) + ) + ; + Result = error("wrong number of arguments or invalid module name in `:- pragma foreign_import_module' declaration", + ErrorTerm) + + ). + :- pred parse_foreign_language(term, foreign_language). :- mode parse_foreign_language(in, out) is semidet. diff --git a/compiler/recompilation_version.m b/compiler/recompilation_version.m index 662a1d9f5..0f0aaec32 100644 --- a/compiler/recompilation_version.m +++ b/compiler/recompilation_version.m @@ -449,6 +449,7 @@ item_to_item_id_2(nothing(_), no). maybe(maybe_pred_or_func_id)::out) is det. is_pred_pragma(foreign_decl(_, _), no). +is_pred_pragma(foreign_import_module(_, _), no). is_pred_pragma(foreign_code(_, _), no). is_pred_pragma(foreign_proc(_, Name, PredOrFunc, Args, _, _), yes(yes(PredOrFunc) - Name / Arity)) :- diff --git a/doc/reference_manual.texi b/doc/reference_manual.texi index cbddfd6e9..0c5a1e8b8 100644 --- a/doc/reference_manual.texi +++ b/doc/reference_manual.texi @@ -4923,11 +4923,38 @@ and linking that source file with the Mercury program (after having compiled it with a compiler for the specified programming language, if appropriate). -Entities declared in @samp{pragma foreign_decl} declarations should be +Entities declared in @samp{pragma foreign_decl} declarations are visible in @samp{pragma foreign_code} and @samp{pragma foreign_proc} declarations that specify the same foreign language and occur in in the same Mercury module. +To make the declarations for Mercury predicates or functions +exported to a foreign language using a @samp{pragma export} +declaration visible to foreign code in a @samp{pragma foreign_code} +or @samp{pragma foreign_proc} declaration, use a declaration of the form + +@example +:- pragma foreign_import_module("@var{Lang}", @var{ImportedModule}). +@end example + +where @var{ImportedModule} is the name of the module containing +the @samp{pragma export} declarations. + +If @var{Lang} is @code{"C"} this is equivalent to +@example +:- pragma foreign_decl("C", "#include ""@var{ImportedModule.h}"""). +@end example + +where @file{@var{ImportedModule}.h} is the automatically generated +header file containing the C declarations for the predicates +and functions exported to C. + +@samp{pragma foreign_import_module} should be used instead of the +explicit @code{#include} because @samp{pragma foreign_import_module} +tells the implementation that @file{@var{ImportedModule}.h} must be built +before the object file for the module containing the +@samp{pragma foreign_import_module} declaration. + @node Adding foreign definitions @section Adding foreign definitions @@ -4939,13 +4966,13 @@ variables) may be included using a declaration of the form @end example This declaration will have effects equivalent to including the specified -@var{DeclCode} in an automatically-generated source file of the specified +@var{Code} in an automatically-generated source file of the specified programming language, in a place appropriate for definitions, and linking that source file with the Mercury program (after having compiled it with a compiler for the specified programming language, if appropriate). -Entities declared in @samp{pragma foreign_code} declarations should be +Entities declared in @samp{pragma foreign_code} declarations are visible in @samp{pragma foreign_proc} declarations that specify the same foreign language and occur in in the same Mercury module. @@ -5004,8 +5031,7 @@ Use the string "MC++" to set the foreign language to Managed C++. The input and output variables will have C types corresponding to their Mercury types, as determined by the rules specified in -``Passing data to and from C'' in the ``C Interface'' -chapter of the Mercury Language Reference Manual. +@ref{Passing data to and from C}. The C code fragment may declare local variables, but it should not declare any labels or static variables unless there is also a Mercury @@ -5089,6 +5115,17 @@ Mercury automatically includes certain headers such as @code{}, but you should not rely on this, as the set of headers which Mercury automatically includes is subject to change. +If a Mercury predicate or function exported using +a @samp{pragma export} declaration is to be used within a +@samp{:- pragma foreign_code} or @samp{:- pragma foreign_proc} +declaration the header file for the module containing the +@samp{pragma export} declaration should be included using a +@samp{pragma foreign_import_module} declaration, for example + +@example +:- pragma foreign_import_module("C", exporting_module). +@end example + @node Using pragma foreign_code for C @subsubsection Using pragma foreign_code for C @@ -5821,6 +5858,22 @@ of the other arguments passed. These @samp{type_info} arguments can be obtained using the Mercury @samp{type_of} function in the Mercury standard library module @samp{std_util}. +To use the C declarations produced for @samp{pragma export} declarations +in C code within a Mercury module, use a @samp{pragma c_import_module} +declaration, for example + +@example +:- pragma c_import_module(imported_module). +@end example + +This is equivalent to +@example +:- pragma c_header_code("#include ""imported_module.h"""). +@end example +but it tells the implementation that the object file for the +module containing the @samp{pragma c_import_module} declaration +should not be built before @file{imported_module.h} is built. + @node Linking with C object files @section Linking with C object files diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile index 74a4a8821..6c127aa58 100644 --- a/tests/hard_coded/Mmakefile +++ b/tests/hard_coded/Mmakefile @@ -53,6 +53,7 @@ ORDINARY_PROGS= \ float_map \ float_reg \ float_rounding_bug \ + foreign_import_module \ frameopt_pragma_redirect \ free_free_mode \ func_and_pred \ diff --git a/tests/hard_coded/foreign_import_module.exp b/tests/hard_coded/foreign_import_module.exp new file mode 100644 index 000000000..d81cc0710 --- /dev/null +++ b/tests/hard_coded/foreign_import_module.exp @@ -0,0 +1 @@ +42 diff --git a/tests/hard_coded/foreign_import_module.m b/tests/hard_coded/foreign_import_module.m new file mode 100644 index 000000000..8643183af --- /dev/null +++ b/tests/hard_coded/foreign_import_module.m @@ -0,0 +1,23 @@ +:- module foreign_import_module. + +:- interface. + +:- import_module int, io. + +:- pred main(io__state::di, io__state::uo) is det. + +:- pred bar(int::in, int::out) is det. + +:- implementation. + +main --> + { bar(41, X) }, + io__write(X), + io__write_char('\n'). + +:- pragma foreign_import_module("C", foreign_import_module_2). + +:- pragma c_code(bar(X::in, Y::out), may_call_mercury, +" + foo(X, &Y); +"). diff --git a/tests/hard_coded/foreign_import_module_2.m b/tests/hard_coded/foreign_import_module_2.m new file mode 100644 index 000000000..8155735d4 --- /dev/null +++ b/tests/hard_coded/foreign_import_module_2.m @@ -0,0 +1,13 @@ +:- module foreign_import_module_2. + +:- interface. + +:- pred foo(int::in, int::out) is det. + +:- implementation. + +:- import_module int. + +:- pragma export(foo(in, out), "foo"). + +foo(X, X+1).