Files
mercury/compiler/ml_foreign_proc_gen.m
Zoltan Somogyi 291879c8bb Move checks for type_infos to add_foreign_proc.m.
This allows to perform those checks for *all* foreign_procs,
not just the ones that get added to the HLDS.

compiler/add_foreign_proc.m:
    Move the code that checks the bodies of foreign_procs for the
    presence of type_info variables for existentially quantified
    type variables here from typecheck.m and typecheck_errors.m.
    Change the diagnostic's wording to match our new phraseology.

    Record identifiers in a set, not a list, for faster membership tests,
    since we now do even more of them.

compiler/foreign.m:
    Provide a mechanism to return the identifiers not just in the
    non-comment parts of foreign_procs, but the comment parts as well,
    since add_foreign_proc.m now needs this functionality.

compiler/make_hlds_warn.m:
    Conform to the change in foreign.m.

compiler/typecheck.m:
compiler/typecheck_errors.m:
    Delete the code that was moved (in a modified form)
    to add_foreign_proc.m.

compiler/ml_foreign_proc_gen.m:
    Update a reference in a comment.

tests/invalid/exist_foreign_error.err_exp:
    Expect the updated wording of the affected diagnostics,
    and expect diagnostics for *all* the foreign_procs in the test,
    regardless of which language they are for.

tests/invalid/exist_foreign_error.err_exp2:
tests/invalid/exist_foreign_error.err_exp3:
    Delete these files, since the output they expect is now
    in the .err_exp file.

tests/invalid/fp_dup_bug.err_exp2:
tests/invalid/fp_dup_bug.err_exp3:
tests/invalid/gh72_errors.err_exp2:
tests/invalid/gh72_errors.err_exp3:
    Expect the updated wording of diagnostics affected by previous
    changes (which updated the .err_exp files for C, not these for
    Java and C#).
2026-01-31 16:31:04 +11:00

1123 lines
45 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2009-2012 The University of Melbourne.
% Copyright (C) 2013-2026 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.
%---------------------------------------------------------------------------%
%
% File: ml_foreign_proc.m.
% Main author: fjh.
%
:- module ml_backend.ml_foreign_proc_gen.
:- interface.
:- import_module hlds.
:- import_module hlds.code_model.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_pred.
:- import_module ml_backend.ml_gen_info.
:- import_module ml_backend.mlds.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_data_foreign.
:- import_module list.
:- pred ml_gen_trace_runtime_cond(trace_expr(trace_runtime)::in,
prog_context::in, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_gen_foreign_proc(code_model::in,
foreign_proc_attributes::in, pred_id::in, proc_id::in,
list(foreign_arg)::in, list(foreign_arg)::in, string::in,
prog_context::in, list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.builtin_ops.
:- import_module backend_libs.c_util.
:- import_module backend_libs.foreign.
:- import_module hlds.hlds_data.
:- import_module hlds.hlds_markers.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_proc_util.
:- import_module hlds.mode_top_functor.
:- import_module hlds.type_util.
:- import_module libs.
:- import_module libs.globals.
:- import_module libs.options.
:- import_module mdbcomp.
:- import_module mdbcomp.builtin_modules.
:- import_module mdbcomp.sym_name.
:- import_module ml_backend.ml_code_util.
:- import_module ml_backend.ml_global_data.
:- import_module parse_tree.builtin_lib_types.
:- import_module parse_tree.prog_type.
:- import_module parse_tree.var_table.
:- import_module bool.
:- import_module int.
:- import_module maybe.
:- import_module require.
:- import_module string.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
ml_gen_trace_runtime_cond(TraceRuntimeCond, Context, Stmts, !Info) :-
ml_success_lval(SuccessLval, !Info),
ml_generate_runtime_cond_code(TraceRuntimeCond, CondRval, !Info),
Stmt = ml_stmt_atomic(assign(SuccessLval, CondRval), Context),
Stmts = [Stmt].
:- pred ml_generate_runtime_cond_code(trace_expr(trace_runtime)::in,
mlds_rval::out, ml_gen_info::in, ml_gen_info::out) is det.
ml_generate_runtime_cond_code(Expr, CondRval, !Info) :-
(
Expr = trace_base(trace_envvar(EnvVar)),
ml_gen_info_add_env_var_name(EnvVar, !Info),
EnvVarRval = ml_lval(ml_target_global_var_ref(env_var_ref(EnvVar))),
ZeroRval = ml_const(mlconst_int(0)),
CondRval = ml_binop(int_cmp(int_type_int, ne), EnvVarRval, ZeroRval)
;
Expr = trace_not(ExprA),
ml_generate_runtime_cond_code(ExprA, RvalA, !Info),
CondRval = ml_unop(logical_not, RvalA)
;
Expr = trace_op(TraceOp, ExprA, ExprB),
ml_generate_runtime_cond_code(ExprA, RvalA, !Info),
ml_generate_runtime_cond_code(ExprB, RvalB, !Info),
(
TraceOp = trace_or,
Op = logical_or
;
TraceOp = trace_and,
Op = logical_and
),
CondRval = ml_binop(Op, RvalA, RvalB)
).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
ml_gen_foreign_proc(CodeModel, Attributes, PredId, ProcId, Args, ExtraArgs,
ForeignCode, Context, Decls, Stmts, !Info) :-
Lang = get_foreign_language(Attributes),
(
CodeModel = model_det,
OrdinaryKind = kind_det
;
CodeModel = model_semi,
ml_gen_info_get_module_info(!.Info, ModuleInfo),
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
_PredInfo, ProcInfo),
proc_info_interface_determinism(ProcInfo, Detism),
determinism_components(Detism, _, MaxSoln),
(
MaxSoln = at_most_zero,
OrdinaryKind = kind_failure
;
( MaxSoln = at_most_one
; MaxSoln = at_most_many
; MaxSoln = at_most_many_cc
),
OrdinaryKind = kind_semi
)
;
CodeModel = model_non,
OrdinaryDespiteDetism = get_ordinary_despite_detism(Attributes),
(
OrdinaryDespiteDetism = not_ordinary_despite_detism,
unexpected($pred, "unexpected code model")
;
OrdinaryDespiteDetism = ordinary_despite_detism,
OrdinaryKind = kind_semi
)
),
(
Lang = lang_c,
ml_gen_foreign_proc_for_c(OrdinaryKind, Attributes,
PredId, ProcId, Args, ExtraArgs,
ForeignCode, Context, Decls, Stmts, !Info)
;
Lang = lang_csharp,
ml_gen_info_get_target(!.Info, Target),
(
Target = ml_target_csharp,
ml_gen_foreign_proc_for_csharp_or_java(ml_target_csharp,
OrdinaryKind, Attributes, PredId, ProcId, Args, ExtraArgs,
ForeignCode, Context, Decls, Stmts, !Info)
;
( Target = ml_target_c
; Target = ml_target_java
),
unexpected($pred,
"C# foreign code not supported for compilation target")
)
;
Lang = lang_java,
ml_gen_foreign_proc_for_csharp_or_java(ml_target_java, OrdinaryKind,
Attributes, PredId, ProcId, Args, ExtraArgs,
ForeignCode, Context, Decls, Stmts, !Info)
).
:- type foreign_proc_detism
---> kind_det
; kind_semi
; kind_failure.
:- inst java_or_csharp for mlds_target_lang/0
---> ml_target_java
; ml_target_csharp.
:- pred ml_gen_foreign_proc_for_csharp_or_java(
mlds_target_lang::in(java_or_csharp), foreign_proc_detism::in,
foreign_proc_attributes::in, pred_id::in, proc_id::in,
list(foreign_arg)::in, list(foreign_arg)::in, string::in,
prog_context::in, list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_foreign_proc_for_csharp_or_java(TargetLang, OrdinaryKind, Attributes,
PredId, _ProcId, OrigArgs, ExtraArgs, CsOrJavaCode, Context,
Decls, Stmts, !Info) :-
Lang = get_foreign_language(Attributes),
ml_gen_info_get_module_info(!.Info, ModuleInfo),
module_info_pred_info(ModuleInfo, PredId, PredInfo),
pred_info_get_markers(PredInfo, Markers),
( if marker_is_present(Markers, marker_mutable_access_pred) then
MutableSpecial = mutable_special_case
else
MutableSpecial = not_mutable_special_case
),
% Generate <declaration of one local variable for each arg>.
expect(unify(ExtraArgs, []), $pred, "extra args"),
Args = OrigArgs,
ml_gen_foreign_proc_csharp_java_decls(!.Info, MutableSpecial, CsOrJavaCode,
1, 1, Args, ArgDecls, CopyTIsIn, _CopyTIsOut),
% Generate code to set the values of the input variables.
ml_gen_foreign_proc_ccsj_input_args(!.Info, Lang, Args, AssignInputs),
% Generate MLDS statements to assign the values of the output variables.
ml_gen_foreign_proc_csharp_java_output_args(MutableSpecial, Args, Context,
AssignOutputs, ConvDecls, ConvStmts, !Info),
% Put it all together.
(
OrdinaryKind = kind_det,
SucceededDecl = [],
AssignSucceeded = []
;
OrdinaryKind = kind_semi,
ml_success_lval(SucceededLval, !Info),
(
TargetLang = ml_target_java,
BoolType = "boolean"
;
TargetLang = ml_target_csharp,
BoolType = "bool"
),
SucceededDecl = [
raw_target_code("\t" ++ BoolType ++ " SUCCESS_INDICATOR;\n")],
AssignSucceeded = [
raw_target_code("\t"),
target_code_output(SucceededLval),
raw_target_code(" = SUCCESS_INDICATOR;\n")
]
;
OrdinaryKind = kind_failure,
ml_success_lval(SucceededLval, !Info),
SucceededDecl = [],
AssignSucceeded = [
raw_target_code("\t"),
target_code_output(SucceededLval),
raw_target_code(" = false;\n")
]
),
StartingFragments = list.condense([
[raw_target_code("{\n")],
ArgDecls,
SucceededDecl,
AssignInputs,
CopyTIsIn,
[user_target_code(CsOrJavaCode, yes(Context))]
]),
StartingCode = inline_target_code(TargetLang, StartingFragments),
StartingCodeStmt = ml_stmt_atomic(StartingCode, Context),
EndingFragments = AssignSucceeded ++ [raw_target_code("\t}\n")],
EndingCode = inline_target_code(TargetLang, EndingFragments),
EndingCodeStmt = ml_stmt_atomic(EndingCode, Context),
Stmts = [StartingCodeStmt] ++
AssignOutputs ++ ConvStmts ++
[EndingCodeStmt],
Decls = ConvDecls.
% For a C foreign_proc, we generate code of the following form:
%
% model_det C foreign_proc:
%
% #define MR_ALLOC_ID <allocation id>
% #define MR_PROC_LABEL <procedure name>
% <declaration of locals needed for boxing/unboxing>
% {
% <declaration of one local variable for each arg>
%
% <assign input args>
% <obtain global lock>
% <c code>
% <boxing/unboxing of outputs>
% <release global lock>
% <assign output args>
% }
% #undef MR_ALLOC_ID
% #undef MR_PROC_LABEL
%
% model_semi C foreign_proc:
%
% #define MR_ALLOC_ID <allocation id>
% #define MR_PROC_LABEL <procedure name>
% <declaration of locals needed for boxing/unboxing>
% {
% <declaration of one local variable for each arg>
% MR_bool SUCCESS_INDICATOR;
%
% <assign input args>
% <obtain global lock>
% <c code>
% <release global lock>
% if (SUCCESS_INDICATOR) {
% <assign output args>
% <boxing/unboxing of outputs>
% }
%
% <succeeded> = SUCCESS_INDICATOR;
% }
% #undef MR_ALLOC_ID
% #undef MR_PROC_LABEL
%
% We insert a #define MR_ALLOC_ID so that the C code in the Mercury
% standard library that allocates memory manually can use MR_ALLOC_ID as an
% argument to incr_hp_msg(), for memory profiling. It replaces an older
% macro MR_PROC_LABEL, which is retained only for backwards compatibility.
%
% Note that we generate this code directly as
% `target_code(lang_C, <string>)' instructions in the MLDS.
% It would probably be nicer to encode more of the structure
% in the MLDS, so that (a) we could do better MLDS optimization
% and (b) so that the generation of C code strings could be
% isolated in mlds_to_c_*.m. Also, we need to do something different
% for targets other than C, e.g. when compiling to C# or Java.
%
:- pred ml_gen_foreign_proc_for_c(foreign_proc_detism::in,
foreign_proc_attributes::in, pred_id::in, proc_id::in,
list(foreign_arg)::in, list(foreign_arg)::in, string::in,
prog_context::in, list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_foreign_proc_for_c(OrdinaryKind, Attributes, PredId, _ProcId,
OrigArgs, ExtraArgs, C_Code, Context, Decls, Stmts, !Info) :-
Lang = get_foreign_language(Attributes),
% Generate <declaration of one local variable for each arg>
Args = OrigArgs ++ ExtraArgs,
ml_gen_foreign_proc_c_decls(!.Info, Lang, C_Code, 1, 1, Args,
ArgDecls, CopyTIsIn, CopyTIsOut),
% Generate code to set the values of the input variables.
ml_gen_foreign_proc_ccsj_input_args(!.Info, Lang, Args, AssignInputs),
% Generate code to assign the values of the output variables.
ml_gen_foreign_proc_c_output_args(Args, Context,
AssignOutputs, ConvDecls, ConvStmts, !Info),
% Generate code fragments to obtain and release the global lock.
ThreadSafe = get_thread_safe(Attributes),
ml_gen_obtain_release_global_lock(!.Info, ThreadSafe, PredId,
ObtainLock, ReleaseLock),
% Generate the MR_ALLOC_ID #define.
ml_gen_hash_define_mr_alloc_id([C_Code], Context,
HashDefineAllocId, HashUndefAllocId, !Info),
% Generate the MR_PROC_LABEL #define.
ml_gen_hash_define_mr_proc_label(!.Info, HashDefineProcLabel),
% Put it all together.
(
OrdinaryKind = kind_det,
StartingFragments = list.condense([
[raw_target_code("{\n")],
HashDefineAllocId,
HashDefineProcLabel,
ArgDecls,
[raw_target_code("\n")],
AssignInputs,
CopyTIsIn,
[raw_target_code(ObtainLock),
raw_target_code("\t\t{\n"),
user_target_code(C_Code, yes(Context)),
raw_target_code("\n\t\t;}\n")],
HashUndefAllocId,
[raw_target_code("#undef MR_PROC_LABEL\n"),
raw_target_code(ReleaseLock)],
CopyTIsOut,
AssignOutputs
]),
EndingFragments = [raw_target_code("}\n")]
;
OrdinaryKind = kind_failure,
% We need to treat this case separately, because for these
% foreign_procs the C code fragment won't assign anything to
% SUCCESS_INDICATOR; the code we generate for CanSucceed = yes
% would test an undefined value.
ml_success_lval(SucceededLval, !Info),
StartingFragments = list.condense([
[raw_target_code("{\n")],
HashDefineAllocId,
HashDefineProcLabel,
ArgDecls,
[raw_target_code("\n")],
AssignInputs,
CopyTIsIn,
[raw_target_code(ObtainLock),
raw_target_code("\t\t{\n"),
user_target_code(C_Code, yes(Context)),
raw_target_code("\n\t\t;}\n")],
HashUndefAllocId,
[raw_target_code("#undef MR_PROC_LABEL\n"),
raw_target_code(ReleaseLock)]
]),
EndingFragments = [
target_code_output(SucceededLval),
raw_target_code(" = MR_FALSE;\n"),
raw_target_code("}\n")
]
;
OrdinaryKind = kind_semi,
ml_success_lval(SucceededLval, !Info),
( if
CopyTIsOut = [],
AssignOutputs = [],
ConvStmts = []
then
IfSuccFragments = [],
EndIfSuccFragments = []
else
IfSuccFragments = [
raw_target_code("\tif (SUCCESS_INDICATOR) {\n") |
CopyTIsOut
] ++ AssignOutputs,
EndIfSuccFragments = [
raw_target_code("\t}\n")
]
),
StartingFragments = list.condense([
[raw_target_code("{\n")],
HashDefineAllocId,
HashDefineProcLabel,
ArgDecls,
[raw_target_code("\tMR_bool SUCCESS_INDICATOR;\n"),
raw_target_code("\n")],
AssignInputs,
CopyTIsIn,
[raw_target_code(ObtainLock),
raw_target_code("\t\t{\n"),
user_target_code(C_Code, yes(Context)),
raw_target_code("\n\t\t;}\n")],
HashUndefAllocId,
[raw_target_code("#undef MR_PROC_LABEL\n"),
raw_target_code(ReleaseLock)],
IfSuccFragments
]),
EndingFragments = list.condense([
EndIfSuccFragments,
[target_code_output(SucceededLval),
raw_target_code(" = SUCCESS_INDICATOR;\n"),
raw_target_code("}\n")]
])
),
StartingCCode = inline_target_code(ml_target_c, StartingFragments),
StartingCCodeStmt = ml_stmt_atomic(StartingCCode, Context),
EndingCCode = inline_target_code(ml_target_c, EndingFragments),
EndingCCodeStmt = ml_stmt_atomic(EndingCCode, Context),
Stmts = [StartingCCodeStmt | ConvStmts] ++ [EndingCCodeStmt],
Decls = ConvDecls.
% Generate code fragments to obtain and release the global lock
% (this is used for ensuring thread safety in a concurrent implementation).
%
:- pred ml_gen_obtain_release_global_lock(ml_gen_info::in,
proc_thread_safe::in, pred_id::in, string::out, string::out) is det.
ml_gen_obtain_release_global_lock(Info, ThreadSafe, PredId,
ObtainLock, ReleaseLock) :-
ml_gen_info_get_module_info(Info, ModuleInfo),
module_info_get_globals(ModuleInfo, Globals),
globals.lookup_bool_option(Globals, parallel, Parallel),
( if
Parallel = yes,
ThreadSafe = proc_not_thread_safe
then
module_info_pred_info(ModuleInfo, PredId, PredInfo),
Name = pred_info_name(PredInfo),
MangledName = quote_string_c(Name),
ObtainLock = "\tMR_OBTAIN_GLOBAL_LOCK(" ++ MangledName ++ ");\n",
ReleaseLock = "\tMR_RELEASE_GLOBAL_LOCK(" ++ MangledName ++ ");\n"
else
ObtainLock = "",
ReleaseLock = ""
).
:- pred ml_gen_hash_define_mr_alloc_id(list(string)::in, prog_context::in,
list(target_code_component)::out, list(target_code_component)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_hash_define_mr_alloc_id(C_Codes, Context, HashDefine, HashUndef,
!Info) :-
ml_gen_info_get_globals(!.Info, Globals),
globals.lookup_bool_option(Globals, profile_memory, ProfileMemory),
( if
ProfileMemory = yes,
list.member(C_Code, C_Codes),
string.sub_string_search(C_Code, "MR_ALLOC_ID", _)
then
ml_gen_info_get_module_info(!.Info, ModuleInfo),
ml_gen_info_get_pred_proc_id(!.Info, PredProcId),
ml_gen_proc_label(ModuleInfo, PredProcId, _Module, ProcLabel),
ml_gen_info_get_global_data(!.Info, GlobalData0),
ml_gen_alloc_site(mlds_function_name(ProcLabel), no, 0, Context,
AllocId, GlobalData0, GlobalData),
ml_gen_info_set_global_data(GlobalData, !Info),
HashDefine = [
raw_target_code("#define MR_ALLOC_ID "),
target_code_alloc_id(AllocId),
raw_target_code("\n")],
HashUndef = [raw_target_code("#undef MR_ALLOC_ID\n")]
else
HashDefine = [],
HashUndef = []
).
:- pred ml_gen_hash_define_mr_proc_label(ml_gen_info::in,
list(target_code_component)::out) is det.
ml_gen_hash_define_mr_proc_label(Info, HashDefine) :-
ml_gen_info_get_module_info(Info, ModuleInfo),
% Note that we use the pred_id and proc_id of the current procedure,
% not the one that the foreign_code originally came from.
% There may not be any function address for the latter, e.g. if it
% has been inlined and the original definition optimized away.
ml_gen_info_get_pred_proc_id(Info, PredProcId),
ml_gen_proc_label(ModuleInfo, PredProcId, Module, PlainFuncName),
HashDefine = [raw_target_code("#define MR_PROC_LABEL "),
target_code_function_name(
qual_function_name(Module, mlds_function_name(PlainFuncName))),
raw_target_code("\n")].
%---------------------------------------------------------------------------%
% ml_gen_foreign_proc_c_decls generates C code to declare the arguments
% of a foreign_proc.
%
:- pred ml_gen_foreign_proc_c_decls(ml_gen_info::in, foreign_language::in,
string::in, int::in, int::in, list(foreign_arg)::in,
list(target_code_component)::out, list(target_code_component)::out,
list(target_code_component)::out) is det.
ml_gen_foreign_proc_c_decls(_, _, _, _, _, [], [], [], []).
ml_gen_foreign_proc_c_decls(Info, Lang, Code, !.TIIn, !.TIOut,
[HeadArg | TailArgs], Decls, TICopyIns, TICopyOuts) :-
ml_gen_foreign_proc_c_decl(Info, Lang, Code, !TIIn, !TIOut,
HeadArg, HeadDecls, HeadTICopyIns, HeadTICopyOuts),
ml_gen_foreign_proc_c_decls(Info, Lang, Code, !.TIIn, !.TIOut,
TailArgs, TailDecls, TailTICopyIns, TailTICopyOuts),
Decls = HeadDecls ++ TailDecls,
TICopyIns = HeadTICopyIns ++ TailTICopyIns,
TICopyOuts = HeadTICopyOuts ++ TailTICopyOuts.
% ml_gen_foreign_proc_c_decl generates C code to declare an argument
% of a foreign_proc.
%
:- pred ml_gen_foreign_proc_c_decl(ml_gen_info::in, foreign_language::in,
string::in, int::in, int::out, int::in, int::out, foreign_arg::in,
list(target_code_component)::out, list(target_code_component)::out,
list(target_code_component)::out) is det.
ml_gen_foreign_proc_c_decl(Info, Lang, Code, !TIIn, !TIOut,
Arg, Decls, TICopyIns, TICopyOuts) :-
% Keep the structure of this code in sync with
% ml_gen_foreign_proc_csharp_java_decl.
Arg = foreign_arg(Var, MaybeNameAndMode, Type, BoxPolicy),
ml_gen_info_get_module_info(Info, ModuleInfo),
( if
MaybeNameAndMode = yes(foreign_arg_name_mode(ArgName, Mode)),
not var_is_singleton(ArgName)
then
(
BoxPolicy = bp_always_boxed,
TypeString = "MR_Word"
;
BoxPolicy = bp_native_if_possible,
TypeString = exported_type_to_string(ModuleInfo, Lang, Type)
),
string.format("\t%s %s;\n", [s(TypeString), s(ArgName)], ArgDecl),
( if ml_is_comp_gen_type_info_arg(Info, Var, ArgName) then
% For a transition period, we make compiler-generate type_info
% arguments visible in Code in two variables:
%
% - ArgName, whose name is given by the HLDS, which (for now)
% uses the old naming scheme (TypeInfo_for_<TypeVarName>), and
%
% - SeqArgName, whose name is given by the new naming scheme
% (TypeInfo_{In,Out}_<SeqNum>).
( if mode_to_top_functor_mode(ModuleInfo, Mode, Type, top_in) then
string.format("TypeInfo_In_%d", [i(!.TIIn)], SeqArgName),
!:TIIn = !.TIIn + 1,
% For inputs, ml_gen_foreign_proc_ccsj_input_args defines
% the name given in the HLDS, i.e. ArgName, so we copy
% ArgName to SeqArgName.
string.format("\t%s = %s;\n", [s(SeqArgName), s(ArgName)],
TICopyIn),
TICopyIns = [raw_target_code(TICopyIn)],
TICopyOuts = []
else
string.format("TypeInfo_Out_%d", [i(!.TIOut)], SeqArgName),
!:TIOut = !.TIOut + 1,
% For outputs, the value is given by Code. Before the
% transition to the new scheme, variable names following
% the new naming scheme should not appear in Code.
% (Theoretically, they could, but none appear in *our* code,
% and if they appear in anyone else's, they qualify as
% implementors, which means they are on their own.)
% So in this case, we assign the ArgName computed by Code
% to SeqArgName, so that a later version of the compiler
% could take the value of the corresponding HLDS variable
% from there.
%
% After Code has been updated to assign to SeqArgName,
% we assign it to ArgName, because the later version of
% the compiler mentioned above may not have arrived yet.
% (For now, updated code will still have to mention ArgName,
% probably in a comment, to avoid a singleton variable warning
% from check_typeinfo_for_existq_tvar.)
( if string.sub_string_search(Code, SeqArgName, _Index) then
% SeqArgName occurs in Code, so assign it to ArgName.
string.format("\t%s = %s;\n", [s(ArgName), s(SeqArgName)],
TICopyOut)
else
% SeqArgName does not occur in Code, so assign ArgName
% to it.
string.format("\t%s = %s;\n", [s(SeqArgName), s(ArgName)],
TICopyOut)
),
TICopyIns = [],
TICopyOuts = [raw_target_code(TICopyOut)]
),
string.format("\t%s %s;\n", [s(TypeString), s(SeqArgName)],
SeqArgDecl),
Decls = [raw_target_code(ArgDecl ++ SeqArgDecl)]
else
Decls = [raw_target_code(ArgDecl)],
TICopyIns = [],
TICopyOuts = []
)
else
% If the variable doesn't occur in the ArgNames list,
% it can't be used, so we just ignore it.
Decls = [],
TICopyIns = [],
TICopyOuts = []
).
%---------------------------------------------------------------------------%
% The foreign code generated to implement mutable variables requires
% special case treatment, enabled by passing `mutable_special_case'.
%
:- type mutable_special_case
---> mutable_special_case
; not_mutable_special_case.
% ml_gen_foreign_proc_csharp_java_decls generates C# or Java code
% to declare the arguments for a foreign_proc.
%
:- pred ml_gen_foreign_proc_csharp_java_decls(ml_gen_info::in,
mutable_special_case::in, string::in, int::in, int::in,
list(foreign_arg)::in, list(target_code_component)::out,
list(target_code_component)::out, list(target_code_component)::out) is det.
ml_gen_foreign_proc_csharp_java_decls(_, _, _, _, _, [], [], [], []).
ml_gen_foreign_proc_csharp_java_decls(Info, MutableSpecial, Code,
!.TIIn, !.TIOut, [HeadArg | TailArgs], Decls, TICopyIns, TICopyOuts) :-
ml_gen_foreign_proc_csharp_java_decl(Info, MutableSpecial, Code,
!TIIn, !TIOut, HeadArg, HeadDecls, HeadTICopyIns, HeadTICopyOuts),
ml_gen_foreign_proc_csharp_java_decls(Info, MutableSpecial, Code,
!.TIIn, !.TIOut, TailArgs, TailDecls, TailTICopyIns, TailTICopyOuts),
Decls = HeadDecls ++ TailDecls,
TICopyIns = HeadTICopyIns ++ TailTICopyIns,
TICopyOuts = HeadTICopyOuts ++ TailTICopyOuts.
% ml_gen_foreign_proc_csharp_java_decl generates C# or Java code
% to declare an argument of a foreign_proc.
%
:- pred ml_gen_foreign_proc_csharp_java_decl(ml_gen_info::in,
mutable_special_case::in, string::in, int::in, int::out, int::in, int::out,
foreign_arg::in, list(target_code_component)::out,
list(target_code_component)::out, list(target_code_component)::out) is det.
ml_gen_foreign_proc_csharp_java_decl(Info, MutableSpecial, Code,
!TIIn, !TIOut, Arg, Decls, TICopyIns, TICopyOuts) :-
% Keep the structure of this code in sync with
% ml_gen_foreign_proc_c_decl. Any documentation there
% applies here as well.
Arg = foreign_arg(Var, MaybeNameAndMode, Type, _BoxPolicy),
ml_gen_info_get_module_info(Info, ModuleInfo),
( if
MaybeNameAndMode = yes(foreign_arg_name_mode(ArgName, Mode)),
not var_is_singleton(ArgName)
then
(
MutableSpecial = not_mutable_special_case,
MLDS_Type = mercury_type_to_mlds_type(ModuleInfo, Type)
;
MutableSpecial = mutable_special_case,
% The code for mutables is generated in the frontend.
% XXX does this code need to be updated for the other
% integer types?
( if Type = int_type then
MLDS_Type = mlds_builtin_type_int(int_type_int)
else
MLDS_Type = mlds_generic_type
)
),
TypeDecl = target_code_type(MLDS_Type),
string.format(" %s;\n", [s(ArgName)], ArgDeclStr),
ArgDecl = raw_target_code(ArgDeclStr),
( if ml_is_comp_gen_type_info_arg(Info, Var, ArgName) then
( if mode_to_top_functor_mode(ModuleInfo, Mode, Type, top_in) then
string.format("TypeInfo_In_%d", [i(!.TIIn)], SeqArgName),
!:TIIn = !.TIIn + 1,
string.format(" %s;\n", [s(SeqArgName)], SeqDeclStr),
SeqDecl = raw_target_code(SeqDeclStr),
Decls = [TypeDecl, ArgDecl, TypeDecl, SeqDecl],
string.format("\t%s = %s;\n", [s(SeqArgName), s(ArgName)],
TICopyIn),
TICopyIns = [raw_target_code(TICopyIn)],
TICopyOuts = []
else
string.format("TypeInfo_Out_%d", [i(!.TIOut)], SeqArgName),
!:TIOut = !.TIOut + 1,
string.format(" %s;\n", [s(SeqArgName)], SeqDeclStr),
SeqDecl = raw_target_code(SeqDeclStr),
Decls = [TypeDecl, ArgDecl, TypeDecl, SeqDecl],
( if string.sub_string_search(Code, SeqArgName, _Index) then
string.format("\t%s = %s;\n", [s(ArgName), s(SeqArgName)],
TICopyOut)
else
string.format("\t%s = %s;\n", [s(SeqArgName), s(ArgName)],
TICopyOut)
),
TICopyIns = [],
TICopyOuts = [raw_target_code(TICopyOut)]
)
else
Decls = [TypeDecl, ArgDecl],
TICopyIns = [],
TICopyOuts = []
)
else
% If the variable doesn't occur in the ArgNames list,
% it can't be used, so we just ignore it.
Decls = [],
TICopyIns = [],
TICopyOuts = []
).
:- pred ml_is_comp_gen_type_info_arg(ml_gen_info::in, prog_var::in,
string::in) is semidet.
ml_is_comp_gen_type_info_arg(Info, Var, ArgName) :-
% This predicate and is_comp_gen_type_info_arg should be kept in sync.
string.prefix(ArgName, "TypeInfo_for_"),
ml_gen_info_get_var_table(Info, VarTable),
lookup_var_entry(VarTable, Var, Entry),
Entry ^ vte_type = defined_type(TypeCtorSymName, [], kind_star),
TypeCtorSymName = qualified(TypeCtorModuleName, TypeCtorName),
TypeCtorModuleName = mercury_private_builtin_module,
TypeCtorName = "type_info".
%---------------------------------------------------------------------------%
% var_is_singleton determines whether or not a given foreign_proc variable
% is singleton (i.e. starts with an underscore)
%
% Singleton vars should be ignored when generating the declarations for
% foreign_proc arguments because:
%
% - they should not appear in the foreign language code
% - they could clash with the system name space
%
:- pred var_is_singleton(string::in) is semidet.
var_is_singleton(Name) :-
string.first_char(Name, '_', _).
%---------------------------------------------------------------------------%
% For C, C# and Java.
%
:- pred ml_gen_foreign_proc_ccsj_input_args(ml_gen_info::in,
foreign_language::in,
list(foreign_arg)::in, list(target_code_component)::out) is det.
ml_gen_foreign_proc_ccsj_input_args(Info, Lang, Args, AssignInputs) :-
list.map(ml_gen_foreign_proc_ccsj_input_arg_if_used(Info, Lang),
Args, AssignInputsList),
list.condense(AssignInputsList, AssignInputs).
% ml_gen_foreign_proc_c_input_arg_if_used generates C, C# or Java code
% to assign the value of an input arg for a foreign_proc.
%
:- pred ml_gen_foreign_proc_ccsj_input_arg_if_used(ml_gen_info::in,
foreign_language::in, foreign_arg::in, list(target_code_component)::out)
is det.
ml_gen_foreign_proc_ccsj_input_arg_if_used(Info, Lang, ForeignArg,
AssignInput) :-
ml_gen_info_get_module_info(Info, ModuleInfo),
ForeignArg = foreign_arg(Var, MaybeNameAndMode, OrigType, BoxPolicy),
( if
MaybeNameAndMode = yes(foreign_arg_name_mode(ArgName, Mode)),
not var_is_singleton(ArgName),
mode_to_top_functor_mode(ModuleInfo, Mode, OrigType, top_in)
then
ml_gen_foreign_proc_ccsj_gen_input_arg(Info, Lang, Var, ArgName,
OrigType, BoxPolicy, AssignInput)
else
% If the variable doesn't occur in the ArgNames list,
% it can't be used, so we just ignore it.
AssignInput = []
).
:- pred ml_gen_foreign_proc_ccsj_gen_input_arg(ml_gen_info::in,
foreign_language::in, prog_var::in, string::in, mer_type::in,
box_policy::in, list(target_code_component)::out) is det.
ml_gen_foreign_proc_ccsj_gen_input_arg(Info, Lang, Var, ArgName, OrigType,
BoxPolicy, AssignInput) :-
ml_gen_info_get_var_table(Info, VarTable),
lookup_var_entry(VarTable, Var, VarEntry),
VarEntry = vte(_, VarType, IsDummy),
ml_gen_var(Info, Var, VarEntry, VarLval),
ml_gen_info_get_module_info(Info, ModuleInfo),
(
IsDummy = is_dummy_type,
% The variable may not have been declared, so we need to generate
% a dummy value for it. Using a constant here is more efficient than
% using private_builtin.dummy_var, which is what ml_gen_var will have
% generated for this variable.
ArgRval = dummy_arg_rval(ModuleInfo, Lang, VarType)
;
IsDummy = is_not_dummy_type,
ml_gen_box_or_unbox_rval(ModuleInfo, VarType, OrigType, BoxPolicy,
ml_lval(VarLval), ArgRval)
),
% At this point we have an rval with the right type for *internal* use
% in the code generated by the Mercury compiler's MLDS back-end. We need
% to convert this to the appropriate type to use for the C interface.
MaybeForeignType = is_this_a_foreign_type(ModuleInfo, OrigType),
TypeString =
maybe_foreign_type_to_string(Lang, OrigType, MaybeForeignType),
ml_gen_info_get_high_level_data(Info, HighLevelData),
( if
input_arg_assignable_with_cast(Lang, HighLevelData, OrigType,
MaybeForeignType, TypeString, Cast)
then
% In the usual case, we can just use an assignment and perhaps a cast.
string.format("\t%s = %s", [s(ArgName), s(Cast)], AssignToArgName),
AssignInput = [
raw_target_code(AssignToArgName),
target_code_input(ArgRval),
raw_target_code(";\n")
]
else
% For foreign types (without the `can_pass_as_mercury_type' assertion)
% we need to call MR_MAYBE_UNBOX_FOREIGN_TYPE.
AssignInput = [
raw_target_code("\tMR_MAYBE_UNBOX_FOREIGN_TYPE("
++ TypeString ++ ", "),
target_code_input(ArgRval),
raw_target_code(", " ++ ArgName ++ ");\n")
]
).
:- func dummy_arg_rval(module_info, foreign_language, mer_type) = mlds_rval.
dummy_arg_rval(ModuleInfo, Lang, Type) = Rval :-
( if Lang = lang_java then
MLDS_Type = mercury_type_to_mlds_type(ModuleInfo, Type),
Rval = ml_const(mlconst_null(MLDS_Type))
else
Rval = ml_const(mlconst_int(0))
).
:- pred input_arg_assignable_with_cast(foreign_language::in, bool::in,
mer_type::in, maybe(foreign_type_and_assertions)::in,
string::in, string::out) is semidet.
input_arg_assignable_with_cast(Lang, HighLevelData,
OrigType, MaybeForeignType, TypeString, Cast) :-
(
Lang = lang_c,
HighLevelData = yes,
(
MaybeForeignType = yes(ForeignType),
ForeignType = foreign_type_and_assertions(_, Assertions),
asserted_can_pass_as_mercury_type(Assertions)
;
MaybeForeignType = no
),
% In general, the types used for the C interface are not the same as
% the types used by --high-level-data, so we always use a cast here.
% (Strictly speaking the cast is not needed for a few cases like `int',
% but it doesn't do any harm.)
Cast = "(" ++ TypeString ++ ") "
;
Lang = lang_c,
HighLevelData = no,
( if OrigType = type_variable(_, _) then
% For --no-high-level-data, we only need to use a cast for
% polymorphic types, which are `MR_Word' in the C interface but
% `MR_Box' in the MLDS back-end.
Cast = "(MR_Word) "
else
(
MaybeForeignType = yes(ForeignType),
ForeignType = foreign_type_and_assertions(_, Assertions),
asserted_can_pass_as_mercury_type(Assertions),
Cast = "(" ++ TypeString ++ ") "
;
MaybeForeignType = no,
Cast = ""
)
)
;
Lang = lang_java,
% There is no difference between types used by the foreign interface
% and the generated code.
Cast = ""
;
Lang = lang_csharp,
Cast = ""
).
:- pred ml_gen_foreign_proc_csharp_java_output_args(mutable_special_case::in,
list(foreign_arg)::in, prog_context::in, list(mlds_stmt)::out,
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_foreign_proc_csharp_java_output_args(_, [], _, [], [], [], !Info).
ml_gen_foreign_proc_csharp_java_output_args(MutableSpecial,
[JavaArg | JavaArgs], Context, Stmts, ConvDecls, ConvStmts, !Info) :-
ml_gen_foreign_proc_csharp_java_output_arg(MutableSpecial, JavaArg,
Context, Stmts1, ConvDecls1, ConvStmts1, !Info),
ml_gen_foreign_proc_csharp_java_output_args(MutableSpecial, JavaArgs,
Context, Stmts2, ConvDecls2, ConvStmts2, !Info),
Stmts = Stmts1 ++ Stmts2,
ConvDecls = ConvDecls1 ++ ConvDecls2,
ConvStmts = ConvStmts1 ++ ConvStmts2.
% ml_gen_foreign_proc_csharp_java_output_arg generates MLDS statements
% to assign the value of an output arg for a foreign_proc.
%
:- pred ml_gen_foreign_proc_csharp_java_output_arg(mutable_special_case::in,
foreign_arg::in, prog_context::in, list(mlds_stmt)::out,
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_foreign_proc_csharp_java_output_arg(MutableSpecial, ForeignArg, Context,
AssignOutput, ConvDecls, ConvOutputStmts, !Info) :-
ForeignArg = foreign_arg(Var, MaybeNameAndMode, OrigType, BoxPolicy),
ml_gen_info_get_module_info(!.Info, ModuleInfo),
( if
MaybeNameAndMode = yes(foreign_arg_name_mode(ArgName, Mode)),
not var_is_singleton(ArgName),
is_type_a_dummy(ModuleInfo, OrigType) = is_not_dummy_type,
mode_to_top_functor_mode(ModuleInfo, Mode, OrigType, top_out)
then
% Create a target lval with the right type for *internal* use in the
% code generated by the Mercury compiler's MLDS back-end.
ml_gen_info_get_var_table(!.Info, VarTable),
lookup_var_entry(VarTable, Var, VarEntry),
ml_gen_var(!.Info, Var, VarEntry, VarLval),
VarType = VarEntry ^ vte_type,
NonMangledArgVarName = lvn_prog_var_foreign(ArgName),
ml_gen_box_or_unbox_lval(VarType, OrigType, BoxPolicy,
VarLval, NonMangledArgVarName, Context, no, 0,
ArgLval, ConvDecls, _ConvInputStmts, ConvOutputStmts, !Info),
MLDSType = mercury_type_to_mlds_type(ModuleInfo, OrigType),
LocalVarLval = ml_local_var(NonMangledArgVarName, MLDSType),
(
MutableSpecial = not_mutable_special_case,
Rval = ml_lval(LocalVarLval)
;
MutableSpecial = mutable_special_case,
% The code for mutables is generated in the frontend.
( if OrigType = int_type then
Rval = ml_lval(LocalVarLval)
else
Rval = ml_unbox(MLDSType, ml_lval(LocalVarLval))
)
),
AssignOutput = [ml_gen_assign(ArgLval, Rval, Context)]
else
% If the variable doesn't occur in the ArgNames list,
% it can't be used, so we just ignore it.
AssignOutput = [],
ConvDecls = [],
ConvOutputStmts = []
).
:- pred ml_gen_foreign_proc_c_output_args(list(foreign_arg)::in,
prog_context::in, list(target_code_component)::out,
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_foreign_proc_c_output_args([], _, [], [], [], !Info).
ml_gen_foreign_proc_c_output_args([ForeignArg | ForeignArgs], Context,
Components, ConvDecls, ConvStmts, !Info) :-
ml_gen_foreign_proc_c_output_arg(ForeignArg, Context,
Components1, ConvDecls1, ConvStmts1, !Info),
ml_gen_foreign_proc_c_output_args(ForeignArgs, Context,
Components2, ConvDecls2, ConvStmts2, !Info),
Components = Components1 ++ Components2,
ConvDecls = ConvDecls1 ++ ConvDecls2,
ConvStmts = ConvStmts1 ++ ConvStmts2.
% ml_gen_foreign_proc_c_output_arg generates C code to assign the value of
% an output arg for a foreign_proc.
%
:- pred ml_gen_foreign_proc_c_output_arg(foreign_arg::in,
prog_context::in, list(target_code_component)::out,
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_foreign_proc_c_output_arg(Arg, Context, AssignOutput, ConvDecls,
ConvOutputStmts, !Info) :-
Arg = foreign_arg(Var, MaybeNameAndMode, OrigType, BoxPolicy),
ml_gen_info_get_module_info(!.Info, ModuleInfo),
( if
MaybeNameAndMode = yes(foreign_arg_name_mode(ArgName, Mode)),
not var_is_singleton(ArgName),
is_type_a_dummy(ModuleInfo, OrigType) = is_not_dummy_type,
mode_to_top_functor_mode(ModuleInfo, Mode, OrigType, top_out)
then
ml_gen_foreign_proc_c_gen_output_arg(Var, ArgName, OrigType, BoxPolicy,
Context, AssignOutput, ConvDecls, ConvOutputStmts, !Info)
else
% If the variable doesn't occur in the ArgNames list,
% it can't be used, so we just ignore it.
AssignOutput = [],
ConvDecls = [],
ConvOutputStmts = []
).
:- pred ml_gen_foreign_proc_c_gen_output_arg(prog_var::in,
string::in, mer_type::in, box_policy::in, prog_context::in,
list(target_code_component)::out,
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_foreign_proc_c_gen_output_arg(Var, ArgName, OrigType, BoxPolicy,
Context, AssignOutput, ConvDecls, ConvOutputStmts, !Info) :-
ml_gen_info_get_var_table(!.Info, VarTable),
lookup_var_entry(VarTable, Var, VarEntry),
VarEntry = vte(_, VarType, _),
ml_gen_var(!.Info, Var, VarEntry, VarLval),
NonMangledArgVarName = lvn_prog_var_foreign(ArgName),
ml_gen_box_or_unbox_lval(VarType, OrigType, BoxPolicy, VarLval,
NonMangledArgVarName, Context, no, 0, ArgLval,
ConvDecls, _ConvInputStmts, ConvOutputStmts, !Info),
% At this point we have an lval with the right type for *internal* use
% in the code generated by the Mercury compiler's MLDS back-end. We need
% to convert this to the appropriate type to use for the C interface.
ml_gen_info_get_module_info(!.Info, ModuleInfo),
MaybeForeignType = is_this_a_foreign_type(ModuleInfo, OrigType),
TypeString = maybe_foreign_type_to_c_string(OrigType, MaybeForeignType),
( if
(
MaybeForeignType = no,
Cast = no
;
MaybeForeignType = yes(ForeignType),
ForeignType = foreign_type_and_assertions(_, Assertions),
asserted_can_pass_as_mercury_type(Assertions),
Cast = yes
)
then
% In the usual case, we can just use an assignment,
% perhaps with a cast.
ml_gen_info_get_high_level_data(!.Info, HighLevelData),
(
HighLevelData = yes,
% In general, the types used for the C interface are not the same
% as the types used by --high-level-data, so we always use a cast
% here. (Strictly speaking the cast is not needed for a few cases
% like `int', but it doesn't do any harm.) Note that we can't
% easily obtain the type string for the RHS of the assignment,
% so instead we cast the LHS.
LHS_Cast = string.format("* (%s *) &", [s(TypeString)]),
RHS_Cast = ""
;
HighLevelData = no,
% For --no-high-level-data, we only need to use a cast for
% polymorphic types, which are `MR_Word' in the C interface but
% `MR_Box' in the MLDS back-end.
( if
( OrigType = type_variable(_, _)
; Cast = yes
)
then
RHS_Cast = "(MR_Box) "
else
RHS_Cast = ""
),
LHS_Cast = ""
),
string.format(" = %s%s;\n", [s(RHS_Cast), s(ArgName)],
AssignFromArgName),
string.format("\t%s", [s(LHS_Cast)], AssignTo),
AssignOutput = [
raw_target_code(AssignTo),
target_code_output(ArgLval),
raw_target_code(AssignFromArgName)
]
else
% For foreign types, we need to call MR_MAYBE_BOX_FOREIGN_TYPE.
AssignOutput = [
raw_target_code("\tMR_MAYBE_BOX_FOREIGN_TYPE("
++ TypeString ++ ", " ++ ArgName ++ ", "),
target_code_output(ArgLval),
raw_target_code(");\n")
]
).
%---------------------------------------------------------------------------%
:- end_module ml_backend.ml_foreign_proc_gen.
%---------------------------------------------------------------------------%