mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
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".
495 lines
18 KiB
Mathematica
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.
|
|
%-----------------------------------------------------------------------------%
|