mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 13:23:53 +00:00
This should make error messages easier to read by removing clutter.
compiler/error_util.m:
Split each of the sym_name and sym_name_and_arity error pieces into two;
one which prints any module qualification present in the given sym_name,
and one which does not. This forces people who use these pieces
to think about whether they want the sym_name module qualified
in the error message or not.
compiler/add_class.m:
compiler/add_clause.m:
compiler/add_foreign_enum.m:
compiler/add_foreign_proc.m:
compiler/add_mode.m:
compiler/add_mutable_aux_preds.m:
compiler/add_pragma.m:
compiler/add_pragma_tabling.m:
compiler/add_pred.m:
compiler/add_type.m:
compiler/check_for_missing_type_defns.m:
compiler/check_promise.m:
compiler/check_raw_comp_unit.m:
compiler/check_typeclass.m:
compiler/det_report.m:
compiler/equiv_type.m:
compiler/format_call.m:
compiler/hlds_error_util.m:
compiler/inst_check.m:
compiler/introduce_parallelism.m:
compiler/make_hlds_error.m:
compiler/make_hlds_passes.m:
compiler/make_tags.m:
compiler/mercury_compile_main.m:
compiler/mode_errors.m:
compiler/modes.m:
compiler/module_qual.qual_errors.m:
compiler/modules.m:
compiler/oisu_check.m:
compiler/parse_inst_mode_defn.m:
compiler/parse_item.m:
compiler/parse_module.m:
compiler/parse_pragma.m:
compiler/parse_type_defn.m:
compiler/polymorphism.m:
compiler/post_term_analysis.m:
compiler/prog_out.m:
compiler/recompilation.check.m:
compiler/resolve_unify_functor.m:
compiler/split_parse_tree_src.m:
compiler/type_constraints.m:
compiler/typecheck_errors.m:
compiler/unused_args.m:
compiler/unused_imports.m:
Conform to the change above. For sym_name references for which
the module qualifier is obvious (usually because it *has* to be
the module being compiled), change the reference to the variant
that omits that qualifier; otherwise, keep the qualifier.
In a few places, improve the wording of an error message.
tests/invalid/bad_instance.err_exp:
tests/invalid/bug17.err_exp:
tests/invalid/builtin_int.err_exp:
tests/invalid/foreign_purity_mismatch.err_exp:
tests/invalid/foreign_type_visibility.err_exp:
tests/invalid/fp_dup_bug.err_exp:
tests/invalid/fundeps_vars.err_exp:
tests/invalid/impl_def_literal_syntax.err_exp:
tests/invalid/inline_conflict.err_exp:
tests/invalid/inst_list_dup.err_exp:
tests/invalid/instance_no_type.err_exp:
tests/invalid/invalid_typeclass.err_exp:
tests/invalid/missing_interface_import.err_exp:
tests/invalid/missing_interface_import2.err_exp:
tests/invalid/oisu_check_semantic_errors.err_exp:
tests/invalid/tc_err1.err_exp:
tests/invalid/tc_err2.err_exp:
tests/invalid/transitive_import.err_exp:
tests/invalid/type_with_no_defn.err_exp:
tests/invalid/typeclass_bogus_method.err_exp:
tests/invalid/typeclass_missing_mode_2.err_exp:
tests/invalid/typeclass_test_10.err_exp:
tests/invalid/typeclass_test_3.err_exp:
tests/invalid/typeclass_test_4.err_exp:
tests/invalid/typeclass_test_5.err_exp:
tests/invalid/typeclass_test_9.err_exp:
tests/invalid/types2.err_exp:
tests/invalid/undef_inst.err_exp:
tests/invalid/undef_mode.err_exp:
tests/invalid/undef_mode_and_no_clauses.err_exp:
tests/invalid/undef_type.err_exp:
tests/invalid/undef_type_mod_qual.err_exp:
tests/invalid/uu_type.err_exp:
tests/invalid/where_direct_arg.err_exp:
tests/invalid/where_direct_arg2.err_exp:
tests/invalid/wrong_type_arity.err_exp:
tests/recompilation/add_type_re.err_exp.2:
tests/recompilation/field_r.err_exp.2:
tests/recompilation/remove_type_re.err_exp.2:
tests/warnings/inst_with_no_type.exp:
Expect the updated versions of error messages.
1735 lines
74 KiB
Mathematica
1735 lines
74 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.
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- 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.error_util.
|
|
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% If the given mutable item is local to this module,
|
|
% add the declarations of its auxiliary predicates 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.
|
|
%
|
|
:- 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.error_msg_inst.
|
|
:- import_module hlds.make_hlds.add_clause.
|
|
:- import_module hlds.make_hlds.add_foreign_proc.
|
|
:- import_module hlds.make_hlds.make_hlds_error.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module mdbcomp.builtin_modules.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.builtin_lib_types.
|
|
:- 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 require.
|
|
:- 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(ItemExport),
|
|
check_mutable(ItemMutable, ItemExport, !.ModuleInfo, !Specs),
|
|
item_mercury_status_to_pred_status(ItemMercuryStatus, PredStatus),
|
|
add_aux_pred_decls_for_mutable(ItemMutable, PredStatus, NeedQual,
|
|
!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, item_export::in, module_info::in,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
check_mutable(ItemMutable, ItemExport, ModuleInfo, !Specs) :-
|
|
ItemMutable = item_mutable_info(MutableName,
|
|
_OrigType, _Type, OrigInst, Inst,
|
|
_InitTerm, _VarSetMutable, MutAttrs, Context, _SeqNum),
|
|
(
|
|
( ItemExport = item_export_nowhere
|
|
; ItemExport = item_export_only_submodules
|
|
)
|
|
;
|
|
ItemExport = item_export_anywhere,
|
|
error_is_exported(Context,
|
|
[decl("mutable"), words("declaration")], !Specs)
|
|
),
|
|
|
|
% 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
|
|
; CompilationTarget = target_erlang, ForeignLanguage = lang_erlang
|
|
),
|
|
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],
|
|
TrailMsg = simple_msg(Context, [always(TrailPieces)]),
|
|
TrailSpec = error_spec(severity_error,
|
|
phase_parse_tree_to_hlds, [TrailMsg]),
|
|
!: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_id)::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),
|
|
UserInstId = inst_id(UserInstSymName, UserInstArity),
|
|
( if
|
|
list.member(UserInstId, 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, UserInstId, InstDefn) then
|
|
InstDefn = hlds_inst_defn(DefnInstVarSet, _Params,
|
|
InstBody, _MMTC, _Context, _Status),
|
|
(
|
|
InstBody = eqv_inst(EqvInst),
|
|
DefnParentInsts = [UserInstId | ParentInsts],
|
|
check_mutable_inst(ModuleInfo, Context, DefnInstVarSet,
|
|
DefnParentInsts, EqvInst, !Specs)
|
|
;
|
|
InstBody = abstract_inst
|
|
)
|
|
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($module, $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($module, $pred, "not_reached")
|
|
).
|
|
|
|
:- pred check_mutable_bound_insts(module_info::in, prog_context::in,
|
|
inst_varset::in, list(inst_id)::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_id)::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_id)::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_id)::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],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs].
|
|
|
|
:- pred named_parents_to_pieces(list(inst_id)::in,
|
|
list(format_component)::out) is det.
|
|
|
|
named_parents_to_pieces([], []).
|
|
named_parents_to_pieces([InstId | InstIds], Pieces) :-
|
|
named_parent_to_pieces(InstId, HeadPieces),
|
|
named_parents_to_pieces(InstIds, TailPieces),
|
|
Pieces = HeadPieces ++ TailPieces.
|
|
|
|
:- pred named_parent_to_pieces(inst_id::in,
|
|
list(format_component)::out) is det.
|
|
|
|
named_parent_to_pieces(InstId, Pieces) :-
|
|
InstId = inst_id(InstName, InstArity),
|
|
Pieces = [words("in the expansion of the named inst"),
|
|
qual_sym_name_and_arity(sym_name_arity(InstName, InstArity)),
|
|
suffix(":"), nl].
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_aux_pred_decls_for_mutable(item_mutable_info::in,
|
|
pred_status::in, need_qualifier::in, module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_aux_pred_decls_for_mutable(ItemMutable, PredStatus, NeedQual,
|
|
!ModuleInfo, !Specs) :-
|
|
ItemMutable = item_mutable_info(MutableName,
|
|
_OrigType, Type, _OrigInst, Inst,
|
|
_InitTerm, _VarSetMutable, MutAttrs, Context, _SeqNum),
|
|
get_mutable_target_params(!.ModuleInfo, MutAttrs, MaybeTargetParams),
|
|
(
|
|
MaybeTargetParams = no
|
|
;
|
|
MaybeTargetParams = yes(TargetParams),
|
|
TargetParams = mutable_target_params(_ImplLang, _Lang, _BoxPolicy,
|
|
PreInit, LockUnlock, UnsafeAccess),
|
|
module_info_get_name(!.ModuleInfo, ModuleName),
|
|
|
|
% The logic of this code should match the logic of define_aux_preds.
|
|
% Parts of this logic are also duplicated (though they shouldn't be)
|
|
% in the parts of write_module_interface_files.m that handle mutables.
|
|
|
|
% Create the pre-initialisation predicate,
|
|
% if needed by the initialisation predicate.
|
|
(
|
|
PreInit = dont_need_pre_init_pred
|
|
;
|
|
PreInit = need_pre_init_pred,
|
|
add_mutable_pre_init_pred_decl(ModuleName, MutableName,
|
|
PredStatus, NeedQual, Context, !ModuleInfo, !Specs)
|
|
),
|
|
|
|
% Create the mutable initialisation predicate.
|
|
add_mutable_init_pred_decl(ModuleName, MutableName,
|
|
PredStatus, NeedQual, Context, !ModuleInfo, !Specs),
|
|
|
|
% Create the primitive access and locking predicates, if needed.
|
|
(
|
|
LockUnlock = dont_need_lock_unlock_preds
|
|
;
|
|
LockUnlock = need_lock_unlock_preds,
|
|
add_mutable_lock_pred_decl(ModuleName, MutableName,
|
|
PredStatus, NeedQual, Context, !ModuleInfo, !Specs),
|
|
add_mutable_unlock_pred_decl(ModuleName, MutableName,
|
|
PredStatus, NeedQual, Context, !ModuleInfo, !Specs)
|
|
),
|
|
(
|
|
UnsafeAccess = dont_need_unsafe_get_set_preds
|
|
;
|
|
UnsafeAccess = need_unsafe_get_set_preds,
|
|
add_mutable_unsafe_get_pred_decl(ModuleName, MutableName,
|
|
Type, Inst, PredStatus, NeedQual, Context,
|
|
!ModuleInfo, !Specs),
|
|
add_mutable_unsafe_set_pred_decl(ModuleName, MutableName,
|
|
Type, Inst, PredStatus, NeedQual, Context,
|
|
!ModuleInfo, !Specs)
|
|
),
|
|
|
|
IsConstant = mutable_var_constant(MutAttrs),
|
|
AttachToIO = mutable_var_attach_to_io_state(MutAttrs),
|
|
(
|
|
IsConstant = mutable_constant,
|
|
expect(unify(PreInit, dont_need_pre_init_pred),
|
|
$module, $pred, "PreInit = need_pre_init_pred"),
|
|
expect(unify(LockUnlock, dont_need_lock_unlock_preds),
|
|
$module, $pred, "LockUnlock = need_lock_unlock_preds"),
|
|
expect(unify(UnsafeAccess, dont_need_unsafe_get_set_preds),
|
|
$module, $pred, "UnsafeAccess = need_unsafe_get_set_preds"),
|
|
expect(unify(AttachToIO, mutable_dont_attach_to_io_state),
|
|
$module, $pred, "AttachToIO = mutable_attach_to_io_state"),
|
|
|
|
% We create the "get" access predicate, which is pure since
|
|
% it always returns the same value, but we must also create
|
|
% a secret "set" predicate for use by the initialization code.
|
|
ConstantGetPredDecl = constant_get_pred_decl(ModuleName,
|
|
MutableName, Type, Inst, Context),
|
|
ConstantSetPredDecl = constant_set_pred_decl(ModuleName,
|
|
MutableName, Type, Inst, Context),
|
|
add_pred_decl_info_for_mutable_aux_pred(ConstantGetPredDecl,
|
|
ModuleName, MutableName, mutable_pred_constant_get,
|
|
PredStatus, NeedQual, !ModuleInfo, !Specs),
|
|
add_pred_decl_info_for_mutable_aux_pred(ConstantSetPredDecl,
|
|
ModuleName, MutableName, mutable_pred_constant_secret_set,
|
|
PredStatus, NeedQual, !ModuleInfo, !Specs)
|
|
;
|
|
IsConstant = mutable_not_constant,
|
|
% Create the standard, non-pure access predicates. These are
|
|
% always created for non-constant mutables, even if the
|
|
% `attach_to_io_state' attribute has been specified.
|
|
StdGetPredDecl = std_get_pred_decl(ModuleName, MutableName,
|
|
Type, Inst, Context),
|
|
StdSetPredDecl = std_set_pred_decl(ModuleName, MutableName,
|
|
Type, Inst, Context),
|
|
add_pred_decl_info_for_mutable_aux_pred(StdGetPredDecl,
|
|
ModuleName, MutableName, mutable_pred_std_get,
|
|
PredStatus, NeedQual, !ModuleInfo, !Specs),
|
|
add_pred_decl_info_for_mutable_aux_pred(StdSetPredDecl,
|
|
ModuleName, MutableName, mutable_pred_std_set,
|
|
PredStatus, NeedQual, !ModuleInfo, !Specs),
|
|
|
|
% If requested, create pure access predicates using
|
|
% the I/O state as well.
|
|
(
|
|
AttachToIO = mutable_dont_attach_to_io_state
|
|
;
|
|
AttachToIO = mutable_attach_to_io_state,
|
|
IOGetPredDecl = io_get_pred_decl(ModuleName, MutableName,
|
|
Type, Inst, Context),
|
|
IOSetPredDecl = io_set_pred_decl(ModuleName, MutableName,
|
|
Type, Inst, Context),
|
|
add_pred_decl_info_for_mutable_aux_pred(IOGetPredDecl,
|
|
ModuleName, MutableName, mutable_pred_io_get,
|
|
PredStatus, NeedQual, !ModuleInfo, !Specs),
|
|
add_pred_decl_info_for_mutable_aux_pred(IOSetPredDecl,
|
|
ModuleName, MutableName, mutable_pred_io_set,
|
|
PredStatus, NeedQual, !ModuleInfo, !Specs)
|
|
)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Add predmode declarations for the four primitive operations.
|
|
%
|
|
:- pred add_mutable_unsafe_get_pred_decl(module_name::in, string::in,
|
|
mer_type::in, mer_inst::in, pred_status::in, need_qualifier::in,
|
|
prog_context::in, module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
:- pred add_mutable_unsafe_set_pred_decl(module_name::in, string::in,
|
|
mer_type::in, mer_inst::in, pred_status::in, need_qualifier::in,
|
|
prog_context::in, module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
:- pred add_mutable_lock_pred_decl(module_name::in, string::in,
|
|
pred_status::in, need_qualifier::in, prog_context::in,
|
|
module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
:- pred add_mutable_unlock_pred_decl(module_name::in, string::in,
|
|
pred_status::in, need_qualifier::in, prog_context::in,
|
|
module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_mutable_unsafe_get_pred_decl(ModuleName, MutableName, Type, Inst,
|
|
PredStatus, NeedQual, Context, !ModuleInfo, !Specs) :-
|
|
PredName = mutable_unsafe_get_pred_sym_name(ModuleName, MutableName),
|
|
ArgTypesAndModes = [type_and_mode(Type, out_mode(Inst))],
|
|
add_mutable_aux_pred_decl(ModuleName, MutableName, mutable_pred_unsafe_get,
|
|
PredName, ArgTypesAndModes, purity_semipure, PredStatus, NeedQual,
|
|
Context, !ModuleInfo, !Specs).
|
|
|
|
add_mutable_unsafe_set_pred_decl(ModuleName, MutableName, Type, Inst,
|
|
PredStatus, NeedQual, Context, !ModuleInfo, !Specs) :-
|
|
PredName = mutable_unsafe_set_pred_sym_name(ModuleName, MutableName),
|
|
ArgTypesAndModes = [type_and_mode(Type, in_mode(Inst))],
|
|
add_mutable_aux_pred_decl(ModuleName, MutableName, mutable_pred_unsafe_set,
|
|
PredName, ArgTypesAndModes, purity_impure, PredStatus, NeedQual,
|
|
Context, !ModuleInfo, !Specs).
|
|
|
|
add_mutable_lock_pred_decl(ModuleName, MutableName, PredStatus, NeedQual,
|
|
Context, !ModuleInfo, !Specs) :-
|
|
PredName = mutable_lock_pred_sym_name(ModuleName, MutableName),
|
|
ArgTypesAndModes = [],
|
|
add_mutable_aux_pred_decl(ModuleName, MutableName, mutable_pred_lock,
|
|
PredName, ArgTypesAndModes, purity_impure, PredStatus, NeedQual,
|
|
Context, !ModuleInfo, !Specs).
|
|
|
|
add_mutable_unlock_pred_decl(ModuleName, MutableName, PredStatus, NeedQual,
|
|
Context, !ModuleInfo, !Specs) :-
|
|
PredName = mutable_unlock_pred_sym_name(ModuleName, MutableName),
|
|
ArgTypesAndModes = [],
|
|
add_mutable_aux_pred_decl(ModuleName, MutableName, mutable_pred_unlock,
|
|
PredName, ArgTypesAndModes, purity_impure, PredStatus, NeedQual,
|
|
Context, !ModuleInfo, !Specs).
|
|
|
|
% Add a predmode declaration for the mutable initialisation predicate.
|
|
%
|
|
:- pred add_mutable_init_pred_decl(module_name::in, string::in,
|
|
pred_status::in, need_qualifier::in, prog_context::in,
|
|
module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_mutable_init_pred_decl(ModuleName, MutableName, PredStatus, NeedQual,
|
|
Context, !ModuleInfo, !Specs) :-
|
|
PredName = mutable_init_pred_sym_name(ModuleName, MutableName),
|
|
ArgTypesAndModes = [],
|
|
add_mutable_aux_pred_decl(ModuleName, MutableName, mutable_pred_pre_init,
|
|
PredName, ArgTypesAndModes, purity_impure, PredStatus, NeedQual,
|
|
Context, !ModuleInfo, !Specs).
|
|
|
|
% Add a predmode declaration for the mutable pre-initialisation
|
|
% predicate. For normal mutables, this initialises the mutex protecting
|
|
% the mutable. For thread-local mutables, this allocates an index
|
|
% into an array of thread-local mutable values.
|
|
%
|
|
:- pred add_mutable_pre_init_pred_decl(module_name::in, string::in,
|
|
pred_status::in, need_qualifier::in, prog_context::in,
|
|
module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_mutable_pre_init_pred_decl(ModuleName, MutableName, PredStatus, NeedQual,
|
|
Context, !ModuleInfo, !Specs) :-
|
|
PredName = mutable_pre_init_pred_sym_name(ModuleName, MutableName),
|
|
ArgTypesAndModes = [],
|
|
add_mutable_aux_pred_decl(ModuleName, MutableName, mutable_pred_pre_init,
|
|
PredName, ArgTypesAndModes, purity_impure, PredStatus, NeedQual,
|
|
Context, !ModuleInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_mutable_aux_pred_decl(module_name::in, string::in,
|
|
mutable_pred_kind::in, sym_name::in, list(type_and_mode)::in, purity::in,
|
|
pred_status::in, need_qualifier::in, prog_context::in,
|
|
module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_mutable_aux_pred_decl(ModuleName, MutableName, Kind, PredName,
|
|
ArgTypesAndModes, Purity, PredStatus, NeedQual, Context,
|
|
!ModuleInfo, !Specs) :-
|
|
PredOrigin = origin_mutable(ModuleName, MutableName, Kind),
|
|
ItemNumber = -1,
|
|
MaybeItemMercuryStatus = maybe.no,
|
|
TypeVarSet = varset.init,
|
|
InstVarSet = varset.init,
|
|
ExistQVars = [],
|
|
Constraints = constraints([], []),
|
|
marker_list_to_markers([marker_mutable_access_pred], Markers),
|
|
module_add_pred_or_func(PredOrigin, Context, ItemNumber,
|
|
MaybeItemMercuryStatus, PredStatus, NeedQual,
|
|
pf_predicate, PredName, TypeVarSet, InstVarSet, ExistQVars,
|
|
ArgTypesAndModes, Constraints, yes(detism_det),
|
|
Purity, Markers, _, !ModuleInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_pred_decl_info_for_mutable_aux_pred(item_pred_decl_info::in,
|
|
module_name::in, string::in, mutable_pred_kind::in,
|
|
pred_status::in, need_qualifier::in, module_info::in, module_info::out,
|
|
list(error_spec)::in, list(error_spec)::out) is det.
|
|
|
|
add_pred_decl_info_for_mutable_aux_pred(ItemPredDecl, ModuleName, MutableName,
|
|
Kind, PredStatus, NeedQual, !ModuleInfo, !Specs) :-
|
|
PredOrigin = origin_mutable(ModuleName, MutableName, Kind),
|
|
ItemNumber = -1,
|
|
MaybeItemMercuryStatus = maybe.no,
|
|
ItemPredDecl = item_pred_decl_info(PredName, PredOrFunc, TypesAndModes,
|
|
WithType, WithInst, MaybeDetism, _Origin, TypeVarSet, InstVarSet,
|
|
ExistQVars, Purity, Constraints, Context, _SeqNum),
|
|
expect(unify(TypeVarSet, varset.init), $module, $pred,
|
|
"TypeVarSet != varset.init"),
|
|
expect(unify(InstVarSet, varset.init), $module, $pred,
|
|
"InstVarSet != varset.init"),
|
|
expect(unify(ExistQVars, []), $module, $pred, "ExistQVars != []"),
|
|
expect(unify(PredOrFunc, pf_predicate), $module, $pred,
|
|
"PredOrFunc != pf_predicate"),
|
|
expect(unify(WithType, no), $module, $pred, "WithType != no"),
|
|
expect(unify(WithInst, no), $module, $pred, "WithInst != no"),
|
|
expect(unify(MaybeDetism, yes(detism_det)), $module, $pred,
|
|
"MaybeDet != yes(detism_det)"),
|
|
expect(unify(Constraints, constraints([], [])), $module, $pred,
|
|
"Constraints != constraints([], [])"),
|
|
marker_list_to_markers([marker_mutable_access_pred], Markers),
|
|
module_add_pred_or_func(PredOrigin, Context, ItemNumber,
|
|
MaybeItemMercuryStatus, PredStatus, NeedQual, PredOrFunc, PredName,
|
|
TypeVarSet, InstVarSet, ExistQVars, TypesAndModes, Constraints,
|
|
MaybeDetism, Purity, Markers, _, !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),
|
|
get_mutable_target_params(!.ModuleInfo, MutAttrs, MaybeTargetParams),
|
|
(
|
|
MaybeTargetParams = no
|
|
;
|
|
MaybeTargetParams = yes(TargetParams),
|
|
TargetParams = mutable_target_params(ImplLang, Lang, _BoxPolicy,
|
|
_PreInit, _LockUnlock, _UnsafeAccess),
|
|
IsConstant = mutable_var_constant(MutAttrs),
|
|
IsThreadLocal = mutable_var_thread_local(MutAttrs),
|
|
|
|
% 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_global_var_c(TargetMutableName, Type, IsConstant,
|
|
IsThreadLocal, Context, !ModuleInfo)
|
|
;
|
|
ImplLang = mutable_lang_csharp,
|
|
define_global_var_csharp(TargetMutableName, Type,
|
|
IsThreadLocal, Context, !ModuleInfo)
|
|
;
|
|
ImplLang = mutable_lang_java,
|
|
define_global_var_java( TargetMutableName, Type,
|
|
IsThreadLocal, Context, !ModuleInfo)
|
|
;
|
|
ImplLang = mutable_lang_erlang
|
|
% For the Erlang backend, we don't define any global variables;
|
|
% instead, the values of thread-local mutables are stored
|
|
% in the thread's process dictionary, and the values of
|
|
% non-thread-local mutables are stored in the
|
|
% ML_erlang_global_server process.
|
|
),
|
|
define_aux_preds(ItemMutable, TargetParams, 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_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_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(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_global_var_csharp(string::in, mer_type::in,
|
|
mutable_thread_local::in, prog_context::in,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
define_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_global_var_java(string::in, mer_type::in,
|
|
mutable_thread_local::in, prog_context::in,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
define_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(item_mutable_info::in, mutable_target_params::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(ItemMutable, TargetParams, TargetMutableName, PredStatus,
|
|
!ModuleInfo, !QualInfo, !Specs) :-
|
|
TargetParams = mutable_target_params(ImplLang, Lang, BoxPolicy,
|
|
PreInit, LockUnlock, UnsafeAccess),
|
|
|
|
% 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)
|
|
;
|
|
ImplLang = mutable_lang_erlang,
|
|
Attrs = Attrs0
|
|
),
|
|
|
|
% 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.
|
|
|
|
(
|
|
PreInit = dont_need_pre_init_pred,
|
|
MaybeCallPreInitExpr = no
|
|
;
|
|
PreInit = need_pre_init_pred,
|
|
define_pre_init_pred(ItemMutable, TargetParams, TargetMutableName,
|
|
Attrs, CallPreInitExpr, PredStatus,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
MaybeCallPreInitExpr = yes(CallPreInitExpr)
|
|
),
|
|
(
|
|
LockUnlock = dont_need_lock_unlock_preds,
|
|
MaybeLockUnlockExprs = no
|
|
;
|
|
LockUnlock = need_lock_unlock_preds,
|
|
define_lock_unlock_preds(ItemMutable, TargetParams, TargetMutableName,
|
|
Attrs, LockUnlockExprs, PredStatus,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
MaybeLockUnlockExprs = yes(LockUnlockExprs)
|
|
),
|
|
(
|
|
UnsafeAccess = dont_need_unsafe_get_set_preds,
|
|
MaybeUnsafeGetSetExprs = no
|
|
;
|
|
UnsafeAccess = need_unsafe_get_set_preds,
|
|
define_unsafe_get_set_preds(ItemMutable, TargetParams,
|
|
TargetMutableName, Attrs, UnsafeGetSetExprs, PredStatus,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
MaybeUnsafeGetSetExprs = yes(UnsafeGetSetExprs)
|
|
),
|
|
|
|
% 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(ItemMutable, TargetParams, TargetMutableName,
|
|
Attrs, MaybeLockUnlockExprs, MaybeUnsafeGetSetExprs, InitSetPredName,
|
|
PredStatus, !ModuleInfo, !QualInfo, !Specs),
|
|
|
|
% 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, MaybeCallPreInitExpr, InitSetPredName,
|
|
Lang, PredStatus, !ModuleInfo, !QualInfo, !Specs).
|
|
|
|
% Define the pre_init predicates, if needed by the init predicate.
|
|
%
|
|
:- pred define_pre_init_pred(item_mutable_info::in, mutable_target_params::in,
|
|
string::in, pragma_foreign_proc_attributes::in, goal::out,
|
|
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_pre_init_pred(ItemMutable, TargetParams, TargetMutableName, Attrs,
|
|
CallPreInitExpr, PredStatus, !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),
|
|
IsThreadLocal = mutable_var_thread_local(MutAttrs),
|
|
TargetParams = mutable_target_params(ImplLang, _Lang, _BoxPolicy,
|
|
_PreInit, _LockUnlock, _UnsafeAccess),
|
|
|
|
expect(unify(IsConstant, mutable_not_constant), $module, $pred,
|
|
"need_pre_init_pred, but IsConstant = mutable_constant"),
|
|
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($module, $pred, "preinit for java")
|
|
;
|
|
ImplLang = mutable_lang_erlang,
|
|
unexpected($module, $pred, "preinit for erlang")
|
|
),
|
|
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(item_mutable_info::in,
|
|
mutable_target_params::in, string::in, pragma_foreign_proc_attributes::in,
|
|
{goal, goal}::out, 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_lock_unlock_preds(ItemMutable, TargetParams, TargetMutableName, Attrs,
|
|
LockUnlockExprs, PredStatus, !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),
|
|
IsThreadLocal = mutable_var_thread_local(MutAttrs),
|
|
TargetParams = mutable_target_params(ImplLang, _Lang, _BoxPolicy,
|
|
_PreInit, _LockUnlock, _UnsafeAccess),
|
|
expect(unify(IsConstant, mutable_not_constant), $module, $pred,
|
|
"need_lock_unlock_preds, but IsConstant = mutable_constant"),
|
|
|
|
(
|
|
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($module, $pred, "lock_unlock for csharp")
|
|
;
|
|
ImplLang = mutable_lang_java,
|
|
unexpected($module, $pred, "lock_unlock for java")
|
|
;
|
|
ImplLang = mutable_lang_erlang,
|
|
unexpected($module, $pred, "lock_unlock for erlang")
|
|
).
|
|
|
|
% Define the unsafe get and set predicates, if needed.
|
|
%
|
|
:- pred define_unsafe_get_set_preds(item_mutable_info::in,
|
|
mutable_target_params::in, string::in, pragma_foreign_proc_attributes::in,
|
|
{goal, goal}::out, 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_unsafe_get_set_preds(ItemMutable, TargetParams, TargetMutableName,
|
|
Attrs, UnsafeGetSetExprs, PredStatus,
|
|
!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),
|
|
IsThreadLocal = mutable_var_thread_local(MutAttrs),
|
|
TargetParams = mutable_target_params(ImplLang, Lang, BoxPolicy,
|
|
_PreInit, _LockUnlock, _UnsafeAccess),
|
|
expect(unify(IsConstant, mutable_not_constant), $module, $pred,
|
|
"need_unsafe_get_set_preds, but IsConstant = mutable_constant"),
|
|
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"
|
|
)
|
|
;
|
|
ImplLang = mutable_lang_erlang,
|
|
unexpected($module, $pred, "unsafe_get_set for erlang")
|
|
),
|
|
|
|
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(item_mutable_info::in,
|
|
mutable_target_params::in, string::in, pragma_foreign_proc_attributes::in,
|
|
maybe({goal, goal})::in, maybe({goal, goal})::in,
|
|
sym_name::out, 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_main_get_set_preds(ItemMutable, TargetParams, TargetMutableName, Attrs,
|
|
MaybeLockUnlockExprs, MaybeUnsafeGetSetExprs, InitSetPredName,
|
|
PredStatus, !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),
|
|
IsThreadLocal = mutable_var_thread_local(MutAttrs),
|
|
AttachToIO = mutable_var_attach_to_io_state(MutAttrs),
|
|
TargetParams = mutable_target_params(ImplLang, _Lang, BoxPolicy,
|
|
_PreInit, _LockUnlock, _UnsafeAccess),
|
|
varset.new_named_var("X", X, varset.init, VarSetOnlyX),
|
|
|
|
(
|
|
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"
|
|
;
|
|
ImplLang = mutable_lang_erlang,
|
|
% These Erlang fragments duplicate those for non-thread-local
|
|
% non-constant mutables below.
|
|
ConstantGetCode =
|
|
string.append_list([
|
|
"'ML_erlang_global_server' ! {get_mutable, ",
|
|
TargetMutableName, ", self()},\n",
|
|
"receive\n",
|
|
" {get_mutable_ack, Value} ->\n",
|
|
" X = Value\n",
|
|
"end\n"
|
|
]),
|
|
ConstantSetCode =
|
|
"'ML_erlang_global_server' ! {set_mutable, " ++
|
|
TargetMutableName ++ ", X}"
|
|
),
|
|
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),
|
|
|
|
expect(unify(AttachToIO, mutable_dont_attach_to_io_state),
|
|
$module, $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($module, $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, ok1(StdGetPredExpr), PredStatus, Context, no,
|
|
goal_type_none, !ModuleInfo, !QualInfo, !Specs),
|
|
module_add_clause(VarSetOnlyX, pf_predicate, StdSetPredName,
|
|
StdPredArgs, ok1(StdSetPredExpr), PredStatus, Context, no,
|
|
goal_type_none, !ModuleInfo, !QualInfo, !Specs)
|
|
;
|
|
ImplLang = mutable_lang_erlang,
|
|
% NOTE We don't call the unsafe get/set predicates, since
|
|
% we don't declare/define them. We don't need them, because
|
|
% in Erlang we can do their job here directly, since (a)
|
|
% we don't need explicit locking, as the message passing
|
|
% system takes care of that, and (b) we don't need to trail
|
|
% the setting of the mutable, even if the mutable is nominally
|
|
% trailed, because the Erlang backend does not implement trailing.
|
|
set_thread_safe(proc_thread_safe, Attrs, ThreadSafeAttrs),
|
|
set_purity(purity_semipure, ThreadSafeAttrs, ErlangGetAttrs),
|
|
set_purity(purity_impure, ThreadSafeAttrs, ErlangSetAttrs),
|
|
(
|
|
IsThreadLocal = mutable_thread_local,
|
|
StdGetCode = "X = get({'MR_thread_local_mutable', " ++
|
|
TargetMutableName ++ "})",
|
|
StdSetCode = "put({'MR_thread_local_mutable', " ++
|
|
TargetMutableName ++ "}, X)"
|
|
;
|
|
IsThreadLocal = mutable_not_thread_local,
|
|
% These Erlang fragments duplicate those for
|
|
% constant mutables above.
|
|
StdGetCode =
|
|
string.append_list([
|
|
"'ML_erlang_global_server' ! {get_mutable, ",
|
|
TargetMutableName, ", self()},\n",
|
|
"receive\n",
|
|
" {get_mutable_ack, Value} ->\n",
|
|
" X = Value\n",
|
|
"end\n"
|
|
]),
|
|
StdSetCode =
|
|
"'ML_erlang_global_server' ! {set_mutable, " ++
|
|
TargetMutableName ++ ", X}"
|
|
),
|
|
StdGetFCInfo = pragma_info_foreign_proc(ErlangGetAttrs,
|
|
StdGetPredName,
|
|
pf_predicate,
|
|
[pragma_var(X, "X", out_mode(Inst), BoxPolicy)],
|
|
VarSetOnlyX, % ProgVarSet
|
|
varset.init, % InstVarSet
|
|
fp_impl_ordinary(StdGetCode, yes(Context))
|
|
),
|
|
StdSetFCInfo = pragma_info_foreign_proc(ErlangSetAttrs,
|
|
StdSetPredName,
|
|
pf_predicate,
|
|
[pragma_var(X, "X", in_mode(Inst), BoxPolicy)],
|
|
VarSetOnlyX, % ProgVarSet
|
|
varset.init, % InstVarSet
|
|
fp_impl_ordinary(StdSetCode, yes(Context))
|
|
),
|
|
add_pragma_foreign_proc(StdGetFCInfo, PredStatus, Context, no,
|
|
!ModuleInfo, !Specs),
|
|
add_pragma_foreign_proc(StdSetFCInfo, PredStatus, Context, no,
|
|
!ModuleInfo, !Specs),
|
|
|
|
ImpureGetExpr = call_expr(Context, StdGetPredName,
|
|
[variable(X, Context)], purity_semipure),
|
|
ImpureSetExpr = call_expr(Context, StdSetPredName,
|
|
[variable(X, Context)], purity_impure)
|
|
),
|
|
(
|
|
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, ok1(PureIOGetPredExpr), PredStatus, Context, no,
|
|
goal_type_none, !ModuleInfo, !QualInfo, !Specs),
|
|
module_add_clause(VarSetXandIOs, pf_predicate, IOSetPredName,
|
|
IOPredArgs, ok1(PureIOSetPredExpr), PredStatus, Context, no,
|
|
goal_type_none, !ModuleInfo, !QualInfo, !Specs)
|
|
)
|
|
).
|
|
|
|
% Define the init predicate, and arrange for it to be called
|
|
% at initialization time.
|
|
%
|
|
:- pred define_init_pred(item_mutable_info::in, maybe(goal)::in,
|
|
sym_name::in, foreign_language::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_init_pred(ItemMutable, MaybeCallPreInitExpr, InitSetPredName,
|
|
Lang, PredStatus, !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 for the reason why we _must_ pass
|
|
% VarSetMutableX here.
|
|
module_add_clause(VarSetMutableX, pf_predicate, InitPredName, [],
|
|
ok1(InitPredExpr), PredStatus, Context, no, goal_type_none,
|
|
!ModuleInfo, !QualInfo, !Specs),
|
|
|
|
InitPredArity = 0,
|
|
add_initialise_for_mutable(InitPredName, InitPredArity, Context,
|
|
Lang, !ModuleInfo, !Specs).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred add_initialise_for_mutable(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(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.
|
|
module_info_new_user_init_pred(SymName, Arity, CName, !ModuleInfo),
|
|
PredNameModesPF = pred_name_modes_pf(SymName, [], pf_predicate),
|
|
FPEInfo = pragma_info_foreign_proc_export(Lang, PredNameModesPF, CName),
|
|
Attrs = item_compiler_attributes(do_allow_export, is_mutable),
|
|
Origin = item_origin_compiler(Attrs),
|
|
add_pragma_foreign_proc_export(Origin, 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,
|
|
% This works for Erlang as well.
|
|
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 = [],
|
|
% This works for Erlang as well.
|
|
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],
|
|
Msg = simple_msg(Context, [always(Pieces)]),
|
|
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
|
|
!:Specs = [Spec | !.Specs],
|
|
|
|
% This works for Erlang as well.
|
|
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.
|
|
% XXX is that true for erlang?
|
|
%
|
|
:- func global_foreign_type_name(box_policy, foreign_language, module_info,
|
|
mer_type) = string.
|
|
|
|
global_foreign_type_name(bp_always_boxed, _, _, _) = "MR_Word".
|
|
global_foreign_type_name(bp_native_if_possible, Lang, ModuleInfo, Type) =
|
|
mercury_exported_type_to_string(ModuleInfo, Lang, Type).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type mutable_impl_lang
|
|
---> mutable_lang_c
|
|
; mutable_lang_csharp
|
|
; mutable_lang_java
|
|
; mutable_lang_erlang.
|
|
|
|
:- 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(
|
|
mutable_impl_lang,
|
|
foreign_language,
|
|
box_policy,
|
|
need_pre_init_pred,
|
|
need_lock_unlock_preds,
|
|
need_unsafe_get_set_preds
|
|
).
|
|
|
|
% 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(module_info::in, mutable_var_attributes::in,
|
|
maybe(mutable_target_params)::out) is det.
|
|
|
|
get_mutable_target_params(ModuleInfo, MutAttrs, MaybeTargetParams) :-
|
|
% 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.
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
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
|
|
;
|
|
CompilationTarget = target_erlang,
|
|
ImplLang = mutable_lang_erlang,
|
|
Lang = lang_erlang,
|
|
PreInit0 = dont_need_pre_init_pred,
|
|
LockUnlock0 = dont_need_lock_unlock_preds,
|
|
UnsafeAccess0 = dont_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
|
|
),
|
|
TargetParams = mutable_target_params(ImplLang, Lang, BoxPolicy,
|
|
PreInit, LockUnlock, UnsafeAccess),
|
|
MaybeTargetParams = yes(TargetParams)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module hlds.make_hlds.add_mutable_aux_preds.
|
|
%---------------------------------------------------------------------------%
|