Files
mercury/compiler/prog_foreign.m
Zoltan Somogyi 7dab0cac8c Remove almost all remaining references to Erlang.
The only three places we still refer to Erlang are the places where such
references are needed to explain the reason why the current code is
what it is.

compiler/builtin_ops.m:
    Delete the builltin ops that compare whole terms, which was only ever
    used by the Erlang backend.

library/private_builtin.m:
    Stop declaring the deleted builtin ops.

compiler/compute_grade.m:
compiler/parse_pragma_foreign.m:
    Stop adding "Support for Erlang has been discontinued" to error messages
    for code that still refers to Erlang.

compiler/add_mutable_aux_preds.m:
compiler/add_pred.m:
compiler/code_util.m:
compiler/generate_mmakefile_fragments.m:
compiler/globals.m:
compiler/llds.m:
compiler/llds_out_data.m:
compiler/mercury_compile_middle_passes.m:
compiler/ml_global_data.m:
compiler/mlds_dump.m:
compiler/mlds_to_c_data.m:
compiler/mlds_to_cs_data.m:
compiler/mlds_to_java_data.m:
compiler/opt_debug.m:
compiler/parse_mutable.m:
compiler/prog_foreign.m:
compiler/simplify_goal_call.m:
compiler/simplify_goal_unify.m:
compiler/term_constr_initial.m:
    Conform to the changes above, and/or delete other references to Erlang.

tests/invalid_make_int/bad_foreign_type_int.int_err_exp:
tests/invalid_nodepend/bad_foreign_code.err_exp:
tests/invalid_nodepend/bad_foreign_decl.err_exp:
tests/invalid_nodepend/bad_foreign_enum.err_exp:
tests/invalid_nodepend/bad_foreign_export.err_exp:
tests/invalid_nodepend/bad_foreign_export_enum.err_exp:
tests/invalid_nodepend/bad_foreign_import_module.err_exp:
tests/invalid_nodepend/bad_foreign_proc.err_exp:
    Don't expect the message about "support has been discontinued".
2024-12-14 22:26:43 +11:00

495 lines
18 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2000-2008, 2010 The University of Melbourne.
% Copyright (C) 2013-2017, 2019-2024 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% Main authors: trd, dgj.
%
% This module defines predicate for interfacing with foreign languages.
% that are necessary for the frontend of the compiler to construct
% the parse tree. The predicates in this module should not depend on the HLDS
% in any way. The predicates for interfacing with foreign languages that
% *do* depend on the HLDS are defined in foreign.m.
%
% This module also contains the parts of the name mangler that are used
% by the frontend of the compiler.
%
% Warning: any changes to the name mangling algorithms implemented in this
% module may also require changes to profiler/demangle.m, util/mdemangle.c and
% compiler/name_mangle.m.
%
%-----------------------------------------------------------------------------%
:- module parse_tree.prog_foreign.
:- interface.
:- import_module libs.
:- import_module libs.globals.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.file_names.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_data_foreign.
:- import_module bool.
:- import_module list.
:- import_module map.
:- import_module pair.
%-----------------------------------------------------------------------------%
:- type foreign_decl_code
---> foreign_decl_code(
fdecl_lang :: foreign_language,
fdecl_is_local :: foreign_decl_is_local,
fdecl_code :: foreign_literal_or_include,
fdecl_context :: prog_context
).
:- type foreign_body_code
---> foreign_body_code(
fbody_lang :: foreign_language,
fbody_code :: foreign_literal_or_include,
fbody_context :: prog_context
).
:- type foreign_export_defn
---> foreign_export_defn(
% The code for `pragma foreign_export' is generated directly
% as strings by export.m.
string
).
:- type foreign_export_decls
---> foreign_export_decls(
fexp_decls_codes :: list(foreign_decl_code),
fexp_decls_list :: list(foreign_export_decl)
).
:- type foreign_export_decl
---> foreign_export_decl(
% Language of the export.
fexp_decl_lang :: foreign_language,
% Return type.
fexp_decl_ret_type :: string,
% Function name.
fexp_decl_func_name :: string,
% Argument declarations.
fexp_decl_arg_decls :: string
).
%-----------------------------------------------------------------------------%
:- pred foreign_decl_code_is_for_lang(foreign_language::in,
foreign_decl_code::in) is semidet.
:- pred foreign_body_code_is_for_lang(foreign_language::in,
foreign_body_code::in) is semidet.
% foreign_import_module_name(ForeignImport)
%
% returns the module name which represents the ForeignImport.
%
% For instance for the foreign_import_module representing
% :- foreign_import_module("C#", module)
% would return the module_name
% unqualified("module__csharp_code")
%
:- func fim_spec_module_name(fim_spec) = module_name.
% foreign_import_module_name_from_module(ForeignImport, CurrentModule)
%
% returns the module name needed to refer to ForeignImport from the
% CurrentModule.
%
:- func fim_spec_module_name_from_module(fim_spec, module_name) = module_name.
% The module name used for this foreign language.
%
:- pred foreign_language_module_name(module_name, foreign_language,
module_name).
:- mode foreign_language_module_name(in, in, out) is det.
% The file extension used for the target files we genereate
% in the given foreign language.
%
:- pred foreign_language_file_extension(foreign_language, ext).
:- mode foreign_language_file_extension(in, out) is det.
% It is possible that more than one foreign language could be used to
% implement a particular piece of code. Therefore, foreign languages
% have an order of preference, from most preferred to least preferred.
% prefer_foreign_language(Globals, Target, Lang1, Lang2) returns
% `yes' if Lang2 is preferred over Lang1; otherwise, it will return no.
%
:- func prefer_foreign_language(globals, compilation_target,
foreign_language, foreign_language) = bool.
% Return all supported foreign languages.
%
:- func all_foreign_languages = list(foreign_language).
:- func foreign_type_language(generic_language_foreign_type)
= foreign_language.
%-----------------------------------------------------------------------------%
%
% The following are the parts of the name mangler that are needed by
% the compiler frontend so that it can write out Makefile fragments.
% Returns the name of the initialization function for a given module.
%
:- func make_init_name(module_name) = string.
% Mangle a possibly module-qualified Mercury symbol name
% into a C identifier.
%
:- func sym_name_mangle(sym_name) = string.
% Mangle an arbitrary name into a C etc identifier.
% Initial digits are allowed.
%
:- func name_mangle(string) = string.
% Mangle an arbitrary name into a C etc identifier.
% The resulting identifier will not begin with a digit.
%
:- func name_mangle_no_leading_digit(string) = string.
% Produces a string of the form Module__Name.
%
:- func qualify_name(string, string) = string.
:- func convert_to_valid_c_identifier(string) = string.
%-----------------------------------------------------------------------------%
% The exported predicate with the given sym_name and arity is represented
% in the target language as the string in the second argument.
% The pred_or_func is implicitly pf_predicate.
%
% Used in the implementation of mutable, initialise and finalise
% declarations.
%
:- type pred_target_name == pair(sym_name_arity, string).
% Maps a mutable, initialise or finalise declaration, whose sequence number
% (which must be present and valid) is the integer key, to the
% the exported predicate or predicates that the compiler generates
% for that declaration.
%
% We use the sequence number as the key in a map so that we can invoke
% the exported initialization predicates in program order, as the
% language manual expects us to do.
%
:- type pred_target_names
---> pred_target_names(
map(int, list(pred_target_name))
).
% Add a new initialize or finalize predicate.
%
:- pred new_user_init_or_final_pred_target_name(module_name::in,
string::in, item_seq_num::in, sym_name::in, user_arity::in, string::out,
pred_target_names::in, pred_target_names::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module char.
:- import_module require.
:- import_module solutions.
:- import_module string.
%-----------------------------------------------------------------------------%
foreign_decl_code_is_for_lang(Lang, DeclCode) :-
Lang = DeclCode ^ fdecl_lang.
foreign_body_code_is_for_lang(Lang, BodyCode) :-
Lang = BodyCode ^ fbody_lang.
fim_spec_module_name(FIMSpec) = ModuleName :-
FIMSpec = fim_spec(Lang, ForeignImportModule),
(
Lang = lang_c,
ModuleName = ForeignImportModule
;
Lang = lang_java,
ModuleName = ForeignImportModule
;
Lang = lang_csharp,
foreign_language_module_name(ForeignImportModule, Lang, ModuleName)
).
fim_spec_module_name_from_module(ModuleFIMSpec, CurrentModule) =
ImportedForeignCodeModuleName :-
ModuleFIMSpec = fim_spec(Lang, _),
ImportedForeignCodeModuleName1 = fim_spec_module_name(ModuleFIMSpec),
(
Lang = lang_c,
ImportedForeignCodeModuleName = ImportedForeignCodeModuleName1
;
Lang = lang_csharp,
ImportedForeignCodeModuleName = handle_std_library(CurrentModule,
ImportedForeignCodeModuleName1)
;
Lang = lang_java,
ImportedForeignCodeModuleName = handle_std_library(CurrentModule,
ImportedForeignCodeModuleName1)
).
% On the il backend, we need to refer to the module "mercury" when
% referencing a std library module when we are not actually building
% the std library.
% XXX Obsolete comment.
%
:- func handle_std_library(module_name, module_name) = module_name.
handle_std_library(CurrentModule, ModuleName0) = ModuleName :-
( if
mercury_std_library_module_name(ModuleName0),
not mercury_std_library_module_name(CurrentModule)
then
ModuleName = unqualified("mercury")
else
ModuleName = ModuleName0
).
%-----------------------------------------------------------------------------%
foreign_language_module_name(ModuleName, Lang, FullyQualifiedModuleName) :-
% Only succeed if this language generates external files.
foreign_language_file_extension(Lang, _),
Ending = "__" ++ simple_foreign_language_string(Lang) ++ "_code",
(
ModuleName = unqualified(Name),
FullyQualifiedModuleName = unqualified(Name ++ Ending)
;
ModuleName = qualified(Module, Name),
FullyQualifiedModuleName = qualified(Module, Name ++ Ending)
).
%-----------------------------------------------------------------------------%
foreign_language_file_extension(lang_c,
ext_cur_ngs_gs(ext_cur_ngs_gs_target_c)).
foreign_language_file_extension(lang_csharp,
ext_cur_ngs_gs(ext_cur_ngs_gs_target_cs)).
foreign_language_file_extension(lang_java,
ext_cur_ngs_gs_java(ext_cur_ngs_gs_java_java)).
%-----------------------------------------------------------------------------%
prefer_foreign_language(_Globals, Target, Lang1, Lang2) = Prefer :-
% Currently we don't use the globals to compare foreign language
% interfaces, but if we added appropriate options we might want
% to do this later.
(
Target = target_c,
% When compiling to C, C is always preferred over any other language.
( if Lang2 = lang_c, not Lang1 = lang_c then
Prefer = yes
else
Prefer = no
)
;
Target = target_csharp,
Prefer = no
;
Target = target_java,
% Nothing useful to do here, but when we add Java as a
% foreign language, we should add it here.
% XXX Obsolete comment.
Prefer = no
).
%-----------------------------------------------------------------------------%
all_foreign_languages = Langs :-
GetLangs = (pred(Lang::out) is multi :- valid_foreign_language(Lang)),
solutions(GetLangs, Langs).
:- pred valid_foreign_language(foreign_language).
:- mode valid_foreign_language(in) is det.
:- mode valid_foreign_language(out) is multi.
valid_foreign_language(lang_c).
valid_foreign_language(lang_java).
valid_foreign_language(lang_csharp).
%-----------------------------------------------------------------------------%
foreign_type_language(c(_)) = lang_c.
foreign_type_language(java(_)) = lang_java.
foreign_type_language(csharp(_)) = lang_csharp.
%-----------------------------------------------------------------------------%
make_init_name(ModuleName) = InitName :-
MangledModuleName = sym_name_mangle(ModuleName),
InitName = "mercury__" ++ MangledModuleName ++ "__".
sym_name_mangle(unqualified(Name)) =
name_mangle(Name).
sym_name_mangle(qualified(ModuleName, PlainName)) = MangledName :-
MangledModuleName = sym_name_mangle(ModuleName),
MangledPlainName = name_mangle(PlainName),
MangledName = qualify_name(MangledModuleName, MangledPlainName).
name_mangle(Name) = name_mangle_2(yes, Name).
name_mangle_no_leading_digit(Name) = name_mangle_2(no, Name).
:- func name_mangle_2(bool, string) = string.
name_mangle_2(AllowLeadingDigit, Name) = MangledName :-
% Warning: any changes to the name mangling algorithm here may also
% require changes to profiler/demangle.m, util/mdemangle.c,
% compiler/name_mangle.m and library/rtti_implementation.m.
( if
string.is_all_alnum_or_underscore(Name),
(
AllowLeadingDigit = yes
;
AllowLeadingDigit = no,
% If the mangled name may be used at the start of a symbol then
% leading digits are invalid.
string.index(Name, 0, FirstChar),
not char.is_digit(FirstChar)
)
then
% Any names that start with `f_' are changed so that they start with
% `f__', so that we can use names starting with `f_' (followed by
% anything except an underscore) without fear of name collisions.
( if string.append("f_", Suffix, Name) then
MangledName = "f__" ++ Suffix
else
MangledName = Name
)
else
MangledName = convert_to_valid_c_identifier(Name)
).
qualify_name(Module0, Name0) = Name :-
string.append_list([Module0, "__", Name0], Name).
convert_to_valid_c_identifier(String) = Name :-
( if name_conversion_table(String, Name0) then
Name = Name0
else
Name = "f" ++ convert_to_valid_c_identifier_2(String)
).
% A table used to convert Mercury functors into C identifiers.
% Feel free to add any new translations you want. The C identifiers
% should start with "f_", to avoid introducing name clashes. If the functor
% name is not found in the table, then we use a fall-back method which
% produces ugly names.
%
% Additions to this table should be reflected in rtti_implementation.m,
% in the ML_name_mangle() method.
%
:- pred name_conversion_table(string::in, string::out) is semidet.
name_conversion_table("\\=", "f_not_equal").
name_conversion_table(">=", "f_greater_or_equal").
name_conversion_table("=<", "f_less_or_equal").
name_conversion_table("=", "f_equal").
name_conversion_table("<", "f_less_than").
name_conversion_table(">", "f_greater_than").
name_conversion_table("-", "f_minus").
name_conversion_table("+", "f_plus").
name_conversion_table("*", "f_times").
name_conversion_table("/", "f_slash").
name_conversion_table(",", "f_comma").
name_conversion_table(";", "f_semicolon").
name_conversion_table("!", "f_cut").
name_conversion_table("{}", "f_tuple").
name_conversion_table("[|]", "f_cons").
name_conversion_table("[]", "f_nil").
% This is the fall-back method. Given a string, produce a C identifier
% for that string by concatenating the decimal expansions of the character
% codes in the string, separated by underlines. The C identifier will
% start with "f_"; this predicate constructs everything except the initial
% "f".
%
% For example, given the input "\n\t" we return "_10_8".
%
:- func convert_to_valid_c_identifier_2(string) = string.
convert_to_valid_c_identifier_2(String) = Name :-
( if string.first_char(String, Char, Rest) then
% XXX This will cause ABI incompatibilities between compilers which are
% built in grades that have different character representations.
char.to_int(Char, Code),
string.int_to_string(Code, CodeString),
string.append("_", CodeString, ThisCharString),
Name0 = convert_to_valid_c_identifier_2(Rest),
string.append(ThisCharString, Name0, Name)
else
% String is the empty string.
Name = String
).
%---------------------------------------------------------------------------%
new_user_init_or_final_pred_target_name(ModuleName0, InitOrFinal, SeqNum,
SymName, UserArity, TargetName, !PredTargetNames) :-
% XXX There is some debate as to whether duplicate initialise directives
% in the same module should constitute an error. Currently it is not, but
% we may wish to revisit this code. The reference manual is therefore
% deliberately quiet on the subject.
(
SeqNum = item_seq_num(SeqNumInt)
;
SeqNum = item_no_seq_num,
unexpected($pred, "item_no_seq_num")
),
UserArity = user_arity(UserArityInt),
!.PredTargetNames = pred_target_names(PredTargetNameMap0),
( if mercury_std_library_module_name(ModuleName0) then
ModuleName = add_outermost_qualifier("mercury", ModuleName0)
else
ModuleName = ModuleName0
),
ModuleNameStr = prog_foreign.sym_name_mangle(ModuleName),
( if map.search(PredTargetNameMap0, SeqNumInt, SeqNumPredTargetNames0) then
% The only situation in which a sequence number will have
% more than one entry is when a solver type's representation
% involves more than one mutable. The number of these should be
% be limited, which is why the quadratic behavior of this code is ok.
% We do nevertheless need to include something, such as Suffix,
% to distinguish the target names from each other.
list.length(SeqNumPredTargetNames0, Suffix),
TargetName = string.format("%s__user_%s_pred_%d_%d",
[s(ModuleNameStr), s(InitOrFinal), i(SeqNumInt), i(Suffix)]),
PredTargetName = sym_name_arity(SymName, UserArityInt) - TargetName,
SeqNumPredTargetNames = SeqNumPredTargetNames0 ++ [PredTargetName],
map.det_update(SeqNumInt, SeqNumPredTargetNames,
PredTargetNameMap0, PredTargetNameMap)
else
TargetName = string.format("%s__user_%s_pred_%d_%d",
[s(ModuleNameStr), s(InitOrFinal), i(SeqNumInt), i(0)]),
PredTargetName = sym_name_arity(SymName, UserArityInt) - TargetName,
map.det_insert(SeqNumInt, [PredTargetName],
PredTargetNameMap0, PredTargetNameMap)
),
!:PredTargetNames = pred_target_names(PredTargetNameMap).
%-----------------------------------------------------------------------------%
:- end_module parse_tree.prog_foreign.
%-----------------------------------------------------------------------------%