mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-05-01 17:24:34 +00:00
compiler/Mercury.options:
Delete a workaround we needed only for Erlang.
compiler/add_mutable_aux_preds.m:
Delete the implementation of mutables for Erlang.
compiler/builtin_ops.m:
Document the fact that the Erlang backend was the only user of
two operations.
compiler/compile_target_code.m:
compiler/module_cmds.m:
Delete the predicates that handled the compilation of Erlang code.
compiler/file_names.m:
Delete code dealing with file names used only by the Erlang backend.
compiler/options.m:
Delete the old internal-only order_constructors_for_erlang option.
Add an XXX about another option intended for Erlang being unused.
Leave the other erlang-related options alive for now, to avoid breaking
Mmakefiles, Mercury.options files etc that may still refer to them.
Delete references to Erlang in help and/or error messages.
compiler/handle_options.m:
Don't both updating options that were used only by the Erlang backend,
and which are now unused.
Delete references to Erlang in help and/or error messages.
compiler/unify_proc.m:
Delete the code handling the Erlang-specific option deleted from options.m.
compiler/check_libgrades.m:
compiler/delay_partial_inst.m:
compiler/llds_out_data.m:
compiler/make_hlds_passes.m:
compiler/mlds_to_c_data.m:
compiler/prog_item.m:
compiler/simplify_goal_call.m:
compiler/write_deps_file.m:
Either delete comments referring to Erlang or the Erlang backend,
or, where their existence was the motivation for some design decisions,
shift the comments to the past tense.
tests/mmc_make/Mmakefile:
Delete a reference to a recently deleted .hrl file.
1867 lines
73 KiB
Mathematica
1867 lines
73 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2015 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.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% This module creates both the public predicates users use to access mutables,
|
|
% and the private auxiliary predicates needed to make that possible.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Mutables are implemented as a source-to-source transformation on the
|
|
% parse tree. The transformation depends on the compilation target.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% C BACKENDS
|
|
%
|
|
% For non-constant mutables the transformation is as follows:
|
|
%
|
|
% :- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [attributes]).
|
|
%
|
|
% ===>
|
|
%
|
|
% :- pragma foreign_decl("C", "
|
|
% extern <CType> mutable_<varname>;
|
|
% #ifdef MR_THREAD_SAFE
|
|
% extern MercuryLock mutable_<varname>_lock;
|
|
% #endif
|
|
%
|
|
% ").
|
|
%
|
|
% :- pragma foreign_code("C", "
|
|
% <CType> mutable_<varname>;
|
|
% #ifdef MR_THREAD_SAFE
|
|
% MercuryLock mutable_<varname>_lock;
|
|
% #endif
|
|
% ").
|
|
%
|
|
% NOTES:
|
|
%
|
|
% * The name of the C global corresponding to mutable_<varname> may be
|
|
% mangled.
|
|
%
|
|
% * <CType> is chosen on a backend-specific basis. If the value stored
|
|
% in the mutable is always boxed it is `MR_Word' otherwise it may
|
|
% be some native type, `MR_Integer', `MR_Float' etc.
|
|
%
|
|
% :- initialise initialise_mutable_<varname>/0.
|
|
%
|
|
% :- impure pred initialise_mutable_<varname> is det.
|
|
%
|
|
% initialise_mutable_<varname> :-
|
|
% impure pre_initialise_mutable_<varname>,
|
|
% impure X = <initval>,
|
|
% impure set_<varname>(X).
|
|
%
|
|
% :- impure pred pre_initialise_mutable_<varname> is det.
|
|
% :- pragma foreign_proc("C",
|
|
% pre_initialise_mutable_<varname>,
|
|
% [will_not_call_mercury],
|
|
% "
|
|
% #ifdef MR_THREAD_SAFE
|
|
% pthread_init_mutex(&mutable_<varname>_lock, MR_MUTEX_ATTR);
|
|
% #endif
|
|
% ").
|
|
%
|
|
% Operations on mutables are defined in terms of the following four predicates.
|
|
% Note that they are all marked `thread_safe' in order to avoid having
|
|
% to acquire the global lock.
|
|
%
|
|
% :- impure pred unsafe_set_<varname>(<vartype>::in(<varinst>)) is det.
|
|
% :- pragma foreign_proc("C",
|
|
% unsafe_set_<varname>(X::in(<varinst>)),
|
|
% [will_not_call_mercury, thread_safe],
|
|
% "
|
|
% mutable_<varname> = X;
|
|
% ").
|
|
%
|
|
% :- semipure pred unsafe_get_<varname>(<vartype>::out(<varinst>)) is det.
|
|
% :- pragma foreign_proc("C",
|
|
% unsafe_get_<varname>(X::out(<varinst>)),
|
|
% [promise_semipure, will_not_call_mercury, thread_safe],
|
|
% "
|
|
% X = mutable_<varname>;
|
|
% ").
|
|
%
|
|
% :- impure lock_<varname> is det.
|
|
% :- pragma foreign_proc("C",
|
|
% lock_<varname>,
|
|
% [will_not_call_mercury, promise_pure],
|
|
% "
|
|
% #ifdef MR_THREAD_SAFE
|
|
% MR_LOCK(&mutable_<varname>_lock, \"lock_<varname>/0\");
|
|
% #endif
|
|
% ").
|
|
%
|
|
% :- impure unlock_<varname> is det.
|
|
% :- pragma foreign_proc("C",
|
|
% unlock_<varname>,
|
|
% [will_not_call_mercury, promise_pure],
|
|
% "
|
|
% #ifdef MR_THREAD_SAFE
|
|
% MR_UNLOCK(&mutable_<varname>_lock, \"unlock_<varname>/0\");
|
|
% #endif
|
|
% ").
|
|
%
|
|
% The other operations are all defined in Mercury using the above predicates:
|
|
%
|
|
% :- impure pred set_<varname>(<vartype>::in(<varinst>)) is det.
|
|
%
|
|
% set_<varname>(X) :-
|
|
% impure lock_<varname>,
|
|
% impure unsafe_set_<varname>(X),
|
|
% impure unlock_<varname>.
|
|
%
|
|
% :- semipure pred get_<varname>(<vartype>::out(<varinst>)) is det.
|
|
%
|
|
% get_<varname>(X) :-
|
|
% promise_semipure (
|
|
% impure lock_<varname>
|
|
% semipure unsafe_get_<varname>(X),
|
|
% impure unlock_<varname>
|
|
% ).
|
|
%
|
|
% etc.
|
|
%
|
|
% For thread-local mutables the transformation is as above, with the following
|
|
% differences:
|
|
%
|
|
% :- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [thread_local]).
|
|
%
|
|
% ===>
|
|
%
|
|
% :- pragma foreign_decl("C", "extern MR_Unsigned mutable_<varname>;").
|
|
% :- pragma foreign_code("C", "MR_Unsigned mutable_<varname>;").
|
|
%
|
|
% :- pragma foreign_proc("C",
|
|
% pre_initialise_mutable_<varname>,
|
|
% [will_not_call_mercury],
|
|
% "
|
|
% mutable_<varname> = MR_new_thread_local_mutable_index();
|
|
% ").
|
|
%
|
|
% :- pragma foreign_proc("C",
|
|
% unsafe_set_<varname>(X::in(<varinst>)),
|
|
% [will_not_call_mercury, thread_safe],
|
|
% "
|
|
% MR_set_thread_local_mutable(<type>, X, mutable_<varname>);
|
|
% ").
|
|
%
|
|
% :- pragma foreign_proc("C",
|
|
% unsafe_get_<varname>(X::out(<varinst>)),
|
|
% [promise_semipure, will_not_call_mercury, thread_safe],
|
|
% "
|
|
% MR_get_thread_local_mutable(<type>, X, mutable_<varname>);
|
|
% ").
|
|
%
|
|
% :- pragma foreign_proc("C",
|
|
% lock_<varname>,
|
|
% [will_not_call_mercury, promise_pure],
|
|
% "
|
|
% /* blank */
|
|
% ").
|
|
%
|
|
% :- pragma foreign_proc("C",
|
|
% unlock_<varname>,
|
|
% [will_not_call_mercury, promise_pure],
|
|
% "
|
|
% /* blank */
|
|
% ").
|
|
%
|
|
% For constant mutables the transformation is:
|
|
%
|
|
% :- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [constant]).
|
|
%
|
|
% ===>
|
|
%
|
|
% :- pragma foreign_decl("C", "extern <CType> mutable_<varname>;").
|
|
% :- pragma foreign_code("C", "<CType> mutable_<varname>;").
|
|
%
|
|
% :- pred get_<varname>(<vartype>::out(<varinst>)) is det.
|
|
% :- pragma foreign_proc("C",
|
|
% get_<varname>(X::out(<varinst>)),
|
|
% [will_not_call_mercury, promise_pure, thread_safe],
|
|
% "
|
|
% X = mutable_<varname>;
|
|
% ").
|
|
%
|
|
% In order to initialise constant mutables we generate the following:
|
|
%
|
|
% :- impure pred secret_initialization_only_set_<varname>(
|
|
% <vartype>::in(<varinst>)) is det.
|
|
%
|
|
% :- pragma foreign_proc("C",
|
|
% secret_initialization_only_set_<varname>(X::in(<varinst>)),
|
|
% [will_not_call_mercury],
|
|
% "
|
|
% mutable_<varname> = X;
|
|
% ").
|
|
%
|
|
% :- initialise initialise_mutable_<varname>/0.
|
|
%
|
|
% :- impure pred initialise_mutable_<varname> is det.
|
|
%
|
|
% initialise_mutable_<varname> :-
|
|
% impure X = <initval>,
|
|
% impure secret_initialization_only_set_<varname>(X).
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% JAVA BACKEND
|
|
%
|
|
% For non-constant mutables the transformation is as follows:
|
|
%
|
|
% :- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [attributes]).
|
|
%
|
|
% ===>
|
|
%
|
|
% :- pragma foreign_code("Java", "
|
|
% static <JType> mutable_<varname>;
|
|
% ").
|
|
%
|
|
% :- initialise initialise_mutable_<varname>/0.
|
|
%
|
|
% :- impure pred initialise_mutable_<varname> is det.
|
|
%
|
|
% initialise_mutable_<varname> :-
|
|
% impure X = <initval>,
|
|
% impure set_<varname>(X).
|
|
%
|
|
% <JType> is either `int' or `java.lang.Object' (all other types).
|
|
%
|
|
% Operations on mutables are defined in terms of the following two predicates.
|
|
% They are actually "safe": by the Java specification, 32-bit variables are
|
|
% loaded/stored atomically. Doubles and longs may be treated as two 32-bit
|
|
% variables, but Mercury does not expose them yet. The predicates are named so
|
|
% to minimise the differences with the C backends.
|
|
%
|
|
% :- impure pred unsafe_set_<varname>(<vartype>::in(<varinst>)) is det.
|
|
% :- pragma foreign_proc("Java",
|
|
% unsafe_set_<varname>(X::in(<varinst>)),
|
|
% [will_not_call_mercury, thread_safe],
|
|
% "
|
|
% mutable_<varname> = X;
|
|
% ").
|
|
%
|
|
% :- semipure pred unsafe_get_<varname>(<vartype>::out(<varinst>)) is det.
|
|
% :- pragma foreign_proc("Java",
|
|
% unsafe_get_<varname>(X::out(<varinst>)),
|
|
% [promise_semipure, will_not_call_mercury, thread_safe],
|
|
% "
|
|
% X = mutable_<varname>;
|
|
% ").
|
|
%
|
|
% If mutable_<varname> has the type `java.lang.Object' a cast is required
|
|
% after the code above, to cast X to the correct type. This is handled by
|
|
% the MLDS code generator.
|
|
%
|
|
% For thread-local mutables the transformation is as follows:
|
|
%
|
|
% :- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [attributes]).
|
|
%
|
|
% ===>
|
|
%
|
|
% :- pragma foreign_code("Java", "
|
|
% static java.lang.ThreadLocal<JType> mutable_<varname> =
|
|
% new java.lang.InheritableThreadLocal<JType>();
|
|
% ").
|
|
%
|
|
% :- pragma foreign_proc("Java",
|
|
% unsafe_set_<varname>(X::in(<varinst>)),
|
|
% [will_not_call_mercury, thread_safe],
|
|
% "
|
|
% mutable_<varname>.set(X);
|
|
% ").
|
|
%
|
|
% :- pragma foreign_proc("Java",
|
|
% unsafe_get_<varname>(X::out(<varinst>)),
|
|
% [promise_semipure, will_not_call_mercury, thread_safe],
|
|
% "
|
|
% X = mutable_<varname>.get();
|
|
% ").
|
|
%
|
|
% <JType> is `java.lang.Integer' or `java.lang.Object'.
|
|
%
|
|
% The above prediates are called by these predicates, again to minimise
|
|
% differences with the C backends:
|
|
%
|
|
% :- impure pred set_<varname>(<vartype>::in(<varinst>)) is det.
|
|
%
|
|
% set_<varname>(X) :-
|
|
% impure unsafe_set_<varname>(X).
|
|
%
|
|
% :- semipure pred get_<varname>(<vartype>::out(<varinst>)) is det.
|
|
%
|
|
% get_<varname>(X) :-
|
|
% semipure unsafe_get_<varname>(X).
|
|
%
|
|
% For constant mutables the transformation is:
|
|
%
|
|
% :- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [constant]).
|
|
%
|
|
% ===>
|
|
%
|
|
% :- pragma foreign_code("Java", "
|
|
% static <JType> mutable_<varname>;
|
|
% ").
|
|
%
|
|
% :- pred get_<varname>(<vartype>::out(<varinst>)) is det.
|
|
% :- pragma foreign_proc("Java",
|
|
% get_<varname>(X::out(<varinst>)),
|
|
% [will_not_call_mercury, promise_pure, thread_safe],
|
|
% "
|
|
% X = mutable_<varname>;
|
|
% ").
|
|
%
|
|
% In order to initialise constant mutables we generate the following:
|
|
%
|
|
% :- impure pred secret_initialization_only_set_<varname>(
|
|
% <vartype>::in(<varinst>)) is det.
|
|
%
|
|
% :- pragma foreign_proc("Java",
|
|
% secret_initialization_only_set_<varname>(X::in(<varinst>)),
|
|
% [will_not_call_mercury],
|
|
% "
|
|
% mutable_<varname> = X;
|
|
% ").
|
|
%
|
|
% :- initialise initialise_mutable_<varname>/0.
|
|
%
|
|
% :- impure pred initialise_mutable_<varname> is det.
|
|
%
|
|
% initialise_mutable_<varname> :-
|
|
% impure X = <initval>,
|
|
% impure secret_initialization_only_set_<varname>(X).
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% C# BACKEND
|
|
%
|
|
% The C# implementation is analogous to the Java implementation, except for
|
|
% thread-local mutables, which are transformed as follows:
|
|
%
|
|
% :- mutable(<varname>, <vartype>, <initvalue>, <varinst>, [attributes]).
|
|
%
|
|
% ===>
|
|
%
|
|
% :- pragma foreign_code("C#", "
|
|
% private static int mutable_<varname>;
|
|
% ").
|
|
%
|
|
% :- initialise initialise_mutable_<varname>/0.
|
|
%
|
|
% :- impure pred initialise_mutable_<varname> is det.
|
|
%
|
|
% initialise_mutable_<varname> :-
|
|
% impure pre_initialise_mutable_<varname>,
|
|
% impure X = <initvalue>,
|
|
% impure set_<varname>(X).
|
|
%
|
|
% :- pragma foreign_proc("C#",
|
|
% pre_initialise_mutable_<varname>,
|
|
% [will_not_call_mercury],
|
|
% "
|
|
% mutable_<varname> = runtime.ThreadLocalMutables.new_index();
|
|
% ").
|
|
%
|
|
% :- pragma foreign_proc("C#",
|
|
% unsafe_set_<varname>(X::in(<varinst>)),
|
|
% [will_not_call_mercury, thread_safe],
|
|
% "
|
|
% runtime.ThreadLocalMutables.set(mutable_<varname>, X);
|
|
% ").
|
|
%
|
|
% :- pragma foreign_proc("C#",
|
|
% unsafe_get_<varname>(X::out(<varinst>)),
|
|
% [promise_semipure, will_not_call_mercury, thread_safe],
|
|
% "
|
|
% X = runtime.ThreadLocalMutables.get(mutable_<varname>);
|
|
% ").
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module hlds.make_hlds.add_mutable_aux_preds.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.make_hlds.qual_info.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.error_util.
|
|
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% If the given mutable item is local to this module, construct the
|
|
% predicate declarations of the auxiliary predicates it needs,
|
|
% and add them to the HLDS.
|
|
%
|
|
:- pred add_aux_pred_decls_for_mutable_if_local(
|
|
sec_item(item_mutable_info)::in, module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
% If the given mutable item is local to this module,
|
|
% add the definitions of its auxiliary predicates to the HLDS,
|
|
% add the (backend-specific) data structure holding the mutable's value
|
|
% to the HLDS, and arrange for this data structure to be initialized.
|
|
%
|
|
% XXX We should do this by constructing those definitions as a set of
|
|
% clauses, foreign_procs and foreign_export items, and adding those
|
|
% to the HLDS, or even better, returning those items to our caller
|
|
% for *it* to add to the HLDS. It should then be possible for us
|
|
% to construct the declarations and the definitions of those aux
|
|
% predicates at the same time.
|
|
%
|
|
:- pred add_aux_pred_defns_for_mutable_if_local(
|
|
sec_item(item_mutable_info)::in,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.foreign.
|
|
:- import_module hlds.add_pred.
|
|
:- import_module hlds.error_msg_inst.
|
|
:- import_module hlds.hlds_inst_mode.
|
|
:- import_module hlds.make_hlds.add_clause.
|
|
:- import_module hlds.make_hlds.add_foreign_proc.
|
|
:- import_module hlds.make_hlds.add_pragma.
|
|
:- 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 parse_tree.builtin_lib_types.
|
|
:- import_module parse_tree.file_names.
|
|
:- import_module parse_tree.maybe_error.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.prog_data_foreign.
|
|
:- import_module parse_tree.prog_foreign.
|
|
:- import_module parse_tree.prog_mode.
|
|
:- import_module parse_tree.prog_mutable.
|
|
|
|
:- import_module bool.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module varset.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
add_aux_pred_decls_for_mutable_if_local(SectionItem, !ModuleInfo, !Specs) :-
|
|
SectionItem = sec_item(SectionInfo, ItemMutable),
|
|
SectionInfo = sec_info(ItemMercuryStatus, NeedQual),
|
|
(
|
|
ItemMercuryStatus = item_defined_in_this_module(_),
|
|
check_mutable(ItemMutable, !.ModuleInfo, !Specs),
|
|
item_mercury_status_to_pred_status(ItemMercuryStatus, PredStatus),
|
|
add_aux_pred_decls_for_mutable(PredStatus, NeedQual,
|
|
ItemMutable, !ModuleInfo, !Specs)
|
|
;
|
|
ItemMercuryStatus = item_defined_in_other_module(_)
|
|
% We don't implement the `mutable' declaration unless it is defined
|
|
% in this module. If we did not have this check, we would duplicate
|
|
% the definition of the global variable storing the mutable
|
|
% in any submodules of the module that actually defined the mutable.
|
|
).
|
|
|
|
:- pred check_mutable(item_mutable_info::in, module_info::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_mutable(ItemMutable, ModuleInfo, !Specs) :-
|
|
ItemMutable = item_mutable_info(MutableName,
|
|
_OrigType, _Type, OrigInst, Inst,
|
|
_InitTerm, _VarSetMutable, MutAttrs, Context, _SeqNum),
|
|
|
|
% XXX We don't currently support the foreign_name attribute
|
|
% for all languages.
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_target(Globals, CompilationTarget),
|
|
(
|
|
( CompilationTarget = target_c, ForeignLanguage = lang_c
|
|
; CompilationTarget = target_java, ForeignLanguage = lang_java
|
|
; CompilationTarget = target_csharp, ForeignLanguage = lang_csharp
|
|
),
|
|
mutable_var_maybe_foreign_names(MutAttrs) = MaybeForeignNames,
|
|
(
|
|
MaybeForeignNames = no
|
|
;
|
|
MaybeForeignNames = yes(ForeignNames),
|
|
% Report any errors with the foreign_name attributes
|
|
% during this pass.
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
get_global_name_from_foreign_names(ModuleInfo, Context,
|
|
ModuleName, MutableName, ForeignLanguage, ForeignNames,
|
|
_TargetMutableName, !Specs)
|
|
)
|
|
),
|
|
|
|
% If the mutable is to be trailed, then we need to be in a trailing grade.
|
|
TrailMutableUpdates = mutable_var_trailed(MutAttrs),
|
|
globals.lookup_bool_option(Globals, use_trail, UseTrail),
|
|
( if
|
|
TrailMutableUpdates = mutable_trailed,
|
|
UseTrail = no
|
|
then
|
|
TrailPieces = [words("Error: trailed"), decl("mutable"),
|
|
words("declaration in non-trailing grade."), nl],
|
|
TrailSpec = simplest_spec($pred, severity_error,
|
|
phase_parse_tree_to_hlds, Context, TrailPieces),
|
|
!:Specs = [TrailSpec | !.Specs]
|
|
else
|
|
true
|
|
),
|
|
|
|
% Check that the inst in the mutable declaration is a valid inst
|
|
% for a mutable declaration.
|
|
% It is okay to pass a dummy varset in here since any attempt
|
|
% to use inst variables in a mutable declaration should already
|
|
% been dealt with when the mutable declaration was parsed.
|
|
DummyInstVarSet = varset.init,
|
|
check_mutable_inst(ModuleInfo, Context, DummyInstVarSet, [], Inst,
|
|
[], ExpandedInstSpecs),
|
|
(
|
|
ExpandedInstSpecs = []
|
|
;
|
|
ExpandedInstSpecs = [_ | _],
|
|
% We found some insts in Inst that are not allowed in mutables.
|
|
%
|
|
% Inst has been processed by equiv_type.m, which replaces named insts
|
|
% with the definition of the named inst. When we check it, the error
|
|
% messages we generate for any errors in it will lack information
|
|
% about what nested sequence of named inst definitions the errors is
|
|
% inside. We therefore compute the error messages on the original
|
|
% inst as well.
|
|
%
|
|
% If ExpandedInstSpecs is nonempty, then UnexpandedInstSpecs should
|
|
% be nonempty as well, but we prepare for it to be empty just in case.
|
|
check_mutable_inst(ModuleInfo, Context, DummyInstVarSet, [], OrigInst,
|
|
[], UnexpandedInstSpecs),
|
|
(
|
|
UnexpandedInstSpecs = [],
|
|
% Printing error messages without the proper context is better than
|
|
% not printing error messages at all, once we have discovered
|
|
% an error.
|
|
!:Specs = ExpandedInstSpecs ++ !.Specs
|
|
;
|
|
UnexpandedInstSpecs = [_ | _],
|
|
!:Specs = UnexpandedInstSpecs ++ !.Specs
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Add an error to !Specs for each part of the inst that isn't allowed
|
|
% inside a mutable declaration.
|
|
%
|
|
:- pred check_mutable_inst(module_info::in, prog_context::in,
|
|
inst_varset::in, list(inst_ctor)::in, mer_inst::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_mutable_inst(ModuleInfo, Context, InstVarSet, ParentInsts, Inst,
|
|
!Specs) :-
|
|
(
|
|
( Inst = any(Uniq, _)
|
|
; Inst = ground(Uniq, _)
|
|
),
|
|
check_mutable_inst_uniqueness(ModuleInfo, Context, InstVarSet,
|
|
ParentInsts, Inst, Uniq, !Specs)
|
|
;
|
|
Inst = bound(Uniq, _, BoundInsts),
|
|
check_mutable_inst_uniqueness(ModuleInfo, Context, InstVarSet,
|
|
ParentInsts, Inst, Uniq, !Specs),
|
|
check_mutable_bound_insts(ModuleInfo, Context, InstVarSet,
|
|
ParentInsts, BoundInsts, !Specs)
|
|
;
|
|
Inst = defined_inst(InstName),
|
|
(
|
|
InstName = user_inst(UserInstSymName, UserInstArgs),
|
|
list.length(UserInstArgs, UserInstArity),
|
|
UserInstCtor = inst_ctor(UserInstSymName, UserInstArity),
|
|
( if
|
|
list.member(UserInstCtor, ParentInsts)
|
|
then
|
|
true
|
|
else if
|
|
UserInstSymName =
|
|
qualified(UserInstModuleName, UserInstBaseName),
|
|
UserInstModuleName = mercury_public_builtin_module,
|
|
UserInstArity = 0,
|
|
( UserInstBaseName = "dead"
|
|
; UserInstBaseName = "mostly_dead"
|
|
)
|
|
then
|
|
FreePieces = [words("may not appear in"),
|
|
decl("mutable"), words("declarations.")],
|
|
UnqualInstName =
|
|
user_inst(unqualified(UserInstBaseName), UserInstArgs),
|
|
UnqualInst = defined_inst(UnqualInstName),
|
|
invalid_inst_in_mutable(ModuleInfo, Context, InstVarSet,
|
|
ParentInsts, UnqualInst, FreePieces, !Specs)
|
|
else
|
|
check_mutable_insts(ModuleInfo, Context, InstVarSet,
|
|
ParentInsts, UserInstArgs, !Specs),
|
|
|
|
module_info_get_inst_table(ModuleInfo, InstTable),
|
|
inst_table_get_user_insts(InstTable, UserInstTable),
|
|
( if map.search(UserInstTable, UserInstCtor, InstDefn) then
|
|
InstDefn = hlds_inst_defn(DefnInstVarSet, _Params,
|
|
InstBody, _MMTC, _Context, _Status),
|
|
InstBody = eqv_inst(EqvInst),
|
|
DefnParentInsts = [UserInstCtor | ParentInsts],
|
|
check_mutable_inst(ModuleInfo, Context, DefnInstVarSet,
|
|
DefnParentInsts, EqvInst, !Specs)
|
|
else
|
|
UndefinedPieces = [words("is not defined.")],
|
|
invalid_inst_in_mutable(ModuleInfo, Context, InstVarSet,
|
|
ParentInsts, Inst, UndefinedPieces, !Specs)
|
|
)
|
|
)
|
|
;
|
|
( InstName = unify_inst(_, _, _, _)
|
|
; InstName = merge_inst(_, _)
|
|
; InstName = ground_inst(_, _, _, _)
|
|
; InstName = any_inst(_, _, _, _)
|
|
; InstName = shared_inst(_)
|
|
; InstName = mostly_uniq_inst(_)
|
|
; InstName = typed_inst(_, _)
|
|
; InstName = typed_ground(_, _)
|
|
),
|
|
unexpected($pred, "non-user inst")
|
|
)
|
|
;
|
|
( Inst = free
|
|
; Inst = free(_)
|
|
),
|
|
FreePieces = [words("may not appear in"),
|
|
decl("mutable"), words("declarations.")],
|
|
invalid_inst_in_mutable(ModuleInfo, Context, InstVarSet, ParentInsts,
|
|
Inst, FreePieces, !Specs)
|
|
;
|
|
Inst = constrained_inst_vars(_, _),
|
|
ConstrainedPieces = [words("is constrained, and thus"),
|
|
words("may not appear in"), decl("mutable"),
|
|
words("declarations.")],
|
|
invalid_inst_in_mutable(ModuleInfo, Context, InstVarSet, ParentInsts,
|
|
Inst, ConstrainedPieces, !Specs)
|
|
;
|
|
Inst = abstract_inst(_, _),
|
|
AbstractPieces = [words("is abstract, and thus"),
|
|
words("may not appear in"), decl("mutable"),
|
|
words("declarations.")],
|
|
invalid_inst_in_mutable(ModuleInfo, Context, InstVarSet, ParentInsts,
|
|
Inst, AbstractPieces, !Specs)
|
|
;
|
|
Inst = inst_var(_)
|
|
% The parser ensures that the inst in the mutable declaration does
|
|
% not have any variables. Any variables we encounter here must be
|
|
% a parameter from a named inst that the top level inst refers to
|
|
% either directly or indirectly.
|
|
;
|
|
Inst = not_reached,
|
|
unexpected($pred, "not_reached")
|
|
).
|
|
|
|
:- pred check_mutable_bound_insts(module_info::in, prog_context::in,
|
|
inst_varset::in, list(inst_ctor)::in, list(bound_inst)::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_mutable_bound_insts(_ModuleInfo, _Context, _InstVarSet, _ParentInsts,
|
|
[], !Specs).
|
|
check_mutable_bound_insts(ModuleInfo, Context, InstVarSet, ParentInsts,
|
|
[BoundInst | BoundInsts], !Specs) :-
|
|
BoundInst = bound_functor(_ConsId, ArgInsts),
|
|
check_mutable_insts(ModuleInfo, Context, InstVarSet, ParentInsts,
|
|
ArgInsts, !Specs),
|
|
check_mutable_bound_insts(ModuleInfo, Context, InstVarSet, ParentInsts,
|
|
BoundInsts, !Specs).
|
|
|
|
:- pred check_mutable_insts(module_info::in, prog_context::in,
|
|
inst_varset::in, list(inst_ctor)::in, list(mer_inst)::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_mutable_insts(_ModuleInfo, _Context, _InstVarSet, _ParentInsts,
|
|
[], !Specs).
|
|
check_mutable_insts(ModuleInfo, Context, InstVarSet, ParentInsts,
|
|
[Inst | Insts], !Specs) :-
|
|
check_mutable_inst(ModuleInfo, Context, InstVarSet, ParentInsts,
|
|
Inst, !Specs),
|
|
check_mutable_insts(ModuleInfo, Context, InstVarSet, ParentInsts,
|
|
Insts, !Specs).
|
|
|
|
%---------------------%
|
|
|
|
:- pred check_mutable_inst_uniqueness(module_info::in, prog_context::in,
|
|
inst_varset::in, list(inst_ctor)::in, mer_inst::in, uniqueness::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_mutable_inst_uniqueness(ModuleInfo, Context, InstVarSet, ParentInsts,
|
|
Inst, Uniq, !Specs) :-
|
|
(
|
|
Uniq = shared
|
|
;
|
|
(
|
|
Uniq = unique,
|
|
UniqStr = "unique"
|
|
;
|
|
Uniq = mostly_unique,
|
|
UniqStr = "mostly_unique"
|
|
;
|
|
Uniq = clobbered,
|
|
UniqStr = "clobbered"
|
|
;
|
|
Uniq = mostly_clobbered,
|
|
UniqStr = "mostly_clobbered"
|
|
),
|
|
( if Inst = ground(Uniq, _) then
|
|
UniqPieces = [words("may not appear in"),
|
|
decl("mutable"), words("declarations.")]
|
|
else
|
|
UniqPieces = [words("has uniqueness"), quote(UniqStr), suffix(","),
|
|
words("which may not appear in"),
|
|
decl("mutable"), words("declarations.")]
|
|
),
|
|
invalid_inst_in_mutable(ModuleInfo, Context, InstVarSet, ParentInsts,
|
|
Inst, UniqPieces, !Specs)
|
|
).
|
|
|
|
:- pred invalid_inst_in_mutable(module_info::in, prog_context::in,
|
|
inst_varset::in, list(inst_ctor)::in, mer_inst::in,
|
|
list(format_component)::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
invalid_inst_in_mutable(ModuleInfo, Context, InstVarSet, ParentInsts, Inst,
|
|
ProblemPieces, !Specs) :-
|
|
named_parents_to_pieces(ParentInsts, ParentPieces),
|
|
InstPieces = error_msg_inst(ModuleInfo, InstVarSet,
|
|
dont_expand_named_insts, quote_short_inst,
|
|
[], [nl_indent_delta(1)], [nl_indent_delta(-1)], Inst),
|
|
Pieces = [words("Error:") | ParentPieces] ++
|
|
[words("the inst") | InstPieces] ++ ProblemPieces ++ [nl],
|
|
Spec = simplest_spec($pred, severity_error, phase_parse_tree_to_hlds,
|
|
Context, Pieces),
|
|
!:Specs = [Spec | !.Specs].
|
|
|
|
:- pred named_parents_to_pieces(list(inst_ctor)::in,
|
|
list(format_component)::out) is det.
|
|
|
|
named_parents_to_pieces([], []).
|
|
named_parents_to_pieces([InstCtor | InstCtors], Pieces) :-
|
|
named_parent_to_pieces(InstCtor, HeadPieces),
|
|
named_parents_to_pieces(InstCtors, TailPieces),
|
|
Pieces = HeadPieces ++ TailPieces.
|
|
|
|
:- pred named_parent_to_pieces(inst_ctor::in,
|
|
list(format_component)::out) is det.
|
|
|
|
named_parent_to_pieces(InstCtor, Pieces) :-
|
|
InstCtor = inst_ctor(InstName, InstArity),
|
|
Pieces = [words("in the expansion of the named inst"),
|
|
qual_sym_name_arity(sym_name_arity(InstName, InstArity)),
|
|
suffix(":"), nl].
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_aux_pred_decls_for_mutable(pred_status::in, need_qualifier::in,
|
|
item_mutable_info::in, module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_aux_pred_decls_for_mutable(PredStatus, NeedQual, ItemMutable,
|
|
!ModuleInfo, !Specs) :-
|
|
ItemMutable = item_mutable_info(MutableName,
|
|
_OrigType, Type, _OrigInst, Inst,
|
|
_InitTerm, _VarSetMutable, MutAttrs, Context, _SeqNum),
|
|
module_info_get_globals(!.ModuleInfo, Globals),
|
|
get_mutable_target_params(Globals, MutAttrs, TargetParams),
|
|
PublicPredKinds = TargetParams ^ mtp_public_aux_preds,
|
|
PrivatePredKinds = TargetParams ^ mtp_private_aux_preds,
|
|
NeededPredKinds = PublicPredKinds ++ PrivatePredKinds,
|
|
module_info_get_name(!.ModuleInfo, ModuleName),
|
|
list.map(
|
|
make_mutable_aux_pred_decl(ModuleName, MutableName, Type, Inst,
|
|
Context),
|
|
NeededPredKinds, NeededPredDecls),
|
|
list.map_foldl2(
|
|
module_add_pred_decl(PredStatus, NeedQual),
|
|
NeededPredDecls, _MaybePredProcIds, !ModuleInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
add_aux_pred_defns_for_mutable_if_local(SectionItem,
|
|
!ModuleInfo, !QualInfo, !Specs) :-
|
|
SectionItem = sec_item(SectionInfo, ItemMutable),
|
|
SectionInfo = sec_info(ItemMercuryStatus, _NeedQual),
|
|
(
|
|
ItemMercuryStatus = item_defined_in_this_module(_),
|
|
item_mercury_status_to_pred_status(ItemMercuryStatus, PredStatus),
|
|
add_aux_pred_defns_for_mutable(ItemMutable, PredStatus, !ModuleInfo,
|
|
!QualInfo, !Specs)
|
|
;
|
|
ItemMercuryStatus = item_defined_in_other_module(_)
|
|
).
|
|
|
|
:- pred add_aux_pred_defns_for_mutable(item_mutable_info::in, pred_status::in,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_aux_pred_defns_for_mutable(ItemMutable, PredStatus,
|
|
!ModuleInfo, !QualInfo, !Specs) :-
|
|
% The transformation here is documented in the comments at the
|
|
% beginning of prog_mutable.m.
|
|
ItemMutable = item_mutable_info(MutableName,
|
|
_OrigType, Type, _OrigInst, _Inst,
|
|
_InitTerm, _VarSetMutable, MutAttrs, Context, _SeqNum),
|
|
module_info_get_globals(!.ModuleInfo, Globals),
|
|
get_mutable_target_params(Globals, MutAttrs, TargetParams),
|
|
IsConstant = mutable_var_constant(MutAttrs),
|
|
IsThreadLocal = mutable_var_thread_local(MutAttrs),
|
|
ImplLang = TargetParams ^ mtp_mutable_impl_lang,
|
|
Lang = TargetParams ^ mtp_target_lang,
|
|
|
|
% Work out what name to give the global in the target language.
|
|
module_info_get_name(!.ModuleInfo, ModuleName),
|
|
decide_mutable_target_var_name(!.ModuleInfo, MutAttrs, ModuleName,
|
|
MutableName, Lang, Context, TargetMutableName),
|
|
|
|
% We define the global storing the mutable now rather than earlier
|
|
% because the target-language-specific name of the type of the global
|
|
% depends on whether there are any foreign_type declarations for Type.
|
|
(
|
|
ImplLang = mutable_lang_c,
|
|
define_mutable_global_var_c(TargetMutableName, Type, IsConstant,
|
|
IsThreadLocal, Context, !ModuleInfo)
|
|
;
|
|
ImplLang = mutable_lang_csharp,
|
|
define_mutable_global_var_csharp(TargetMutableName, Type,
|
|
IsThreadLocal, Context, !ModuleInfo)
|
|
;
|
|
ImplLang = mutable_lang_java,
|
|
define_mutable_global_var_java( TargetMutableName, Type,
|
|
IsThreadLocal, Context, !ModuleInfo)
|
|
),
|
|
define_aux_preds_for_mutable(TargetParams, ItemMutable, TargetMutableName,
|
|
PredStatus, !ModuleInfo, !QualInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Define the global holding the mutable.
|
|
%
|
|
|
|
% Define the global variable used to hold the mutable on the C backend,
|
|
% and if needed, the mutex controlling access to it.
|
|
%
|
|
:- pred define_mutable_global_var_c(string::in, mer_type::in,
|
|
mutable_constant::in, mutable_thread_local::in, prog_context::in,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
define_mutable_global_var_c(TargetMutableName, Type, IsConstant, IsThreadLocal,
|
|
Context, !ModuleInfo) :-
|
|
% The declaration we construct will be included in the .mh files. Since
|
|
% these are grade independent, we need to output both the high- and
|
|
% low-level C declarations for the global used to implement the mutable,
|
|
% and make the choice conditional on whether MR_HIGHLEVEL_CODE is defined.
|
|
(
|
|
IsThreadLocal = mutable_not_thread_local,
|
|
% The only difference between the high- and low-level C backends
|
|
% is that in the latter, mutables are *always* boxed, whereas
|
|
% in the former they may not be.
|
|
HighLevelTypeName = global_foreign_type_name(bp_native_if_possible,
|
|
lang_c, !.ModuleInfo, Type),
|
|
LowLevelTypeName = global_foreign_type_name(bp_always_boxed,
|
|
lang_c, !.ModuleInfo, Type),
|
|
module_info_get_globals(!.ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, highlevel_code, HighLevelCode),
|
|
(
|
|
HighLevelCode = no,
|
|
TypeName = LowLevelTypeName
|
|
;
|
|
HighLevelCode = yes,
|
|
TypeName = HighLevelTypeName
|
|
)
|
|
;
|
|
IsThreadLocal = mutable_thread_local,
|
|
% For thread-local mutables, the variable holds an index into an
|
|
% array.
|
|
TypeName = "MR_Unsigned",
|
|
HighLevelTypeName = TypeName,
|
|
LowLevelTypeName = TypeName
|
|
),
|
|
|
|
% Constant mutables do not require mutexes, as their values are never
|
|
% updated. Thread-local mutables do not require mutexes either.
|
|
( if
|
|
( IsConstant = mutable_constant
|
|
; IsThreadLocal = mutable_thread_local
|
|
)
|
|
then
|
|
LockDeclStrs = [],
|
|
LockDefnStrs = []
|
|
else
|
|
MutexVarName = mutable_mutex_var_name(TargetMutableName),
|
|
LockDeclStrs = [
|
|
"#ifdef MR_THREAD_SAFE\n",
|
|
" extern MercuryLock ", MutexVarName, ";\n",
|
|
"#endif\n"
|
|
],
|
|
LockDefnStrs = [
|
|
"#ifdef MR_THREAD_SAFE\n",
|
|
" MercuryLock ", MutexVarName, ";\n",
|
|
"#endif\n"
|
|
]
|
|
),
|
|
|
|
DeclBody = string.append_list([
|
|
"#ifdef MR_HIGHLEVEL_CODE\n",
|
|
" extern ", HighLevelTypeName, " ", TargetMutableName, ";\n",
|
|
"#else\n",
|
|
" extern ", LowLevelTypeName, " ", TargetMutableName, ";\n",
|
|
"#endif\n" | LockDeclStrs]),
|
|
ForeignDeclCode = foreign_decl_code(lang_c, foreign_decl_is_exported,
|
|
floi_literal(DeclBody), Context),
|
|
module_add_foreign_decl_code_aux(ForeignDeclCode, !ModuleInfo),
|
|
|
|
DefnBody = string.append_list([
|
|
TypeName, " ", TargetMutableName, ";\n" | LockDefnStrs]),
|
|
ForeignBodyCode = foreign_body_code(lang_c,
|
|
floi_literal(DefnBody), Context),
|
|
module_add_foreign_body_code(ForeignBodyCode, !ModuleInfo).
|
|
|
|
% Define the global variable used to hold the mutable on the C# backend.
|
|
%
|
|
:- pred define_mutable_global_var_csharp(string::in, mer_type::in,
|
|
mutable_thread_local::in, prog_context::in,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
define_mutable_global_var_csharp(TargetMutableName, Type, IsThreadLocal,
|
|
Context, !ModuleInfo) :-
|
|
(
|
|
IsThreadLocal = mutable_not_thread_local,
|
|
( if Type = int_type then
|
|
TypeStr = "int"
|
|
else
|
|
TypeStr = "object"
|
|
)
|
|
;
|
|
IsThreadLocal = mutable_thread_local,
|
|
TypeStr = "int"
|
|
),
|
|
DefnBody = "static " ++ TypeStr ++ " " ++ TargetMutableName ++ ";\n",
|
|
DefnForeignBodyCode =
|
|
foreign_body_code(lang_csharp, floi_literal(DefnBody), Context),
|
|
module_add_foreign_body_code(DefnForeignBodyCode, !ModuleInfo).
|
|
|
|
% Define the global variable used to hold the mutable on the Java backend.
|
|
%
|
|
:- pred define_mutable_global_var_java(string::in, mer_type::in,
|
|
mutable_thread_local::in, prog_context::in,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
define_mutable_global_var_java(TargetMutableName, Type, IsThreadLocal,
|
|
Context, !ModuleInfo) :-
|
|
(
|
|
IsThreadLocal = mutable_not_thread_local,
|
|
% Synchronization is only required for double and long values,
|
|
% which Mercury does not expose. We could also use the volatile
|
|
% keyword. (Java Language Specification, 2nd Ed., 17.4).
|
|
( if Type = int_type then
|
|
TypeStr = "int"
|
|
else
|
|
TypeStr = "java.lang.Object"
|
|
),
|
|
DefnBody = "static " ++ TypeStr ++ " " ++ TargetMutableName ++ ";\n"
|
|
;
|
|
IsThreadLocal = mutable_thread_local,
|
|
( if Type = int_type then
|
|
TypeStr = "java.lang.Integer"
|
|
else
|
|
TypeStr = "java.lang.Object"
|
|
),
|
|
DefnBody = string.append_list([
|
|
"static java.lang.ThreadLocal<", TypeStr, "> ",
|
|
TargetMutableName,
|
|
" = new java.lang.InheritableThreadLocal<", TypeStr, ">();\n"
|
|
])
|
|
),
|
|
DefnForeignBodyCode =
|
|
foreign_body_code(lang_java, floi_literal(DefnBody), Context),
|
|
module_add_foreign_body_code(DefnForeignBodyCode, !ModuleInfo).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred define_aux_preds_for_mutable(mutable_target_params::in,
|
|
item_mutable_info::in, string::in, pred_status::in,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
define_aux_preds_for_mutable(TargetParams, ItemMutable, TargetMutableName,
|
|
PredStatus, !ModuleInfo, !QualInfo, !Specs) :-
|
|
TargetParams = mutable_target_params(ImplLang, Lang, BoxPolicy,
|
|
_PreInit, _LockUnlock, _UnsafeAccess,
|
|
PrivatePredKinds, PublicPredKinds),
|
|
NeededPredKinds = PublicPredKinds ++ PrivatePredKinds,
|
|
|
|
% Set up the default attributes for the foreign_procs used for the
|
|
% access predicates.
|
|
Attrs0 = default_attributes(Lang),
|
|
(
|
|
ImplLang = mutable_lang_c,
|
|
set_box_policy(BoxPolicy, Attrs0, Attrs1),
|
|
set_may_call_mercury(proc_will_not_call_mercury, Attrs1, Attrs)
|
|
;
|
|
( ImplLang = mutable_lang_csharp
|
|
; ImplLang = mutable_lang_java
|
|
),
|
|
% The mutable variable name is not module-qualified, and so
|
|
% it must not be exported to `.opt' files. We could add the
|
|
% qualification but it would be better to move the mutable code
|
|
% generation into the backends first.
|
|
set_may_duplicate(yes(proc_may_not_duplicate), Attrs0, Attrs)
|
|
),
|
|
|
|
% The logic of this code should match the logic of
|
|
% add_mutable_aux_pred_decls, though there is one difference of order:
|
|
% we define the init predicate last, though it is declared second
|
|
% just after the pre_init predicate. This is because the definition
|
|
% needs information we gather during the definition of the other
|
|
% predicates.
|
|
|
|
some [!PredKinds]
|
|
(
|
|
set.list_to_set(NeededPredKinds, !:PredKinds),
|
|
( if set.remove(mutable_pred_pre_init, !PredKinds) then
|
|
define_pre_init_pred(TargetParams, ItemMutable, TargetMutableName,
|
|
Attrs, PredStatus, CallPreInitExpr,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
MaybeCallPreInitExpr = yes(CallPreInitExpr)
|
|
else
|
|
MaybeCallPreInitExpr = no
|
|
),
|
|
( if
|
|
set.remove(mutable_pred_lock, !PredKinds),
|
|
set.remove(mutable_pred_unlock, !PredKinds)
|
|
then
|
|
define_lock_unlock_preds(TargetParams, ItemMutable,
|
|
TargetMutableName, Attrs, PredStatus, LockUnlockExprs,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
MaybeLockUnlockExprs = yes(LockUnlockExprs)
|
|
else
|
|
MaybeLockUnlockExprs = no
|
|
),
|
|
( if
|
|
set.remove(mutable_pred_unsafe_get, !PredKinds),
|
|
set.remove(mutable_pred_unsafe_set, !PredKinds)
|
|
then
|
|
define_unsafe_get_set_preds(TargetParams, ItemMutable,
|
|
TargetMutableName, Attrs, PredStatus, UnsafeGetSetExprs,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
MaybeUnsafeGetSetExprs = yes(UnsafeGetSetExprs)
|
|
else
|
|
MaybeUnsafeGetSetExprs = no
|
|
),
|
|
|
|
% We do this after defining (a) the lock and unlock predicates and
|
|
% (b) the unsafe get and set predicates, since they give us
|
|
% (a) MaybeLockUnlockExprs and (b) MaybeUnsafeGetSetExprs respectively.
|
|
define_main_get_set_preds(TargetParams, ItemMutable, TargetMutableName,
|
|
Attrs, PredStatus, MaybeLockUnlockExprs, MaybeUnsafeGetSetExprs,
|
|
InitSetPredName, !PredKinds, !ModuleInfo, !QualInfo, !Specs),
|
|
|
|
( if set.remove(mutable_pred_init, !PredKinds) then
|
|
% We do this after defining (a) the preinit predicate and
|
|
% (b) the main get and set predicates, since they give us
|
|
% (a) MaybeCallPreInitExpr and (b) InitSetPredName respectively.
|
|
define_init_pred(ItemMutable, PredStatus, Lang, InitSetPredName,
|
|
MaybeCallPreInitExpr, !ModuleInfo, !QualInfo, !Specs)
|
|
else
|
|
unexpected($pred, "mutable does not need init predicate")
|
|
),
|
|
|
|
expect(set.is_empty(!.PredKinds), $pred, "!.PredKinds is not empty")
|
|
).
|
|
|
|
% Define the pre_init predicates, if needed by the init predicate.
|
|
%
|
|
:- pred define_pre_init_pred(mutable_target_params::in, item_mutable_info::in,
|
|
string::in, pragma_foreign_proc_attributes::in, pred_status::in, goal::out,
|
|
module_info::in, module_info::out,
|
|
qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
define_pre_init_pred(TargetParams, ItemMutable, TargetMutableName, Attrs,
|
|
PredStatus, CallPreInitExpr, !ModuleInfo, !QualInfo, !Specs) :-
|
|
module_info_get_name(!.ModuleInfo, ModuleName),
|
|
ItemMutable = item_mutable_info(MutableName,
|
|
_OrigType, _Type, _OrigInst, _Inst,
|
|
_InitTerm, _VarSetMutable, MutAttrs, Context, _SeqNum),
|
|
IsConstant = mutable_var_constant(MutAttrs),
|
|
expect(unify(IsConstant, mutable_not_constant), $pred,
|
|
"need_pre_init_pred, but IsConstant = mutable_constant"),
|
|
IsThreadLocal = mutable_var_thread_local(MutAttrs),
|
|
ImplLang = TargetParams ^ mtp_mutable_impl_lang,
|
|
PreInitPredName = mutable_pre_init_pred_sym_name(ModuleName, MutableName),
|
|
|
|
(
|
|
ImplLang = mutable_lang_c,
|
|
(
|
|
IsThreadLocal = mutable_not_thread_local,
|
|
PreInitCode = string.append_list([
|
|
"#ifdef MR_THREAD_SAFE\n",
|
|
" pthread_mutex_init(&",
|
|
mutable_mutex_var_name(TargetMutableName),
|
|
", MR_MUTEX_ATTR);\n",
|
|
"#endif\n"
|
|
])
|
|
;
|
|
IsThreadLocal = mutable_thread_local,
|
|
PreInitCode = TargetMutableName ++
|
|
" = MR_new_thread_local_mutable_index();\n"
|
|
)
|
|
;
|
|
ImplLang = mutable_lang_csharp,
|
|
PreInitCode = TargetMutableName ++
|
|
" = runtime.ThreadLocalMutables.new_index();\n"
|
|
;
|
|
ImplLang = mutable_lang_java,
|
|
unexpected($pred, "preinit for java")
|
|
),
|
|
PreInitFCInfo = pragma_info_foreign_proc(Attrs,
|
|
PreInitPredName,
|
|
pf_predicate,
|
|
[], % Args
|
|
varset.init, % ProgVarSet
|
|
varset.init, % InstVarSet
|
|
fp_impl_ordinary(PreInitCode, yes(Context))
|
|
),
|
|
add_pragma_foreign_proc(PreInitFCInfo, PredStatus, Context, no,
|
|
!ModuleInfo, !Specs),
|
|
|
|
CallPreInitExpr = call_expr(Context, PreInitPredName, [], purity_impure).
|
|
|
|
% Define the lock and unlock predicates, if needed.
|
|
%
|
|
:- pred define_lock_unlock_preds(mutable_target_params::in,
|
|
item_mutable_info::in, string::in, pragma_foreign_proc_attributes::in,
|
|
pred_status::in, {goal, goal}::out,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
define_lock_unlock_preds(TargetParams, ItemMutable, TargetMutableName, Attrs,
|
|
PredStatus, LockUnlockExprs, !ModuleInfo, !QualInfo, !Specs) :-
|
|
module_info_get_name(!.ModuleInfo, ModuleName),
|
|
ItemMutable = item_mutable_info(MutableName,
|
|
_OrigType, _Type, _OrigInst, _Inst,
|
|
_InitTerm, _VarSetMutable, MutAttrs, Context, _SeqNum),
|
|
IsConstant = mutable_var_constant(MutAttrs),
|
|
expect(unify(IsConstant, mutable_not_constant), $pred,
|
|
"need_lock_unlock_preds, but IsConstant = mutable_constant"),
|
|
IsThreadLocal = mutable_var_thread_local(MutAttrs),
|
|
ImplLang = TargetParams ^ mtp_mutable_impl_lang,
|
|
|
|
(
|
|
ImplLang = mutable_lang_c,
|
|
set_thread_safe(proc_thread_safe, Attrs, LockAndUnlockAttrs),
|
|
MutableMutexVarName = mutable_mutex_var_name(TargetMutableName),
|
|
|
|
(
|
|
IsThreadLocal = mutable_not_thread_local,
|
|
% XXX The second argument of both calls should be the name of
|
|
% the Mercury predicate, with chars escaped as appropriate.
|
|
LockForeignProcBody = string.append_list([
|
|
"#ifdef MR_THREAD_SAFE\n",
|
|
" MR_LOCK(&" ++ MutableMutexVarName ++ ",
|
|
\"" ++ MutableMutexVarName ++ "\");\n" ++
|
|
"#endif\n"
|
|
]),
|
|
UnlockForeignProcBody = string.append_list([
|
|
"#ifdef MR_THREAD_SAFE\n",
|
|
" MR_UNLOCK(&" ++ MutableMutexVarName ++ ",
|
|
\"" ++ MutableMutexVarName ++ "\");\n" ++
|
|
"#endif\n"
|
|
])
|
|
;
|
|
IsThreadLocal = mutable_thread_local,
|
|
LockForeignProcBody = "",
|
|
UnlockForeignProcBody = ""
|
|
),
|
|
LockPredName =
|
|
mutable_lock_pred_sym_name(ModuleName, MutableName),
|
|
UnlockPredName =
|
|
mutable_unlock_pred_sym_name(ModuleName, MutableName),
|
|
LockFCInfo = pragma_info_foreign_proc(LockAndUnlockAttrs,
|
|
LockPredName,
|
|
pf_predicate,
|
|
[],
|
|
varset.init, % ProgVarSet
|
|
varset.init, % InstVarSet
|
|
fp_impl_ordinary(LockForeignProcBody, yes(Context))
|
|
),
|
|
UnlockFCInfo = pragma_info_foreign_proc(LockAndUnlockAttrs,
|
|
UnlockPredName,
|
|
pf_predicate,
|
|
[],
|
|
varset.init, % ProgVarSet
|
|
varset.init, % InstVarSet
|
|
fp_impl_ordinary(UnlockForeignProcBody, yes(Context))
|
|
),
|
|
add_pragma_foreign_proc(LockFCInfo, PredStatus, Context, no,
|
|
!ModuleInfo, !Specs),
|
|
add_pragma_foreign_proc(UnlockFCInfo, PredStatus, Context, no,
|
|
!ModuleInfo, !Specs),
|
|
CallLockExpr0 =
|
|
call_expr(Context, LockPredName, [], purity_impure),
|
|
CallUnlockExpr0 =
|
|
call_expr(Context, UnlockPredName, [], purity_impure),
|
|
LockUnlockExprs = {CallLockExpr0, CallUnlockExpr0}
|
|
;
|
|
ImplLang = mutable_lang_csharp,
|
|
unexpected($pred, "lock_unlock for csharp")
|
|
;
|
|
ImplLang = mutable_lang_java,
|
|
unexpected($pred, "lock_unlock for java")
|
|
).
|
|
|
|
% Define the unsafe get and set predicates, if needed.
|
|
%
|
|
:- pred define_unsafe_get_set_preds(mutable_target_params::in,
|
|
item_mutable_info::in, string::in, pragma_foreign_proc_attributes::in,
|
|
pred_status::in, {goal, goal}::out,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
define_unsafe_get_set_preds(TargetParams, ItemMutable, TargetMutableName,
|
|
Attrs, PredStatus, UnsafeGetSetExprs,
|
|
!ModuleInfo, !QualInfo, !Specs) :-
|
|
module_info_get_name(!.ModuleInfo, ModuleName),
|
|
ItemMutable = item_mutable_info(MutableName,
|
|
_OrigType, Type, _OrigInst, Inst,
|
|
_InitTerm, _VarSetMutable, MutAttrs, Context, _SeqNum),
|
|
IsConstant = mutable_var_constant(MutAttrs),
|
|
expect(unify(IsConstant, mutable_not_constant), $pred,
|
|
"need_unsafe_get_set_preds, but IsConstant = mutable_constant"),
|
|
IsThreadLocal = mutable_var_thread_local(MutAttrs),
|
|
ImplLang = TargetParams ^ mtp_mutable_impl_lang,
|
|
Lang = TargetParams ^ mtp_target_lang,
|
|
BoxPolicy = TargetParams ^ mtp_box_policy,
|
|
varset.new_named_var("X", X, varset.init, VarSetOnlyX),
|
|
|
|
set_thread_safe(proc_thread_safe, Attrs, ThreadSafeAttrs),
|
|
set_purity(purity_semipure, ThreadSafeAttrs, UnsafeGetAttrs),
|
|
UnsafeSetAttrs = ThreadSafeAttrs, % defaults to purity_impure
|
|
|
|
TrailedMutable = mutable_var_trailed(MutAttrs),
|
|
(
|
|
ImplLang = mutable_lang_c,
|
|
(
|
|
TrailedMutable = mutable_untrailed,
|
|
TrailCode = ""
|
|
;
|
|
TrailedMutable = mutable_trailed,
|
|
% We have already checked that we are in a
|
|
% trailing grade.
|
|
TrailCode = "MR_trail_current_value(&" ++
|
|
TargetMutableName ++ ");\n"
|
|
),
|
|
(
|
|
IsThreadLocal = mutable_not_thread_local,
|
|
UnsafeGetCode = "X = " ++ TargetMutableName ++ ";\n",
|
|
UnsafeSetCode = TargetMutableName ++ " = X;\n"
|
|
;
|
|
IsThreadLocal = mutable_thread_local,
|
|
TypeName = global_foreign_type_name(BoxPolicy, Lang,
|
|
!.ModuleInfo, Type),
|
|
UnsafeGetCode = "MR_get_thread_local_mutable(" ++
|
|
TypeName ++ ", X, " ++ TargetMutableName ++ ");\n",
|
|
UnsafeSetCode = "MR_set_thread_local_mutable(" ++
|
|
TypeName ++ ", X, " ++ TargetMutableName ++ ");\n"
|
|
)
|
|
;
|
|
ImplLang = mutable_lang_csharp,
|
|
% We generate an error for trailed mutables in pass 2, but we
|
|
% still continue on to pass 3 even in the presence of such errors.
|
|
TrailCode = "",
|
|
(
|
|
IsThreadLocal = mutable_not_thread_local,
|
|
UnsafeGetCode = "\tX = " ++ TargetMutableName ++ ";\n",
|
|
UnsafeSetCode = "\t" ++ TargetMutableName ++ " = X;\n"
|
|
;
|
|
IsThreadLocal = mutable_thread_local,
|
|
( if Type = int_type then
|
|
Cast = "(int) "
|
|
else
|
|
Cast = ""
|
|
),
|
|
UnsafeGetCode = "\tX = " ++ Cast ++
|
|
"runtime.ThreadLocalMutables.get(" ++
|
|
TargetMutableName ++ ");\n",
|
|
UnsafeSetCode = "\truntime.ThreadLocalMutables.set(" ++
|
|
TargetMutableName ++ ", X);\n"
|
|
)
|
|
;
|
|
ImplLang = mutable_lang_java,
|
|
% We generate an error for trailed mutables in pass 2, but we
|
|
% still continue on to pass 3 even in the presence of such errors.
|
|
TrailCode = "",
|
|
(
|
|
IsThreadLocal = mutable_not_thread_local,
|
|
UnsafeGetCode = "\tX = " ++ TargetMutableName ++ ";\n",
|
|
UnsafeSetCode = "\t" ++ TargetMutableName ++ " = X;\n"
|
|
;
|
|
IsThreadLocal = mutable_thread_local,
|
|
UnsafeGetCode = "\tX = " ++ TargetMutableName ++ ".get();\n",
|
|
UnsafeSetCode = "\t" ++ TargetMutableName ++ ".set(X);\n"
|
|
)
|
|
),
|
|
|
|
UnsafeGetPredName =
|
|
mutable_unsafe_get_pred_sym_name(ModuleName, MutableName),
|
|
UnsafeSetPredName =
|
|
mutable_unsafe_set_pred_sym_name(ModuleName, MutableName),
|
|
UnsafeGetFCInfo = pragma_info_foreign_proc(UnsafeGetAttrs,
|
|
UnsafeGetPredName,
|
|
pf_predicate,
|
|
[pragma_var(X, "X", out_mode(Inst), BoxPolicy)],
|
|
VarSetOnlyX, % ProgVarSet
|
|
varset.init, % InstVarSet
|
|
fp_impl_ordinary(UnsafeGetCode, yes(Context))
|
|
),
|
|
UnsafeSetFCInfo = pragma_info_foreign_proc(UnsafeSetAttrs,
|
|
UnsafeSetPredName,
|
|
pf_predicate,
|
|
[pragma_var(X, "X", in_mode(Inst), BoxPolicy)],
|
|
VarSetOnlyX, % ProgVarSet
|
|
varset.init, % InstVarSet
|
|
fp_impl_ordinary(TrailCode ++ UnsafeSetCode, yes(Context))
|
|
),
|
|
add_pragma_foreign_proc(UnsafeGetFCInfo, PredStatus, Context, no,
|
|
!ModuleInfo, !Specs),
|
|
add_pragma_foreign_proc(UnsafeSetFCInfo, PredStatus, Context, no,
|
|
!ModuleInfo, !Specs),
|
|
|
|
CallUnsafeGetExpr0 = call_expr(Context, UnsafeGetPredName,
|
|
[variable(X, Context)], purity_semipure),
|
|
CallUnsafeSetExpr0 = call_expr(Context, UnsafeSetPredName,
|
|
[variable(X, Context)], purity_impure),
|
|
UnsafeGetSetExprs = {CallUnsafeGetExpr0, CallUnsafeSetExpr0}.
|
|
|
|
% Define one of the following sets of predicates:
|
|
%
|
|
% 1: the standard get predicate and the constant set predicate; or
|
|
% 2: the standard get and set predicates; or
|
|
% 3: the standard get and set predicates and the io get and set predicate.
|
|
%
|
|
% We define set 1 if the mutable is constant, and one of 2 or 3
|
|
% if it is not, depending on whether the mutable is attached to
|
|
% the I/O state.
|
|
%
|
|
% We do this *after* creating the lock and unlock predicates
|
|
% and the unsafe get and set predicates, since they give us
|
|
% MaybeLockUnlockExprs and MaybeUnsafeGetSetExprs.
|
|
%
|
|
:- pred define_main_get_set_preds(mutable_target_params::in,
|
|
item_mutable_info::in, string::in, pragma_foreign_proc_attributes::in,
|
|
pred_status::in, maybe({goal, goal})::in, maybe({goal, goal})::in,
|
|
sym_name::out, set(mutable_pred_kind)::in, set(mutable_pred_kind)::out,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
define_main_get_set_preds(TargetParams, ItemMutable, TargetMutableName, Attrs,
|
|
PredStatus, MaybeLockUnlockExprs, MaybeUnsafeGetSetExprs,
|
|
InitSetPredName, !PredKinds, !ModuleInfo, !QualInfo, !Specs) :-
|
|
module_info_get_name(!.ModuleInfo, ModuleName),
|
|
ItemMutable = item_mutable_info(MutableName,
|
|
_OrigType, _Type, _OrigInst, Inst,
|
|
_InitTerm, _VarSetMutable, MutAttrs, Context, _SeqNum),
|
|
IsConstant = mutable_var_constant(MutAttrs),
|
|
AttachToIO = mutable_var_attach_to_io_state(MutAttrs),
|
|
ImplLang = TargetParams ^ mtp_mutable_impl_lang,
|
|
BoxPolicy = TargetParams ^ mtp_box_policy,
|
|
varset.new_named_var("X", X, varset.init, VarSetOnlyX),
|
|
|
|
% Due to the nontrivial flows of information between the code pieces
|
|
% that construct the code of each aux pred, this code duplicates
|
|
% the logic of compute_needed_public_mutable_aux_preds. We ensure that we
|
|
% define the same predicates that compute_needed_public_mutable_aux_preds
|
|
% has said we need (which is also the set that gets declared) by
|
|
%
|
|
% - checking that the aux predicates we define here are in the set of
|
|
% mutable_pred_kinds computed by compute_needed_public_mutable_aux_preds,
|
|
% and then
|
|
% - having our caller check that there are no mutable_pred_kinds that
|
|
% it says we need but which we have *not* defined.
|
|
(
|
|
IsConstant = mutable_constant,
|
|
ConstantGetPredName =
|
|
mutable_get_pred_sym_name(ModuleName, MutableName),
|
|
ConstantSecretSetPredName =
|
|
mutable_secret_set_pred_sym_name(ModuleName, MutableName),
|
|
InitSetPredName = ConstantSecretSetPredName,
|
|
|
|
set_purity(purity_pure, Attrs, ConstantGetAttrs0),
|
|
set_thread_safe(proc_thread_safe,
|
|
ConstantGetAttrs0, ConstantGetAttrs),
|
|
ConstantSetAttrs = Attrs,
|
|
(
|
|
( ImplLang = mutable_lang_c
|
|
; ImplLang = mutable_lang_csharp
|
|
; ImplLang = mutable_lang_java
|
|
),
|
|
ConstantGetCode = "X = " ++ TargetMutableName ++ ";\n",
|
|
ConstantSetCode = TargetMutableName ++ " = X;\n"
|
|
),
|
|
ConstantGetFCInfo = pragma_info_foreign_proc(ConstantGetAttrs,
|
|
ConstantGetPredName,
|
|
pf_predicate,
|
|
[pragma_var(X, "X", out_mode(Inst), BoxPolicy)],
|
|
VarSetOnlyX, % ProgVarSet
|
|
varset.init, % InstVarSet
|
|
fp_impl_ordinary(ConstantGetCode, yes(Context))
|
|
),
|
|
% NOTE: we don't need to trail the set action, since it is
|
|
% executed only once at initialization time.
|
|
ConstantSetFCInfo = pragma_info_foreign_proc(ConstantSetAttrs,
|
|
ConstantSecretSetPredName,
|
|
pf_predicate,
|
|
[pragma_var(X, "X", in_mode(Inst), BoxPolicy)],
|
|
VarSetOnlyX, % ProgVarSet
|
|
varset.init, % InstVarSet
|
|
fp_impl_ordinary(ConstantSetCode, yes(Context))
|
|
),
|
|
add_pragma_foreign_proc(ConstantGetFCInfo, PredStatus, Context, no,
|
|
!ModuleInfo, !Specs),
|
|
add_pragma_foreign_proc(ConstantSetFCInfo, PredStatus, Context, no,
|
|
!ModuleInfo, !Specs),
|
|
set.det_remove(mutable_pred_constant_get, !PredKinds),
|
|
set.det_remove(mutable_pred_constant_secret_set, !PredKinds),
|
|
|
|
expect(unify(AttachToIO, mutable_dont_attach_to_io_state),
|
|
$pred, "AttachToIO = mutable_attach_to_io_state")
|
|
;
|
|
IsConstant = mutable_not_constant,
|
|
StdGetPredName = mutable_get_pred_sym_name(ModuleName, MutableName),
|
|
StdSetPredName = mutable_set_pred_sym_name(ModuleName, MutableName),
|
|
InitSetPredName = StdSetPredName,
|
|
(
|
|
( ImplLang = mutable_lang_c
|
|
; ImplLang = mutable_lang_csharp
|
|
; ImplLang = mutable_lang_java
|
|
),
|
|
(
|
|
MaybeUnsafeGetSetExprs =
|
|
yes({CallUnsafeGetExpr, CallUnsafeSetExpr})
|
|
;
|
|
MaybeUnsafeGetSetExprs = no,
|
|
unexpected($pred,
|
|
"mutable_not_constant but MaybeUnsafeGetSetExprs = no")
|
|
),
|
|
(
|
|
MaybeLockUnlockExprs = no,
|
|
ImpureGetExpr = CallUnsafeGetExpr,
|
|
ImpureSetExpr = CallUnsafeSetExpr
|
|
;
|
|
MaybeLockUnlockExprs = yes({CallLockExpr, CallUnlockExpr}),
|
|
ImpureGetExpr = conj_expr(Context, CallLockExpr,
|
|
conj_expr(Context, CallUnsafeGetExpr, CallUnlockExpr)),
|
|
ImpureSetExpr = conj_expr(Context, CallLockExpr,
|
|
conj_expr(Context, CallUnsafeSetExpr, CallUnlockExpr))
|
|
),
|
|
|
|
StdPredArgs = [variable(X, Context)],
|
|
StdGetPredExpr = promise_purity_expr(Context, purity_semipure,
|
|
ImpureGetExpr),
|
|
StdSetPredExpr = ImpureSetExpr,
|
|
module_add_clause(VarSetOnlyX, pf_predicate, StdGetPredName,
|
|
StdPredArgs, ok2(StdGetPredExpr, []), PredStatus, Context, no,
|
|
goal_type_none, !ModuleInfo, !QualInfo, !Specs),
|
|
module_add_clause(VarSetOnlyX, pf_predicate, StdSetPredName,
|
|
StdPredArgs, ok2(StdSetPredExpr, []), PredStatus, Context, no,
|
|
goal_type_none, !ModuleInfo, !QualInfo, !Specs),
|
|
set.det_remove(mutable_pred_std_get, !PredKinds),
|
|
set.det_remove(mutable_pred_std_set, !PredKinds)
|
|
),
|
|
(
|
|
AttachToIO = mutable_dont_attach_to_io_state
|
|
;
|
|
AttachToIO = mutable_attach_to_io_state,
|
|
some [!VarSet] (
|
|
!:VarSet = VarSetOnlyX,
|
|
varset.new_named_var("IO0", IO0, !VarSet),
|
|
varset.new_named_var("IO", IO, !VarSet),
|
|
VarSetXandIOs = !.VarSet
|
|
),
|
|
IOGetPredName = StdGetPredName,
|
|
IOSetPredName = StdSetPredName,
|
|
IOPredArgs = [variable(X, Context),
|
|
variable(IO0, Context), variable(IO, Context)],
|
|
|
|
% It is important to have CopyIOExpr INSIDE the promise_pure scope
|
|
% for the set predicate. If it were outside, then the scope
|
|
% would not bind any variables, and since it is promised pure,
|
|
% the compiler would be allowed to delete it. The problem does not
|
|
% arise for the get predicate, since ImpureGetExpr binds X.
|
|
CopyIOExpr = unify_expr(Context,
|
|
variable(IO0, Context), variable(IO, Context),
|
|
purity_impure),
|
|
IOGetPredExpr = conj_expr(Context, ImpureGetExpr, CopyIOExpr),
|
|
IOSetPredExpr = conj_expr(Context, ImpureSetExpr, CopyIOExpr),
|
|
PureIOGetPredExpr =
|
|
promise_purity_expr(Context, purity_pure, IOGetPredExpr),
|
|
PureIOSetPredExpr =
|
|
promise_purity_expr(Context, purity_pure, IOSetPredExpr),
|
|
|
|
module_add_clause(VarSetXandIOs, pf_predicate, IOGetPredName,
|
|
IOPredArgs, ok2(PureIOGetPredExpr, []), PredStatus, Context,
|
|
no, goal_type_none, !ModuleInfo, !QualInfo, !Specs),
|
|
module_add_clause(VarSetXandIOs, pf_predicate, IOSetPredName,
|
|
IOPredArgs, ok2(PureIOSetPredExpr, []), PredStatus, Context,
|
|
no, goal_type_none, !ModuleInfo, !QualInfo, !Specs),
|
|
set.det_remove(mutable_pred_io_get, !PredKinds),
|
|
set.det_remove(mutable_pred_io_set, !PredKinds)
|
|
)
|
|
).
|
|
|
|
% Define the init predicate, and arrange for it to be called
|
|
% at initialization time.
|
|
%
|
|
:- pred define_init_pred(item_mutable_info::in, pred_status::in,
|
|
foreign_language::in, sym_name::in, maybe(goal)::in,
|
|
module_info::in, module_info::out, qual_info::in, qual_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
define_init_pred(ItemMutable, PredStatus, Lang, InitSetPredName,
|
|
MaybeCallPreInitExpr, !ModuleInfo, !QualInfo, !Specs) :-
|
|
module_info_get_name(!.ModuleInfo, ModuleName),
|
|
ItemMutable = item_mutable_info(MutableName,
|
|
_OrigType, _Type, _OrigInst, _Inst,
|
|
InitTerm, VarSetMutable, _MutAttrs, Context, _SeqNum),
|
|
varset.new_named_var("X", X, VarSetMutable, VarSetMutableX),
|
|
VarX = variable(X, Context),
|
|
|
|
UnifyExpr = unify_expr(Context, VarX, InitTerm, purity_impure),
|
|
CallSetExpr = call_expr(Context, InitSetPredName, [VarX], purity_impure),
|
|
UnifyCallSetExpr = conj_expr(Context, UnifyExpr, CallSetExpr),
|
|
(
|
|
MaybeCallPreInitExpr = no,
|
|
InitPredExpr = UnifyCallSetExpr
|
|
;
|
|
MaybeCallPreInitExpr = yes(CallPreInitExpr),
|
|
InitPredExpr = conj_expr(Context, CallPreInitExpr, UnifyCallSetExpr)
|
|
),
|
|
InitPredName = mutable_init_pred_sym_name(ModuleName, MutableName),
|
|
% See the comments for parse_mutable_decl_info for the reason
|
|
% why we _must_ pass VarSetMutableX here.
|
|
module_add_clause(VarSetMutableX, pf_predicate, InitPredName, [],
|
|
ok2(InitPredExpr, []), PredStatus, Context, no, goal_type_none,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
|
|
InitPredArity = 0,
|
|
add_initialise_for_mutable(ModuleName, MutableName,
|
|
InitPredName, InitPredArity, Context, Lang, !ModuleInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_initialise_for_mutable(module_name::in, string::in,
|
|
sym_name::in, arity::in, prog_context::in, foreign_language::in,
|
|
module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_initialise_for_mutable(ModuleName, MutableName, SymName, Arity,
|
|
Context, Lang, !ModuleInfo, !Specs) :-
|
|
% The compiler introduces initialise declarations that call impure
|
|
% predicates as part of the source-to-source transformation for mutable
|
|
% variables. These predicates *must* be impure in order to prevent the
|
|
% compiler optimizing them away.
|
|
Attrs = item_compiler_attributes(compiler_origin_mutable(ModuleName,
|
|
MutableName, mutable_pred_init)),
|
|
Origin = item_origin_compiler(Attrs),
|
|
module_info_new_user_init_pred(SymName, Arity, CName, !ModuleInfo),
|
|
PredNameModesPF = pred_name_modes_pf(SymName, [], pf_predicate),
|
|
FPEInfo = pragma_info_foreign_proc_export(Origin, Lang,
|
|
PredNameModesPF, CName),
|
|
add_pragma_foreign_proc_export(FPEInfo, Context, !ModuleInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Decide what the name of the underlying global used to implement the
|
|
% mutable should be. If there is a foreign_name attribute then use that
|
|
% otherwise construct one based on the Mercury name for the mutable.
|
|
%
|
|
:- pred decide_mutable_target_var_name(module_info::in,
|
|
mutable_var_attributes::in, module_name::in, string::in,
|
|
foreign_language::in, prog_context::in, string::out) is det.
|
|
|
|
decide_mutable_target_var_name(ModuleInfo, MutAttrs, ModuleName, MutableName,
|
|
ForeignLanguage, Context, TargetMutableName) :-
|
|
mutable_var_maybe_foreign_names(MutAttrs) = MaybeForeignNames,
|
|
(
|
|
MaybeForeignNames = no,
|
|
TargetMutableName = mutable_c_var_name(ModuleName, MutableName)
|
|
;
|
|
MaybeForeignNames = yes(ForeignNames),
|
|
% We have already any errors during pass 2, so ignore them here.
|
|
get_global_name_from_foreign_names(ModuleInfo, Context,
|
|
ModuleName, MutableName, ForeignLanguage, ForeignNames,
|
|
TargetMutableName, [], _Specs)
|
|
).
|
|
|
|
% Check to see if there is a valid foreign_name attribute for this backend.
|
|
% If so, use it as the name of the global variable in the target code,
|
|
% otherwise take the Mercury name for the mutable and mangle it into
|
|
% an appropriate variable name.
|
|
%
|
|
:- pred get_global_name_from_foreign_names(module_info::in,
|
|
prog_context::in, module_name::in, string::in, foreign_language::in,
|
|
list(foreign_name)::in, string::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
get_global_name_from_foreign_names(ModuleInfo, Context,
|
|
ModuleName, MutableName, ForeignLanguage, ForeignNames,
|
|
TargetMutableName, !Specs) :-
|
|
get_matching_foreign_names(ForeignNames, ForeignLanguage,
|
|
TargetMutableNames),
|
|
(
|
|
TargetMutableNames = [],
|
|
TargetMutableName = mutable_c_var_name(ModuleName, MutableName)
|
|
;
|
|
TargetMutableNames = [foreign_name(_, TargetMutableName)]
|
|
% XXX We should really check that this is a valid identifier
|
|
% in the target language here.
|
|
;
|
|
TargetMutableNames = [_, _ | _],
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_target(Globals, CompilationTarget),
|
|
Pieces = [words("Error: multiple foreign_name attributes"),
|
|
words("specified for the"),
|
|
fixed(compilation_target_string(CompilationTarget)),
|
|
words("backend."), nl],
|
|
Spec = simplest_spec($pred, severity_error, phase_parse_tree_to_hlds,
|
|
Context, Pieces),
|
|
!:Specs = [Spec | !.Specs],
|
|
|
|
TargetMutableName = mutable_c_var_name(ModuleName, MutableName)
|
|
).
|
|
|
|
:- pred get_matching_foreign_names(list(foreign_name)::in,
|
|
foreign_language::in, list(foreign_name)::out) is det.
|
|
|
|
get_matching_foreign_names([], _TargetForeignLanguage, []).
|
|
get_matching_foreign_names([ForeignName | ForeignNames], TargetForeignLanguage,
|
|
MatchingForeignNames) :-
|
|
get_matching_foreign_names(ForeignNames, TargetForeignLanguage,
|
|
TailMatchingForeignNames),
|
|
ForeignName = foreign_name(ForeignLanguage, _),
|
|
( if ForeignLanguage = TargetForeignLanguage then
|
|
MatchingForeignNames = [ForeignName | TailMatchingForeignNames]
|
|
else
|
|
MatchingForeignNames = TailMatchingForeignNames
|
|
).
|
|
|
|
% The first argument global_foreign_type_name says whether the mutable
|
|
% should always be boxed or not. The only difference between the high- and
|
|
% low-level C backends is that in the latter mutables are *always* boxed,
|
|
% whereas in the former they may not be. The other backends that support
|
|
% mutables are all native_if_possible.
|
|
%
|
|
:- func global_foreign_type_name(box_policy, foreign_language, module_info,
|
|
mer_type) = string.
|
|
|
|
global_foreign_type_name(BoxPolicy, Lang, ModuleInfo, Type) = String :-
|
|
(
|
|
BoxPolicy = bp_always_boxed,
|
|
String = "MR_Word"
|
|
;
|
|
BoxPolicy = bp_native_if_possible,
|
|
String = exported_type_to_string(ModuleInfo, Lang, Type)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func mutable_c_var_name(module_name, string) = string.
|
|
|
|
mutable_c_var_name(ModuleName, Name) = MangledCVarName :-
|
|
RawCVarName = "mutable_variable_" ++ Name,
|
|
QualifiedCVarName0 = qualified(ModuleName, RawCVarName),
|
|
( if mercury_std_library_module_name(ModuleName) then
|
|
QualifiedCVarName =
|
|
add_outermost_qualifier("mercury", QualifiedCVarName0)
|
|
else
|
|
QualifiedCVarName = QualifiedCVarName0
|
|
),
|
|
MangledCVarName = sym_name_mangle(QualifiedCVarName).
|
|
|
|
% Returns the name of the mutex associated a given mutable. The input
|
|
% to this function is the name of the mutable in the target language,
|
|
% i.e. it is the result of a call to mutable_c_var_name/2 or one of the
|
|
% specified foreign names for the mutable.
|
|
%
|
|
:- func mutable_mutex_var_name(string) = string.
|
|
|
|
mutable_mutex_var_name(TargetMutableVarName) = MutexVarName :-
|
|
MutexVarName = TargetMutableVarName ++ "_lock".
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type mutable_impl_lang
|
|
---> mutable_lang_c
|
|
; mutable_lang_csharp
|
|
; mutable_lang_java.
|
|
|
|
:- type need_pre_init_pred
|
|
---> dont_need_pre_init_pred
|
|
; need_pre_init_pred.
|
|
|
|
:- type need_lock_unlock_preds
|
|
---> dont_need_lock_unlock_preds
|
|
; need_lock_unlock_preds.
|
|
|
|
:- type need_unsafe_get_set_preds
|
|
---> dont_need_unsafe_get_set_preds
|
|
; need_unsafe_get_set_preds.
|
|
|
|
:- type mutable_target_params
|
|
---> mutable_target_params(
|
|
mtp_mutable_impl_lang :: mutable_impl_lang,
|
|
mtp_target_lang :: foreign_language,
|
|
mtp_box_policy :: box_policy,
|
|
mtp_need_pre_init :: need_pre_init_pred,
|
|
mtp_need_locking :: need_lock_unlock_preds,
|
|
mtp_need_unsafe_get_set :: need_unsafe_get_set_preds,
|
|
mtp_private_aux_preds :: list(mutable_pred_kind),
|
|
mtp_public_aux_preds :: list(mutable_pred_kind)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% This predicate decides which auxiliary predicates we need
|
|
% to implement a mutable. The rest of this module just implements
|
|
% the decisions made here, which are recorded in the mutable_target_params.
|
|
%
|
|
:- pred get_mutable_target_params(globals::in, mutable_var_attributes::in,
|
|
mutable_target_params::out) is det.
|
|
|
|
get_mutable_target_params(Globals, MutAttrs, TargetParams) :-
|
|
% The set of predicates we need depends on
|
|
% - the compilation target, since we use different implementations
|
|
% of mutables on different backends, and
|
|
% - on the properties of the mutable itself.
|
|
globals.get_target(Globals, CompilationTarget),
|
|
(
|
|
CompilationTarget = target_c,
|
|
ImplLang = mutable_lang_c,
|
|
Lang = lang_c,
|
|
PreInit0 = need_pre_init_pred,
|
|
LockUnlock0 = need_lock_unlock_preds,
|
|
UnsafeAccess0 = need_unsafe_get_set_preds,
|
|
globals.lookup_bool_option(Globals, highlevel_code, HighLevelCode),
|
|
(
|
|
HighLevelCode = no,
|
|
BoxPolicy = bp_always_boxed
|
|
;
|
|
HighLevelCode = yes,
|
|
BoxPolicy = bp_native_if_possible
|
|
)
|
|
;
|
|
CompilationTarget = target_csharp,
|
|
ImplLang = mutable_lang_csharp,
|
|
Lang = lang_csharp,
|
|
IsThreadLocal = mutable_var_thread_local(MutAttrs),
|
|
(
|
|
IsThreadLocal = mutable_thread_local,
|
|
PreInit0 = need_pre_init_pred
|
|
;
|
|
IsThreadLocal = mutable_not_thread_local,
|
|
PreInit0 = dont_need_pre_init_pred
|
|
),
|
|
LockUnlock0 = dont_need_lock_unlock_preds,
|
|
UnsafeAccess0 = need_unsafe_get_set_preds,
|
|
BoxPolicy = bp_native_if_possible
|
|
;
|
|
CompilationTarget = target_java,
|
|
ImplLang = mutable_lang_java,
|
|
Lang = lang_java,
|
|
PreInit0 = dont_need_pre_init_pred,
|
|
LockUnlock0 = dont_need_lock_unlock_preds,
|
|
UnsafeAccess0 = need_unsafe_get_set_preds,
|
|
BoxPolicy = bp_native_if_possible
|
|
),
|
|
IsConstant = mutable_var_constant(MutAttrs),
|
|
(
|
|
IsConstant = mutable_not_constant,
|
|
PreInit = PreInit0,
|
|
LockUnlock = LockUnlock0,
|
|
UnsafeAccess = UnsafeAccess0
|
|
;
|
|
IsConstant = mutable_constant,
|
|
PreInit = dont_need_pre_init_pred,
|
|
LockUnlock = dont_need_lock_unlock_preds,
|
|
UnsafeAccess = dont_need_unsafe_get_set_preds
|
|
),
|
|
compute_needed_private_mutable_aux_preds(PreInit, LockUnlock, UnsafeAccess,
|
|
PrivatePredKinds),
|
|
compute_needed_public_mutable_aux_preds(MutAttrs, PublicPredKinds),
|
|
TargetParams = mutable_target_params(ImplLang, Lang, BoxPolicy,
|
|
PreInit, LockUnlock, UnsafeAccess, PrivatePredKinds, PublicPredKinds).
|
|
|
|
% This predicate decides which of the private auxiliary predicates
|
|
% we should generate for a mutable.
|
|
%
|
|
% This same decisions for the public aux predicates are made by
|
|
% compute_needed_public_mutable_aux_preds in prog_mutable.m.
|
|
%
|
|
:- pred compute_needed_private_mutable_aux_preds(need_pre_init_pred::in,
|
|
need_lock_unlock_preds::in, need_unsafe_get_set_preds::in,
|
|
list(mutable_pred_kind)::out) is det.
|
|
|
|
compute_needed_private_mutable_aux_preds(PreInit, LockUnlock, UnsafeAccess,
|
|
PrivateAuxPreds) :-
|
|
% The logic of this code should match the logic of the
|
|
% define_aux_preds_for_mutable predicate above.
|
|
|
|
% Create the mutable initialisation predicate.
|
|
InitPreds = [mutable_pred_init],
|
|
|
|
% Create the pre-initialisation predicate,
|
|
% if needed by the initialisation predicate.
|
|
(
|
|
PreInit = dont_need_pre_init_pred,
|
|
PreInitPreds = []
|
|
;
|
|
PreInit = need_pre_init_pred,
|
|
PreInitPreds = [mutable_pred_pre_init]
|
|
),
|
|
|
|
% Create the primitive access and locking predicates, if needed.
|
|
(
|
|
UnsafeAccess = dont_need_unsafe_get_set_preds,
|
|
UnsafeAccessPreds = []
|
|
;
|
|
UnsafeAccess = need_unsafe_get_set_preds,
|
|
UnsafeAccessPreds = [mutable_pred_unsafe_get, mutable_pred_unsafe_set]
|
|
),
|
|
(
|
|
LockUnlock = dont_need_lock_unlock_preds,
|
|
LockUnlockPreds = []
|
|
;
|
|
LockUnlock = need_lock_unlock_preds,
|
|
LockUnlockPreds = [mutable_pred_lock, mutable_pred_unlock]
|
|
),
|
|
PrivateAuxPreds = InitPreds ++ PreInitPreds ++
|
|
UnsafeAccessPreds ++ LockUnlockPreds.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module hlds.make_hlds.add_mutable_aux_preds.
|
|
%---------------------------------------------------------------------------%
|