Files
mercury/compiler/make_hlds_passes.m
Zoltan Somogyi 6d43c71948 Implement semantic checks for oisu (order independent state update) pragmas.
Estimated hours taken: 30
Branches: main

Implement semantic checks for oisu (order independent state update) pragmas.

compiler/hlds_pred.m:
	Record for each procedure whether it implements an operation
	on a oisu type, and if yes, what kind of operation.

compiler/hlds_module.m:
	Add to module_infos a data structure that lists the oisu pragmas
	in the module, and the procedures mentioned in them. These are intended
	to be used later during code generation.

compiler/add_pragma.m:
	Add such pragmas to the HLDS, after checking whatever properties
	can be checked during the creation of the HLDS.

compiler/oisu_check.m:
	A new module, whose job it is to check those aspects of oisu pragmas
	that can be checked only after other semantics are complete on the
	module.

compiler/check_hlds.m:
	Add the new module.

compiler/notes/compiler_design.html:
	Document the new module.

compiler/mercury_compile_front_end.m:
	Invoke the new module.

compiler/error_util.m:
	Add the new semantic check as a phase.

compiler/mercury_to_mercury.m:
	Fix typos in the code for writing out oisu pragmas.

compiler/prog_io_pragmas.m:
	Fix typos in the code for reading in oisu pragmas.

compiler/module_qual.m:
	Improve the error messages generated for any problems discovered during
	module qualification inside pragmas, by writing out what *kind* of
	pragma the problem was discovered in.

compiler/modules.m:
	Fix a bug: oisu pragmas *can* appear in module interfaces.

compiler/stratify.m:
	Give a predicate a better interface and a name.

compiler/hlds_goal.m:
	Remove a duplicate comment.

compiler/make_hlds_passes.m:
	Fix formatting.

tests/hard_coded/oisu_check_main.{m,exp}:
tests/hard_coded/oisu_check_db.m:
	A new multimodule test case, which uses oisu pragmas correctly.

tests/invalid/oisu_check_add_pragma_errors.{m,err_exp}:
	A new test case, which tests add_pragma.m's ability to diagnose
	the problems it is supposed to diagnose.

tests/invalid/oisu_check_semantic_errors.{m,err_exp}:
	A new test case, which tests oisu_check.m's ability to diagnose
	the problems it is supposed to diagnose.

tests/hard_coded/Mmakefile:
tests/invalid/Mmakefile:
	Enable the new test cases.
2012-10-08 04:14:49 +00:00

3370 lines
136 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1993-2012 The University of Melbourne.
% 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.make_hlds_passes.
:- interface.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module hlds.make_hlds.qual_info.
:- import_module mdbcomp.prim_data.
:- import_module parse_tree.equiv_type.
:- import_module parse_tree.error_util.
:- import_module parse_tree.module_qual.
:- import_module parse_tree.prog_data.
:- import_module bool.
:- import_module list.
:- import_module term.
%-----------------------------------------------------------------------------%
% When adding an item to the HLDS we need to know both its
% import_status and whether uses of it must be module qualified.
%
:- type item_status
---> item_status(import_status, need_qualifier).
% do_parse_tree_to_hlds(Globals, DumpBaseFileName, ParseTree, MQInfo,
% EqvMap, UsedModules, QualInfo, InvalidTypes, InvalidModes, HLDS,
% Specs):
%
% Given MQInfo (returned by module_qual.m) and EqvMap and UsedModules
% (both returned by equiv_type.m), converts ParseTree to HLDS.
% Any errors found are returned in Specs.
% Returns InvalidTypes = yes if undefined types found.
% Returns InvalidModes = yes if undefined or cyclic insts or modes found.
% QualInfo is an abstract type that is then passed back to
% produce_instance_method_clauses (see below).
%
:- pred do_parse_tree_to_hlds(globals::in, string::in, compilation_unit::in,
mq_info::in, eqv_map::in, used_modules::in, qual_info::out,
bool::out, bool::out, module_info::out, list(error_spec)::out) is det.
% The bool records whether any cyclic insts or modes were detected.
%
:- pred add_item_decl_pass_1(item::in, bool::out,
item_status::in, item_status::out, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
:- pred add_item_pass_3(item::in, import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
:- pred add_stratified_pred(string::in, sym_name::in, arity::in,
term.context::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
% add_pred_marker(PragmaName, Name, Arity, Status,
% Context, Marker, ConflictMarkers, !ModuleInfo, !Specs):
%
% Adds Marker to the marker list of the pred(s) with give Name and Arity,
% updating the ModuleInfo. If the named pred does not exist, or the pred
% already has a marker in ConflictMarkers, report an error.
%
:- pred add_pred_marker(string::in, sym_name::in, arity::in, import_status::in,
prog_context::in, marker::in, list(marker)::in,
module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
:- type add_marker_pred_info == pred(pred_info, pred_info).
:- inst add_marker_pred_info == (pred(in, out) is det).
:- pred do_add_pred_marker(string::in, sym_name::in, arity::in,
import_status::in, bool::in, term.context::in,
add_marker_pred_info::in(add_marker_pred_info),
module_info::in, module_info::out, list(pred_id)::out,
list(error_spec)::in, list(error_spec)::out) is det.
:- pred module_mark_as_external(sym_name::in, int::in, prog_context::in,
module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
:- pred maybe_check_field_access_function(sym_name::in, arity::in,
import_status::in, prog_context::in, module_info::in,
list(error_spec)::in, list(error_spec)::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.foreign.
:- import_module check_hlds.clause_to_proc.
:- import_module check_hlds.type_util.
:- import_module hlds.hlds_code_util.
:- import_module hlds.hlds_data.
:- import_module hlds.hlds_out.
:- import_module hlds.hlds_out.hlds_out_mode.
:- import_module hlds.make_hlds.add_class.
:- import_module hlds.make_hlds.add_clause.
:- import_module hlds.make_hlds.add_mode.
:- import_module hlds.make_hlds.add_pragma.
:- import_module hlds.make_hlds.add_pred.
:- import_module hlds.make_hlds.add_solver.
:- import_module hlds.make_hlds.add_special_pred.
:- import_module hlds.make_hlds.add_type.
:- import_module hlds.make_hlds.make_hlds_error.
:- import_module hlds.make_hlds.make_hlds_warn.
:- import_module hlds.make_hlds.qual_info.
:- import_module hlds.pred_table.
:- import_module hlds.special_pred.
:- import_module libs.file_util.
:- import_module libs.globals.
:- import_module libs.options.
:- import_module parse_tree.builtin_lib_types.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_mode.
:- import_module parse_tree.prog_mutable.
:- import_module parse_tree.prog_out.
:- import_module parse_tree.prog_type.
:- import_module parse_tree.prog_util.
:- import_module recompilation.
:- import_module int.
:- import_module map.
:- import_module pair.
:- import_module require.
:- import_module set.
:- import_module solutions.
:- import_module string.
:- import_module varset.
%-----------------------------------------------------------------------------%
do_parse_tree_to_hlds(Globals, DumpBaseFileName, unit_module(Name, Items),
MQInfo0, EqvMap, UsedModules, QualInfo, InvalidTypes, InvalidModes,
!:ModuleInfo, !:Specs) :-
mq_info_get_partial_qualifier_info(MQInfo0, PQInfo),
module_info_init(Name, DumpBaseFileName, Items, Globals, PQInfo, no,
!:ModuleInfo),
module_info_set_used_modules(UsedModules, !ModuleInfo),
!:Specs = [],
add_item_list_decls_pass_1(Items,
item_status(status_local, may_be_unqualified), !ModuleInfo,
no, InvalidModes0, !Specs),
globals.lookup_bool_option(Globals, statistics, Statistics),
trace [io(!IO)] (
maybe_write_string(Statistics, "% Processed all items in pass 1\n",
!IO),
maybe_report_stats(Statistics, !IO)
),
add_item_list_decls_pass_2(Items,
item_status(status_local, may_be_unqualified),
!ModuleInfo, [], Pass2Specs),
(
Pass2Specs = [],
InvalidTypes1 = no
;
Pass2Specs = [_ | _],
InvalidTypes1 = yes
),
!:Specs = Pass2Specs ++ !.Specs,
(
InvalidTypes1 = no,
some [!TypeTable] (
% Figure out how arguments should be stored into fields
% before constructors are added to the HLDS.
module_info_get_type_table(!.ModuleInfo, !:TypeTable),
foldl_over_type_ctor_defns(decide_du_type_layout(!.ModuleInfo),
!.TypeTable, !TypeTable),
module_info_set_type_table(!.TypeTable, !ModuleInfo),
% Add constructors and special preds to the HLDS. This must be done
% after adding all type and `:- pragma foreign_type' declarations.
% If there were errors in foreign type type declarations, doing
% this may cause a compiler abort.
foldl3_over_type_ctor_defns(process_type_defn, !.TypeTable,
no, InvalidTypes2, !ModuleInfo, !Specs)
)
;
InvalidTypes1 = yes,
InvalidTypes2 = yes
),
% Add the special preds for the builtin types which don't have a
% type declaration, hence no hlds_type_defn is generated for them.
(
Name = mercury_public_builtin_module,
compiler_generated_rtti_for_builtins(!.ModuleInfo)
->
list.foldl(add_builtin_type_ctor_special_preds,
builtin_type_ctors_with_no_hlds_type_defn, !ModuleInfo)
;
true
),
% Balance any data structures that need it.
module_info_optimize(!ModuleInfo),
trace [io(!IO)] (
maybe_write_string(Statistics, "% Processed all items in pass 2\n",
!IO),
maybe_report_stats(Statistics, !IO)
),
init_qual_info(MQInfo0, EqvMap, QualInfo0),
add_item_list_pass_3(Items, status_local, !ModuleInfo, QualInfo0, QualInfo,
!Specs),
trace [io(!IO)] (
maybe_write_string(Statistics, "% Processed all items in pass 3\n",
!IO)
),
qual_info_get_mq_info(QualInfo, MQInfo),
mq_info_get_type_error_flag(MQInfo, InvalidTypes3),
InvalidTypes = InvalidTypes1 `or` InvalidTypes2 `or` InvalidTypes3,
mq_info_get_mode_error_flag(MQInfo, InvalidModes1),
InvalidModes = InvalidModes0 `or` InvalidModes1.
%-----------------------------------------------------------------------------%
:- pred decide_du_type_layout(module_info::in, type_ctor::in,
hlds_type_defn::in, type_table::in, type_table::out) is det.
decide_du_type_layout(ModuleInfo, TypeCtor, TypeDefn, !TypeTable) :-
get_type_defn_body(TypeDefn, Body0),
(
Body0 = hlds_du_type(Ctors0, ConsTagValues, MaybeCheaperTagTest,
DuKind, MaybeUserEqComp, DirectArgFunctors, ReservedTag,
ReservedAddr, MaybeForeign),
list.map(layout_du_ctor_args(ModuleInfo, DuKind), Ctors0, Ctors),
Body = hlds_du_type(Ctors, ConsTagValues, MaybeCheaperTagTest,
DuKind, MaybeUserEqComp, DirectArgFunctors, ReservedTag,
ReservedAddr, MaybeForeign),
set_type_defn_body(Body, TypeDefn, PackedTypeDefn),
replace_type_ctor_defn(TypeCtor, PackedTypeDefn, !TypeTable)
;
( Body0 = hlds_eqv_type(_)
; Body0 = hlds_foreign_type(_)
; Body0 = hlds_solver_type(_, _)
; Body0 = hlds_abstract_type(_)
)
% Leave these types alone.
).
:- pred layout_du_ctor_args(module_info::in, du_type_kind::in,
constructor::in, constructor::out) is det.
layout_du_ctor_args(ModuleInfo, DuKind, Ctor0, Ctor) :-
Ctor0 = ctor(ExistTVars, Constraints, Name, Args0, Context),
module_info_get_globals(ModuleInfo, Globals),
(
( DuKind = du_type_kind_mercury_enum
; DuKind = du_type_kind_foreign_enum(_)
; DuKind = du_type_kind_direct_dummy
; DuKind = du_type_kind_notag(_, _, _)
),
Args1 = Args0
;
DuKind = du_type_kind_general,
% A functor with a single float argument can have a double-width word
% if it is not a no-tag functor. An example is `poly_type.f(float)'.
( use_double_word_floats(Globals, yes) ->
set_double_word_floats(ModuleInfo, Args0, Args1)
;
Args1 = Args0
)
),
globals.lookup_bool_option(Globals, allow_argument_packing, ArgPacking),
(
ArgPacking = yes,
globals.lookup_int_option(Globals, bits_per_word, TargetWordBits),
pack_du_ctor_args(ModuleInfo, TargetWordBits, 0, Args1, Args2, _),
WorthPacking = worth_arg_packing(Args1, Args2),
(
WorthPacking = yes,
Args = Args2
;
WorthPacking = no,
Args = Args1
)
;
ArgPacking = no,
Args = Args1
),
Ctor = ctor(ExistTVars, Constraints, Name, Args, Context).
:- pred use_double_word_floats(globals::in, bool::out) is det.
use_double_word_floats(Globals, DoubleWordFloats) :-
globals.get_target(Globals, Target),
globals.lookup_int_option(Globals, bits_per_word, TargetWordBits),
globals.lookup_bool_option(Globals, single_prec_float, SinglePrecFloat),
(
Target = target_c,
(
TargetWordBits = 32,
SinglePrecFloat = no
->
DoubleWordFloats = yes
;
DoubleWordFloats = no
)
;
( Target = target_il
; Target = target_csharp
; Target = target_java
; Target = target_asm
; Target = target_x86_64
; Target = target_erlang
),
DoubleWordFloats = no
).
:- pred set_double_word_floats(module_info::in,
list(constructor_arg)::in, list(constructor_arg)::out) is det.
set_double_word_floats(_ModuleInfo, [], []).
set_double_word_floats(ModuleInfo, [Arg0 | Args0], [Arg | Args]) :-
Arg0 = ctor_arg(Name, Type, _, Context),
( type_is_float_eqv(ModuleInfo, Type) ->
ArgWidth = double_word,
Arg = ctor_arg(Name, Type, ArgWidth, Context)
;
Arg = Arg0
),
set_double_word_floats(ModuleInfo, Args0, Args).
:- pred pack_du_ctor_args(module_info::in, int::in, int::in,
list(constructor_arg)::in, list(constructor_arg)::out,
arg_width::out) is det.
pack_du_ctor_args(_ModuleInfo, _TargetWordBits, _Shift, [], [],
full_word).
pack_du_ctor_args(ModuleInfo, TargetWordBits, Shift,
[Arg0 | Args0], [Arg | Args], ArgWidth) :-
Arg0 = ctor_arg(Name, Type, ArgWidth0, Context),
( type_is_enum_bits(ModuleInfo, Type, NumBits) ->
Mask = int.pow(2, NumBits) - 1,
% Try to place the argument in the current word, otherwise move on to
% the next word.
( Shift + NumBits > TargetWordBits ->
ArgWidth1 = partial_word_first(Mask),
NextShift = NumBits
; Shift = 0 ->
ArgWidth1 = partial_word_first(Mask),
NextShift = NumBits
;
ArgWidth1 = partial_word_shifted(Shift, Mask),
NextShift = Shift + NumBits
),
pack_du_ctor_args(ModuleInfo, TargetWordBits, NextShift, Args0, Args,
NextArgWidth),
% If this argument starts a word but the next argument is not packed
% with it, then this argument is not packed.
(
ArgWidth1 = partial_word_first(_),
NextArgWidth \= partial_word_shifted(_, _)
->
ArgWidth = full_word
;
ArgWidth = ArgWidth1
),
Arg = ctor_arg(Name, Type, ArgWidth, Context)
;
Arg = Arg0,
ArgWidth = ArgWidth0,
NextShift = 0,
pack_du_ctor_args(ModuleInfo, TargetWordBits, NextShift, Args0, Args,
_)
).
:- pred type_is_enum_bits(module_info::in, mer_type::in, int::out) is semidet.
type_is_enum_bits(ModuleInfo, Type, NumBits) :-
type_to_type_defn_body(ModuleInfo, Type, TypeBody),
TypeCategory = classify_type_defn_body(TypeBody),
(
TypeCategory = ctor_cat_enum(cat_enum_mercury),
NumBits = cons_tags_bits(TypeBody ^ du_type_cons_tag_values)
;
TypeCategory = ctor_cat_user(cat_user_general),
TypeBody = hlds_abstract_type(abstract_enum_type(NumBits))
).
:- func cons_tags_bits(cons_tag_values) = int.
cons_tags_bits(ConsTagValues) = NumBits :-
map.foldl_values(max_int_tag, ConsTagValues, 0, MaxFunctor),
int.log2(MaxFunctor + 1, NumBits).
:- pred max_int_tag(cons_tag::in, int::in, int::out) is det.
max_int_tag(ConsTag, !Max) :-
( ConsTag = int_tag(Int) ->
int.max(Int, !Max)
;
unexpected($module, $pred, "non-integer value for enumeration")
).
:- func worth_arg_packing(list(constructor_arg), list(constructor_arg)) = bool.
worth_arg_packing(UnpackedArgs, PackedArgs) = Worthwhile :-
count_words(UnpackedArgs, 0, UnpackedLength),
count_words(PackedArgs, 0, PackedLength),
expect(PackedLength =< UnpackedLength, $module, $pred,
"packed length exceeds unpacked length"),
% Boehm GC will round up allocations (at least) to the next even number
% of words. There is no point saving a single word if that word will be
% allocated anyway.
( round_to_even(PackedLength) < round_to_even(UnpackedLength) ->
Worthwhile = yes
;
Worthwhile = no
).
:- pred count_words(list(constructor_arg)::in, int::in, int::out) is det.
count_words([], !Count).
count_words([Arg | Args], !Count) :-
ArgWidth = Arg ^ arg_width,
(
ArgWidth = full_word,
!:Count = !.Count + 1
;
ArgWidth = double_word,
!:Count = !.Count + 2
;
ArgWidth = partial_word_first(_),
!:Count = !.Count + 1
;
ArgWidth = partial_word_shifted(_Shift, _Mask)
),
count_words(Args, !Count).
:- func round_to_even(int) = int.
round_to_even(I) = (int.even(I) -> I ; I + 1).
%-----------------------------------------------------------------------------%
:- pred add_builtin_type_ctor_special_preds(type_ctor::in,
module_info::in, module_info::out) is det.
add_builtin_type_ctor_special_preds(TypeCtor, !ModuleInfo) :-
varset.init(TVarSet),
Body = hlds_abstract_type(abstract_type_general),
term.context_init(Context),
Status = status_local,
construct_type(TypeCtor, [], Type),
% XXX We call `eagerly_add_special_preds' instead of `add_special_preds'
% to bypass a call to `special_pred_is_generated_lazily' which calls
% `classify_type_ctor'. `classify_type_ctor' knows about unqualified
% builtin types, but not the qualified types like `builtin.int'/0 from
% `builtin_type_ctors_with_no_hlds_type_defn'. Eventually it tries to
% look up the builtin type from the type definition table, and aborts as
% it won't find it.
%
% The special preds for these types shouldn't be generated lazily anyway.
eagerly_add_special_preds(TVarSet, Type, TypeCtor, Body, Context, Status,
!ModuleInfo).
%-----------------------------------------------------------------------------%
% pass 1:
% Add the declarations one by one to the module,
% except for type definitions and pragmas.
%
% The `InvalidModes' bool records whether we detected
% any cyclic insts or modes.
%
:- pred add_item_list_decls_pass_1(list(item)::in, item_status::in,
module_info::in, module_info::out, bool::in, bool::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_item_list_decls_pass_1([], _, !ModuleInfo, !InvalidModes, !Specs).
add_item_list_decls_pass_1([Item | Items], !.Status, !ModuleInfo,
!InvalidModes, !Specs) :-
add_item_decl_pass_1(Item, NewInvalidModes, !Status, !ModuleInfo, !Specs),
!:InvalidModes = bool.or(!.InvalidModes, NewInvalidModes),
add_item_list_decls_pass_1(Items, !.Status, !ModuleInfo, !InvalidModes,
!Specs).
% pass 2:
% Add the type definitions and pragmas one by one to the module,
% and add default modes for functions with no mode declaration.
%
% Adding type definitions needs to come after we have added the pred
% declarations, since we need to have the pred_id for `index/2' and
% `compare/3' when we add compiler-generated clauses for `compare/3'.
% (And similarly for other compiler-generated predicates like that.)
%
% Adding pragmas needs to come after we have added the
% pred declarations, in order to allow the pragma declarations
% for a predicate to syntactically precede the pred declaration.
%
% Adding default modes for functions needs to come after we have
% processed all the mode declarations, since otherwise we can't be
% sure that there isn't a mode declaration for the function.
%
:- pred add_item_list_decls_pass_2(list(item)::in, item_status::in,
module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_item_list_decls_pass_2([], _, !ModuleInfo, !Specs).
add_item_list_decls_pass_2([Item | Items], !.Status, !ModuleInfo, !Specs) :-
add_item_decl_pass_2(Item, !Status, !ModuleInfo, !Specs),
add_item_list_decls_pass_2(Items, !.Status, !ModuleInfo, !Specs).
% pass 3:
% Add the clauses one by one to the module.
%
% Check that the declarations for field extraction and update functions
% are sensible.
%
% Check that predicates listed in `:- initialise' and `:- finalise'
% declarations exist and have the correct signature, introduce
% pragma foreign_export declarations for them and record their exported
% name in the module_info so that we can generate code to call it at
% initialisation/finalisation time.
%
:- pred add_item_list_pass_3(list(item)::in, import_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_item_list_pass_3([], _Status, !ModuleInfo, !QualInfo, !Specs).
add_item_list_pass_3([Item | Items], Status0, !ModuleInfo, !QualInfo,
!Specs) :-
add_item_pass_3(Item, Status0, Status1, !ModuleInfo, !QualInfo, !Specs),
add_item_list_pass_3(Items, Status1, !ModuleInfo, !QualInfo, !Specs).
%-----------------------------------------------------------------------------%
add_item_decl_pass_1(Item, FoundError, !Status, !ModuleInfo, !Specs) :-
(
Item = item_module_start(_),
FoundError = no
;
Item = item_module_end(_),
FoundError = no
;
Item = item_module_defn(ItemModuleDefn),
add_pass_1_module_defn(ItemModuleDefn, !Status, !ModuleInfo, !Specs),
FoundError = no
;
Item = item_type_defn(ItemTypeDefnInfo),
add_pass_1_type_defn(ItemTypeDefnInfo, !Status, !ModuleInfo, !Specs),
FoundError = no
;
Item = item_inst_defn(ItemInstDefnInfo),
module_add_inst_defn(ItemInstDefnInfo, FoundError,
!.Status, !ModuleInfo, !Specs)
;
Item = item_mode_defn(ItemModeDefnInfo),
module_add_mode_defn(ItemModeDefnInfo, FoundError,
!.Status, !ModuleInfo, !Specs)
;
Item = item_pred_decl(ItemPredDecl),
add_pass_1_pred_decl(ItemPredDecl, !.Status, !ModuleInfo, !Specs),
FoundError = no
;
Item = item_mode_decl(ItemModeDecl),
add_pass_1_mode_decl(ItemModeDecl, !.Status, !ModuleInfo, !Specs),
FoundError = no
;
Item = item_typeclass(ItemTypeClass),
module_add_class_defn(ItemTypeClass, !.Status, !ModuleInfo, !Specs),
FoundError = no
;
Item = item_mutable(ItemMutable),
add_pass_1_mutable(ItemMutable, !.Status, !ModuleInfo, !Specs),
FoundError = no
;
( Item = item_clause(_)
; Item = item_pragma(_)
; Item = item_promise(_)
; Item = item_instance(_)
; Item = item_initialise(_)
; Item = item_finalise(_)
; Item = item_nothing(_)
),
% These will be processed only in later passes.
%
% We don't want to add clauses or pragma foreign_procs before we add
% the declarations of the predicates they implement.
%
% We don't want to add instance declarations before the typeclass
% declaration it implements.
FoundError = no
).
:- pred add_pass_1_type_defn(item_type_defn_info::in,
item_status::in, item_status::out, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_1_type_defn(ItemTypeDefnInfo, !Status, !ModuleInfo, !Specs) :-
% If this is a solver type then we need to also add the declarations
% for the compiler generated construction function and deconstruction
% predicate for the special constrained data constructor.
%
% In pass 3 we add the corresponding clauses.
%
% Before switch detection, we turn calls to these functions/predicates
% into ordinary constructions/deconstructions, but preserve the
% corresponding impurity annotations.
ItemTypeDefnInfo = item_type_defn_info(TVarSet, SymName, TypeParams,
TypeDefn, _Cond, Context, _SeqNum),
( TypeDefn = parse_tree_solver_type(SolverTypeDetails, _MaybeUserEqComp) ->
add_solver_type_decl_items(TVarSet, SymName, TypeParams,
SolverTypeDetails, Context, !Status, !ModuleInfo, !Specs),
MutableItems = SolverTypeDetails ^ std_mutable_items,
add_solver_type_mutable_items_pass_1(MutableItems, !.Status,
!ModuleInfo, !Specs)
;
true
).
:- pred add_pass_1_pred_decl(item_pred_decl_info::in,
item_status::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_1_pred_decl(ItemPredDecl, Status, !ModuleInfo, !Specs) :-
ItemPredDecl = item_pred_decl_info(Origin, TypeVarSet, InstVarSet,
ExistQVars, PredOrFunc, PredName, TypesAndModes, _WithType, _WithInst,
MaybeDet, _Cond, Purity, ClassContext, Context, _SeqNum),
init_markers(Markers0),
% If this predicate was added as a result of the mutable transformation
% then mark this predicate as a mutable access pred. We do this so that
% we can tell optimizations, like inlining, to treat it specially.
(
Origin = compiler(Reason),
(
Reason = mutable_decl,
add_marker(marker_mutable_access_pred, Markers0, Markers)
;
( Reason = initialise_decl
; Reason = finalise_decl
; Reason = solver_type
; Reason = pragma_memo_attribute
; Reason = foreign_imports
),
Markers = Markers0
)
;
Origin = user,
Markers = Markers0
),
module_add_pred_or_func(TypeVarSet, InstVarSet, ExistQVars,
PredOrFunc, PredName, TypesAndModes, MaybeDet, Purity, ClassContext,
Markers, Context, Status, _, !ModuleInfo, !Specs).
:- pred add_pass_1_mode_decl(item_mode_decl_info::in,
item_status::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_1_mode_decl(ItemModeDecl, Status, !ModuleInfo, !Specs) :-
ItemModeDecl = item_mode_decl_info(VarSet, MaybePredOrFunc, PredName,
Modes, _WithInst, MaybeDet, _Cond, Context, _SeqNum),
(
MaybePredOrFunc = yes(PredOrFunc),
Status = item_status(ImportStatus, _),
IsClassMethod = no,
module_add_mode(VarSet, PredName, Modes, MaybeDet, ImportStatus,
Context, PredOrFunc, IsClassMethod, _, !ModuleInfo, !Specs)
;
MaybePredOrFunc = no,
% equiv_type.m should have either set the pred_or_func
% or removed the item from the list.
unexpected($module, $pred, "no pred_or_func on mode declaration")
).
:- pred add_pass_1_module_defn(item_module_defn_info::in,
item_status::in, item_status::out, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_1_module_defn(ItemModuleDefn, !Status, !ModuleInfo, !Specs) :-
ItemModuleDefn = item_module_defn_info(ModuleDefn, Context, _SeqNum),
( module_defn_update_import_status(ModuleDefn, StatusPrime) ->
!:Status = StatusPrime
;
(
( ModuleDefn = md_import(ModuleSpecifiers)
; ModuleDefn = md_use(ModuleSpecifiers)
),
!.Status = item_status(IStat, _),
add_module_specifiers(ModuleSpecifiers, IStat, !ModuleInfo)
;
ModuleDefn = md_external(MaybeBackend, External),
( External = name_arity(Name, Arity) ->
module_info_get_globals(!.ModuleInfo, Globals),
CurrentBackend = lookup_current_backend(Globals),
(
(
MaybeBackend = no
;
MaybeBackend = yes(Backend),
Backend = CurrentBackend
)
->
module_mark_as_external(Name, Arity, Context, !ModuleInfo,
!Specs)
;
true
)
;
Pieces = [words("Warning:"), quote("external"),
words("declaration requires arity."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds,
[Msg]),
!:Specs = [Spec | !.Specs]
)
;
( ModuleDefn = md_include_module(_)
; ModuleDefn = md_version_numbers(_, _)
; ModuleDefn = md_transitively_imported
)
;
( ModuleDefn = md_interface
; ModuleDefn = md_implementation
; ModuleDefn = md_implementation_but_exported_to_submodules
; ModuleDefn = md_imported(_)
; ModuleDefn = md_used(_)
; ModuleDefn = md_opt_imported
; ModuleDefn = md_abstract_imported
),
unexpected($module, $pred,
"module_defn_update_import_status missed something")
;
ModuleDefn = md_export(_),
Pieces = [words("Warning: declaration not yet implemented."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_warning, phase_parse_tree_to_hlds,
[Msg]),
!:Specs = [Spec | !.Specs]
)
).
:- pred add_pass_1_mutable(item_mutable_info::in,
item_status::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_1_mutable(Item, Status, !ModuleInfo, !Specs) :-
% We add the initialise decl and the foreign_decl on the second pass and
% the foreign_proc clauses on the third pass.
Item = item_mutable_info(Name, Type, _InitValue, Inst, MutAttrs,
_MutVarset, Context, _SeqNum),
Status = item_status(ImportStatus, _),
DefinedThisModule = status_defined_in_this_module(ImportStatus),
(
DefinedThisModule = yes,
module_info_get_name(!.ModuleInfo, ModuleName),
% The predicate declarations we produce depends on the compilation
% target, which use different source-to-source transformations for
% mutables.
module_info_get_globals(!.ModuleInfo, Globals),
globals.get_target(Globals, CompilationTarget),
(
CompilationTarget = target_c,
WantPreInitDecl = yes,
WantLockDecls = yes,
WantUnsafeAccessDecls = yes
;
CompilationTarget = target_csharp,
IsThreadLocal = mutable_var_thread_local(MutAttrs),
(
IsThreadLocal = mutable_thread_local,
WantPreInitDecl = yes
;
IsThreadLocal = mutable_not_thread_local,
WantPreInitDecl = no
),
WantLockDecls = no,
WantUnsafeAccessDecls = yes
;
CompilationTarget = target_java,
WantPreInitDecl = no,
WantLockDecls = no,
WantUnsafeAccessDecls = yes
;
CompilationTarget = target_erlang,
WantPreInitDecl = no,
WantLockDecls = no,
WantUnsafeAccessDecls = no
;
( CompilationTarget = target_il
; CompilationTarget = target_asm
; CompilationTarget = target_x86_64
),
% Not supported yet.
WantPreInitDecl = yes,
WantLockDecls = yes,
WantUnsafeAccessDecls = yes
),
% Create the mutable initialisation predicate.
InitPredDeclItem = mutable_init_pred_decl(ModuleName, Name, Context),
add_item_decl_pass_1(InitPredDeclItem, _, Status, _,
!ModuleInfo, !Specs),
IsConstant = mutable_var_constant(MutAttrs),
(
IsConstant = no,
% Create the pre-initialisation predicate. This is called
% by the mutable initialisation predicate.
(
WantPreInitDecl = yes,
PreInitPredDeclItem = mutable_pre_init_pred_decl(ModuleName,
Name, Context),
add_item_decl_pass_1(PreInitPredDeclItem, _, Status, _,
!ModuleInfo, !Specs)
;
WantPreInitDecl = no
),
% Create the primitive access and locking predicates.
(
WantLockDecls = yes,
LockPredDeclItem = lock_pred_decl(ModuleName, Name, Context),
UnlockPredDecl = unlock_pred_decl(ModuleName, Name, Context),
add_item_decl_pass_1(LockPredDeclItem, _, Status, _,
!ModuleInfo, !Specs),
add_item_decl_pass_1(UnlockPredDecl, _, Status, _,
!ModuleInfo, !Specs)
;
WantLockDecls = no
),
(
WantUnsafeAccessDecls = yes,
UnsafeGetPredDeclItem = unsafe_get_pred_decl(ModuleName, Name,
Type, Inst, Context),
UnsafeSetPredDeclItem = unsafe_set_pred_decl(ModuleName, Name,
Type, Inst, Context),
add_item_decl_pass_1(UnsafeGetPredDeclItem, _, Status, _,
!ModuleInfo, !Specs),
add_item_decl_pass_1(UnsafeSetPredDeclItem, _, Status, _,
!ModuleInfo, !Specs)
;
WantUnsafeAccessDecls = no
),
% 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.
StdGetPredDeclItem = std_get_pred_decl(ModuleName, Name,
Type, Inst, Context),
StdSetPredDeclItem = std_set_pred_decl(ModuleName, Name,
Type, Inst, Context),
add_item_decl_pass_1(StdGetPredDeclItem, _, Status, _,
!ModuleInfo, !Specs),
add_item_decl_pass_1(StdSetPredDeclItem, _, Status, _,
!ModuleInfo, !Specs),
% If requested, create the pure access predicates using
% the I/O state as well.
CreateIOInterface = mutable_var_attach_to_io_state(MutAttrs),
(
CreateIOInterface = yes,
IOGetPredDeclItem = io_get_pred_decl(ModuleName, Name,
Type, Inst, Context),
IOSetPredDeclItem = io_set_pred_decl(ModuleName, Name,
Type, Inst, Context),
add_item_decl_pass_1(IOGetPredDeclItem, _, Status, _,
!ModuleInfo, !Specs),
add_item_decl_pass_1(IOSetPredDeclItem, _, Status, _,
!ModuleInfo, !Specs)
;
CreateIOInterface = no
)
;
IsConstant = yes,
% 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.
ConstantGetPredDeclItem = constant_get_pred_decl(ModuleName, Name,
Type, Inst, Context),
ConstantSetPredDeclItem = constant_set_pred_decl(ModuleName, Name,
Type, Inst, Context),
add_item_decl_pass_1(ConstantGetPredDeclItem, _, Status, _,
!ModuleInfo, !Specs),
add_item_decl_pass_1(ConstantSetPredDeclItem, _, Status, _,
!ModuleInfo, !Specs)
)
;
DefinedThisModule = no
).
:- pred add_solver_type_mutable_items_pass_1(list(item_mutable_info)::in,
item_status::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_solver_type_mutable_items_pass_1([], _, !ModuleInfo, !Specs).
add_solver_type_mutable_items_pass_1([MutableInfo | MutableInfos], Status,
!ModuleInfo, !Specs) :-
add_pass_1_mutable(MutableInfo, Status, !ModuleInfo, !Specs),
add_solver_type_mutable_items_pass_1(MutableInfos, Status,
!ModuleInfo, !Specs).
:- pred add_module_specifiers(list(module_specifier)::in, import_status::in,
module_info::in, module_info::out) is det.
add_module_specifiers(Specifiers, IStat, !ModuleInfo) :-
( status_defined_in_this_module(IStat) = yes ->
module_add_imported_module_specifiers(IStat, Specifiers, !ModuleInfo)
; IStat = status_imported(import_locn_ancestor_private_interface_proper) ->
module_add_imported_module_specifiers(IStat, Specifiers, !ModuleInfo),
% Any import_module which comes from a private interface
% must by definition be a module used by the parent module.
module_info_add_parents_to_used_modules(Specifiers, !ModuleInfo)
;
module_add_indirectly_imported_module_specifiers(Specifiers,
!ModuleInfo)
).
%-----------------------------------------------------------------------------%
:- pred add_item_decl_pass_2(item::in,
item_status::in, item_status::out, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_item_decl_pass_2(Item, !Status, !ModuleInfo, !Specs) :-
(
Item = item_module_defn(ItemModuleDefn),
ItemModuleDefn = item_module_defn_info(ModuleDefn, _, _SeqNum),
( module_defn_update_import_status(ModuleDefn, NewStatus) ->
!:Status = NewStatus
;
true
)
;
Item = item_type_defn(ItemTypeDefn),
add_pass_2_type_defn(ItemTypeDefn, !.Status, !ModuleInfo, !Specs)
;
Item = item_pred_decl(ItemPredDecl),
add_pass_2_pred_decl(ItemPredDecl, !.Status, !ModuleInfo, !Specs)
;
Item = item_pragma(ItemPragma),
add_pass_2_pragma(ItemPragma, !Status, !ModuleInfo, !Specs)
;
Item = item_instance(ItemInstance),
add_pass_2_instance(ItemInstance, !.Status, !ModuleInfo, !Specs)
;
Item = item_initialise(ItemInitialise),
add_pass_2_initialise(ItemInitialise, !.Status, !ModuleInfo, !Specs)
;
Item = item_finalise(ItemFinalise),
add_pass_2_finalise(ItemFinalise, !.Status, !ModuleInfo, !Specs)
;
Item = item_mutable(ItemMutable),
add_pass_2_mutable(ItemMutable, !.Status, !ModuleInfo, !Specs)
;
( Item = item_module_start(_)
; Item = item_module_end(_)
; Item = item_clause(_)
; Item = item_inst_defn(_)
; Item = item_mode_defn(_)
; Item = item_mode_decl(_)
; Item = item_promise(_)
; Item = item_typeclass(_)
; Item = item_nothing(_)
)
% Do nothing in pass 2 for these kinds of items.
).
:- pred add_pass_2_type_defn(item_type_defn_info::in,
item_status::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_2_type_defn(ItemTypeDefn, Status, !ModuleInfo, !Specs) :-
ItemTypeDefn = item_type_defn_info(VarSet, Name, Args, TypeDefn, Cond,
Context, _SeqNum),
module_add_type_defn(VarSet, Name, Args, TypeDefn, Cond, Context,
Status, !ModuleInfo, !Specs),
( TypeDefn = parse_tree_solver_type(SolverTypeDetails, _MaybeUserEqComp) ->
MutableItems = SolverTypeDetails ^ std_mutable_items,
add_solver_type_mutable_items_pass_2(MutableItems, Status,
!ModuleInfo, !Specs)
;
true
).
:- pred add_pass_2_pred_decl(item_pred_decl_info::in,
item_status::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_2_pred_decl(ItemPredDecl, _Status, !ModuleInfo, !Specs) :-
ItemPredDecl = item_pred_decl_info(_Origin, _TypeVarSet, _InstVarSet,
_ExistQVars, PredOrFunc, SymName, TypesAndModes, _WithType, _WithInst,
_MaybeDet, _Cond, _Purity, _ClassContext, _Context, _SeqNum),
% Add default modes for function declarations, if necessary.
(
PredOrFunc = pf_predicate
;
PredOrFunc = pf_function,
list.length(TypesAndModes, Arity),
adjust_func_arity(pf_function, FuncArity, Arity),
module_info_get_predicate_table(!.ModuleInfo, PredTable0),
predicate_table_lookup_func_sym_arity(PredTable0,
is_fully_qualified, SymName, FuncArity, PredIds),
(
PredIds = [_ | _],
predicate_table_get_preds(PredTable0, Preds0),
maybe_add_default_func_modes(PredIds, Preds0, Preds),
predicate_table_set_preds(Preds, PredTable0, PredTable),
module_info_set_predicate_table(PredTable, !ModuleInfo)
;
PredIds = [],
unexpected($module, $pred, "can't find func declaration")
)
).
:- pred add_pass_2_instance(item_instance_info::in,
item_status::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_2_instance(ItemInstance, Status, !ModuleInfo, !Specs) :-
ItemInstance = item_instance_info(Constraints, Name, Types, OriginalTypes,
Body, VarSet, InstanceModuleName, Context, _SeqNum),
Status = item_status(ImportStatus, _),
(
Body = instance_body_abstract,
make_status_abstract(ImportStatus, BodyStatus)
;
Body = instance_body_concrete(_),
BodyStatus = ImportStatus
),
module_add_instance_defn(InstanceModuleName, Constraints, Name,
Types, OriginalTypes, Body, VarSet, BodyStatus, Context,
!ModuleInfo, !Specs).
:- pred add_pass_2_initialise(item_initialise_info::in,
item_status::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_2_initialise(ItemInitialise, Status, !ModuleInfo, !Specs) :-
% These are processed properly during pass 3, we just do some
% error checking at this point.
ItemInitialise = item_initialise_info(Origin, _, _, Context, _SeqNum),
Status = item_status(ImportStatus, _),
( ImportStatus = status_exported ->
(
Origin = user,
error_is_exported(Context, "`initialise' declaration", !Specs)
;
Origin = compiler(Details),
(
% Ignore the error if this initialise declaration was
% introduced because of a mutable declaration.
Details = mutable_decl
;
( Details = initialise_decl
; Details = finalise_decl
; Details = solver_type
; Details = foreign_imports
; Details = pragma_memo_attribute
),
unexpected($module, $pred,
"Bad introduced initialise declaration")
)
)
;
true
).
:- pred add_pass_2_finalise(item_finalise_info::in,
item_status::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_2_finalise(ItemFinalise, Status, !ModuleInfo, !Specs) :-
% There are processed properly during pass 3, we just do some error
% checking at this point.
ItemFinalise = item_finalise_info(Origin, _, _, Context, _SeqNum),
Status = item_status(ImportStatus, _),
( ImportStatus = status_exported ->
(
Origin = user,
error_is_exported(Context, "`finalise' declaration", !Specs)
;
% There are no source-to-source transformations that introduce
% finalise declarations.
Origin = compiler(_),
unexpected($module, $pred, "Bad introduced finalise declaration.")
)
;
true
).
:- pred add_pass_2_mutable(item_mutable_info::in,
item_status::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_2_mutable(ItemMutable, Status, !ModuleInfo, !Specs) :-
ItemMutable = item_mutable_info(Name, _Type, _InitTerm, Inst,
MutAttrs, _MutVarset, Context, _SeqNum),
Status = item_status(ImportStatus, _),
( ImportStatus = status_exported ->
error_is_exported(Context, "`mutable' declaration", !Specs)
;
true
),
% We don't implement the `mutable' declaration unless it is defined in
% this module. Not having this check means that we might end up up
% duplicating the definition of the global variable in any submodules.
DefinedThisModule = status_defined_in_this_module(ImportStatus),
(
DefinedThisModule = yes,
module_info_get_globals(!.ModuleInfo, Globals),
globals.get_target(Globals, CompilationTarget),
% XXX We don't currently support the foreign_name attribute
% for all languages.
(
(
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,
module_info_get_name(!.ModuleInfo, ModuleName),
(
MaybeForeignNames = no
;
MaybeForeignNames = yes(ForeignNames),
% Report any errors with the foreign_name attributes
% during this pass.
ReportErrors = yes,
get_global_name_from_foreign_names(!.ModuleInfo, ReportErrors,
Context, ModuleName, Name, ForeignLanguage, ForeignNames,
_TargetMutableName, !Specs)
),
% If we are creating the I/O version of the set predicate then we
% need to add a promise_pure pragma for it. This needs to be done
% here (in stage 2) rather than in stage 3 where the rest of the
% mutable transformation is.
IOStateInterface = mutable_var_attach_to_io_state(MutAttrs),
(
IOStateInterface = yes,
SetPredName = mutable_set_pred_sym_name(ModuleName, Name),
SetPredNameArity = pred_name_arity(SetPredName, 3),
IOSetPromisePurePragma = pragma_promise_pure(SetPredNameArity),
IOSetPromisePureItemPragma = item_pragma_info(
compiler(mutable_decl), IOSetPromisePurePragma, Context,
-1),
add_pass_2_pragma(IOSetPromisePureItemPragma, Status, _,
!ModuleInfo, !Specs)
;
IOStateInterface = no
)
;
( CompilationTarget = target_il
; CompilationTarget = target_asm
; CompilationTarget = target_x86_64
),
Pieces = [words("Error: foreign_name mutable attribute not yet"),
words("implemented 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]
),
% Check that the inst in the mutable declaration is a valid inst for a
% mutable declaration.
( is_valid_mutable_inst(!.ModuleInfo, Inst) ->
true
;
% 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,
InstStr = mercury_expanded_inst_to_string(Inst, DummyInstVarset,
!.ModuleInfo),
InvalidInstPieces = [
words("Error: the inst"),
quote(InstStr),
words("is not a valid inst for a mutable declaration.")
],
% XXX We could provide more information about exactly *why* the
% inst was not valid here as well.
InvalidInstMsg = simple_msg(Context, [always(InvalidInstPieces)]),
InvalidInstSpec = error_spec(severity_error,
phase_parse_tree_to_hlds, [InvalidInstMsg]),
!:Specs = [ InvalidInstSpec | !.Specs ]
)
;
DefinedThisModule = no
).
:- pred add_solver_type_mutable_items_pass_2(list(item_mutable_info)::in,
item_status::in, module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_solver_type_mutable_items_pass_2([], _, !ModuleInfo, !Specs).
add_solver_type_mutable_items_pass_2([MutableInfo | MutableInfos], Status,
!ModuleInfo, !Specs) :-
add_pass_2_mutable(MutableInfo, Status, !ModuleInfo, !Specs),
add_solver_type_mutable_items_pass_2(MutableInfos, Status,
!ModuleInfo, !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, bool::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, ReportErrors, Context,
ModuleName, MercuryMutableName, ForeignLanguage, ForeignNames,
TargetMutableName, !Specs) :-
solutions(get_matching_foreign_name(ForeignNames, ForeignLanguage),
TargetMutableNames),
(
TargetMutableNames = [],
% This works for Erlang as well.
TargetMutableName = mutable_c_var_name(ModuleName, MercuryMutableName)
;
TargetMutableNames = [foreign_name(_, TargetMutableName)]
% XXX We should really check that this is a valid identifier
% in the target language here.
;
TargetMutableNames = [_, _ | _],
(
ReportErrors = yes,
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]
;
ReportErrors = no
),
% This works for Erlang as well.
TargetMutableName = mutable_c_var_name(ModuleName, MercuryMutableName)
).
:- pred get_matching_foreign_name(list(foreign_name)::in,
foreign_language::in, foreign_name::out) is nondet.
get_matching_foreign_name(ForeignNames, ForeignLanguage, ForeignName) :-
list.member(ForeignName, ForeignNames),
ForeignName = foreign_name(ForeignLanguage, _).
%-----------------------------------------------------------------------------%
add_item_pass_3(Item, !Status, !ModuleInfo, !QualInfo, !Specs) :-
(
Item = item_module_defn(ItemModuleDefn),
add_pass_3_module_defn(ItemModuleDefn, !Status, !ModuleInfo, !QualInfo,
!Specs)
;
Item = item_clause(ItemClause),
add_pass_3_clause(ItemClause, !.Status, !ModuleInfo, !QualInfo, !Specs)
;
Item = item_type_defn(ItemTypeDefn),
add_pass_3_type_defn(ItemTypeDefn, !.Status, !ModuleInfo, !QualInfo,
!Specs)
;
Item = item_pred_decl(ItemPredDecl),
add_pass_3_pred_decl(ItemPredDecl, !.Status, !ModuleInfo, !QualInfo,
!Specs)
;
Item = item_pragma(ItemPragma),
add_pass_3_pragma(ItemPragma, !Status, !ModuleInfo, !QualInfo, !Specs)
;
Item = item_promise(ItemPromise),
add_pass_3_promise(ItemPromise, !.Status, !ModuleInfo, !QualInfo,
!Specs)
;
Item = item_initialise(ItemInitialise),
add_pass_3_initialise(ItemInitialise, !.Status, !ModuleInfo, !QualInfo,
!Specs)
;
Item = item_finalise(ItemFinalise),
add_pass_3_finalise(ItemFinalise, !.Status, !ModuleInfo, !QualInfo,
!Specs)
;
Item = item_mutable(ItemMutable),
add_pass_3_mutable(ItemMutable, !.Status, !ModuleInfo, !QualInfo,
!Specs)
;
( Item = item_module_start(_)
; Item = item_module_end(_)
; Item = item_inst_defn(_)
; Item = item_mode_defn(_)
; Item = item_mode_decl(_)
; Item = item_typeclass(_)
; Item = item_instance(_)
; Item = item_nothing(_)
)
% Do nothing.
).
:- pred add_pass_3_clause(item_clause_info::in,
import_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_pass_3_clause(ItemClause, Status, !ModuleInfo, !QualInfo, !Specs) :-
ItemClause = item_clause_info(Origin, VarSet, PredOrFunc,
PredName, Args, Body, Context, SeqNum),
( Status = status_exported ->
(
Origin = user,
list.length(Args, Arity),
% There is no point printing out the qualified name since that
% information is already in the context.
UnqualifiedPredName = unqualify_name(PredName),
ClauseId = simple_call_id_to_string(PredOrFunc,
unqualified(UnqualifiedPredName) / Arity),
error_is_exported(Context, "clause for " ++ ClauseId, !Specs)
;
Origin = compiler(Details),
(
% Ignore clauses that are introduced as a result of
% `initialise', `finalise' or `mutable' declarations
% or pragma memos.
( Details = initialise_decl
; Details = finalise_decl
; Details = mutable_decl
; Details = pragma_memo_attribute
)
;
( Details = solver_type
; Details = foreign_imports
),
unexpected($module, $pred, "Bad introduced clauses")
)
)
;
true
),
% At this stage we only need know that it's not a promise declaration.
module_add_clause(VarSet, PredOrFunc, PredName, Args, Body, Status,
Context, yes(SeqNum), goal_type_none, !ModuleInfo, !QualInfo, !Specs).
:- pred add_pass_3_type_defn(item_type_defn_info::in,
import_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_pass_3_type_defn(ItemTypeDefn, Status, !ModuleInfo, !QualInfo, !Specs) :-
ItemTypeDefn = item_type_defn_info(_TVarSet, SymName, TypeParams, TypeDefn,
_Cond, Context, _SeqNum),
% If this is a solver type, then we need to also add the clauses for
% the compiler generated inst cast predicate (the declaration for which
% was added in pass 1). We should only add the clauses if this is the
% module in which the solver type was defined though.
(
TypeDefn = parse_tree_solver_type(SolverTypeDetails, _MaybeUserEqComp),
status_defined_in_this_module(Status) = yes
->
add_solver_type_clause_items(SymName, TypeParams, SolverTypeDetails,
Context, Status, _, !ModuleInfo, !QualInfo, !Specs),
MutableItems = SolverTypeDetails ^ std_mutable_items,
add_solver_type_mutable_items_clauses(MutableItems, Status, _,
!ModuleInfo, !QualInfo, !Specs)
;
true
).
:- pred add_pass_3_pred_decl(item_pred_decl_info::in,
import_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_pass_3_pred_decl(ItemPredDecl, Status, !ModuleInfo, !QualInfo, !Specs) :-
ItemPredDecl = item_pred_decl_info(_, _, _, _, PredOrFunc, SymName,
TypesAndModes, _WithType, _WithInst, _, _, _, _, Context, _SeqNum),
(
PredOrFunc = pf_predicate
;
PredOrFunc = pf_function,
list.length(TypesAndModes, PredArity),
adjust_func_arity(pf_function, FuncArity, PredArity),
maybe_check_field_access_function(SymName, FuncArity, Status,
Context, !.ModuleInfo, !Specs)
).
:- pred add_pass_3_module_defn(item_module_defn_info::in,
import_status::in, import_status::out, module_info::in, module_info::out,
qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_pass_3_module_defn(ItemModuleDefn, !Status, !ModuleInfo, !QualInfo,
!Specs) :-
ItemModuleDefn = item_module_defn_info(ModuleDefn, _Context, _SeqNum),
( ModuleDefn = md_version_numbers(ModuleName, ModuleVersionNumbers) ->
% Record the version numbers for each imported module
% if smart recompilation is enabled.
RecordPred = (pred(RecompInfo0::in, RecompInfo::out) is det :-
RecompInfo = RecompInfo0 ^ version_numbers ^
map.elem(ModuleName) := ModuleVersionNumbers
),
apply_to_recompilation_info(RecordPred, !QualInfo)
; module_defn_update_import_status(ModuleDefn, ItemStatus1) ->
ItemStatus1 = item_status(!:Status, NeedQual),
qual_info_get_mq_info(!.QualInfo, MQInfo0),
mq_info_set_need_qual_flag(NeedQual, MQInfo0, MQInfo),
qual_info_set_mq_info(MQInfo, !QualInfo)
;
true
).
:- pred add_pass_3_promise(item_promise_info::in,
import_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_pass_3_promise(ItemPromise, Status, !ModuleInfo, !QualInfo, !Specs) :-
ItemPromise = item_promise_info(PromiseType, Goal, VarSet, UnivVars,
Context, _SeqNum),
% If the outermost universally quantified variables are placed in the head
% of the dummy predicate, the typechecker will avoid warning about unbound
% type variables as this implicitly adds a universal quantification of the
% type variables needed.
term.var_list_to_term_list(UnivVars, HeadVars),
(
% Extra error checking for promise ex declarations.
( PromiseType = promise_type_exclusive
; PromiseType = promise_type_exhaustive
; PromiseType = promise_type_exclusive_exhaustive
),
check_promise_ex_decl(UnivVars, PromiseType, Goal, Context, !Specs)
;
PromiseType = promise_type_true
),
% Add as dummy predicate.
add_promise_clause(PromiseType, HeadVars, VarSet, Goal, Context,
Status, !ModuleInfo, !QualInfo, !Specs).
:- pred add_pass_3_initialise(item_initialise_info::in,
import_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_pass_3_initialise(ItemInitialise, Status, !ModuleInfo, !QualInfo,
!Specs) :-
ItemInitialise = item_initialise_info(Origin, SymName, Arity, Context,
_SeqNum),
Origin = user,
% To handle a `:- initialise initpred.' declaration for C backends we need
% to:
% (1) construct a new C function name, CName, to use to export initpred,
% (2) add the export pragma that does this
% (3) record the initpred/cname pair in the ModuleInfo so that
% code generation can ensure cname is called during module initialisation.
%
% For the Erlang backend, we need to have the initpred recorded in the
% ModuleInfo. This is implied by the handling for the C backends.
module_info_get_predicate_table(!.ModuleInfo, PredTable),
predicate_table_lookup_pred_sym_arity(PredTable,
may_be_partially_qualified, SymName, Arity, PredIds),
(
PredIds = [],
Pieces = [words("Error:"), sym_name_and_arity(SymName/Arity),
words("used in initialise declaration"),
words("does not have a corresponding pred declaration."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs]
;
PredIds = [HeadPredId | TailPredIds],
(
TailPredIds = [],
PredId = HeadPredId,
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
pred_info_get_arg_types(PredInfo, ArgTypes),
pred_info_get_procedures(PredInfo, ProcTable),
ProcInfos = map.values(ProcTable),
module_info_get_globals(!.ModuleInfo, Globals),
globals.get_target(Globals, CompilationTarget),
ExportLang = target_lang_to_foreign_export_lang(CompilationTarget),
(
ArgTypes = [Arg1Type, Arg2Type],
type_is_io_state(Arg1Type),
type_is_io_state(Arg2Type),
list.member(ProcInfo, ProcInfos),
proc_info_get_maybe_declared_argmodes(ProcInfo,
MaybeHeadModes),
MaybeHeadModes = yes(HeadModes),
HeadModes = [ di_mode, uo_mode ],
proc_info_get_declared_determinism(ProcInfo, MaybeDetism),
MaybeDetism = yes(Detism),
( Detism = detism_det ; Detism = detism_cc_multi ),
pred_info_get_purity(PredInfo, Purity),
Purity = purity_pure
->
module_info_new_user_init_pred(SymName, Arity, CName,
!ModuleInfo),
PredNameModesPF = pred_name_modes_pf(SymName,
[di_mode, uo_mode], pf_predicate),
FPEInfo = pragma_info_foreign_proc_export(ExportLang,
PredNameModesPF, CName),
ExportPragma = pragma_foreign_proc_export(FPEInfo),
ExportItemPragma = item_pragma_info(compiler(initialise_decl),
ExportPragma, Context, -1),
ExportItem = item_pragma(ExportItemPragma),
add_item_pass_3(ExportItem, Status, _,
!ModuleInfo, !QualInfo, !Specs)
;
ArgTypes = [],
list.member(ProcInfo, ProcInfos),
proc_info_get_maybe_declared_argmodes(ProcInfo,
MaybeHeadModes),
MaybeHeadModes = yes(HeadModes),
HeadModes = [],
proc_info_get_declared_determinism(ProcInfo, MaybeDetism),
MaybeDetism = yes(Detism),
( Detism = detism_det ; Detism = detism_cc_multi ),
pred_info_get_purity(PredInfo, Purity),
Purity = purity_impure
->
module_info_new_user_init_pred(SymName, Arity, CName,
!ModuleInfo),
PredNameModesPF = pred_name_modes_pf(SymName, [],
pf_predicate),
FPEInfo = pragma_info_foreign_proc_export(ExportLang,
PredNameModesPF, CName),
ExportPragma = pragma_foreign_proc_export(FPEInfo),
ExportItemPragma = item_pragma_info(compiler(initialise_decl),
ExportPragma, Context, -1),
ExportItem = item_pragma(ExportItemPragma),
add_item_pass_3(ExportItem, Status, _,
!ModuleInfo, !QualInfo, !Specs)
;
Pieces = [words("Error:"), sym_name_and_arity(SymName/Arity),
words("used in initialise declaration has"),
words("invalid signature."), nl],
% TODO: provide verbose error information here.
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds,
[Msg]),
!:Specs = [Spec | !.Specs]
)
;
TailPredIds = [_ | _],
Pieces = [words("Error:"), sym_name_and_arity(SymName/Arity),
words("used in initialise declaration"),
words("multiple pred declarations."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs]
)
).
add_pass_3_initialise(ItemInitialise, Status, !ModuleInfo, !QualInfo,
!Specs) :-
ItemInitialise = item_initialise_info(Origin, SymName, Arity, Context,
_SeqNum),
Origin = compiler(Details),
% 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.
(
Details = mutable_decl,
module_info_get_globals(!.ModuleInfo, Globals),
globals.get_target(Globals, CompilationTarget),
(
CompilationTarget = target_c,
MaybeExportLang = yes(lang_c)
;
CompilationTarget = target_java,
MaybeExportLang = yes(lang_java)
;
CompilationTarget = target_csharp,
MaybeExportLang = yes(lang_csharp)
;
CompilationTarget = target_erlang,
MaybeExportLang = yes(lang_erlang)
;
% Untested.
( CompilationTarget = target_asm
; CompilationTarget = target_il
; CompilationTarget = target_x86_64
),
MaybeExportLang = no
),
(
MaybeExportLang = yes(ExportLang),
module_info_new_user_init_pred(SymName, Arity, CName, !ModuleInfo),
PredNameModesPF = pred_name_modes_pf(SymName, [], pf_predicate),
FPEInfo = pragma_info_foreign_proc_export(ExportLang,
PredNameModesPF, CName),
ExportPragma = pragma_foreign_proc_export(FPEInfo),
ExportItemPragma = item_pragma_info(compiler(mutable_decl),
ExportPragma, Context, -1),
ExportItem = item_pragma(ExportItemPragma),
add_item_pass_3(ExportItem, Status, _,
!ModuleInfo, !QualInfo, !Specs)
;
MaybeExportLang = no
)
;
( Details = initialise_decl
; Details = finalise_decl
; Details = solver_type
; Details = pragma_memo_attribute
; Details = foreign_imports
),
unexpected($module, $pred, "Bad introduced initialise declaration")
).
:- pred add_pass_3_finalise(item_finalise_info::in,
import_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_pass_3_finalise(ItemFinalise, Status, !ModuleInfo, !QualInfo, !Specs) :-
ItemFinalise = item_finalise_info(Origin, SymName, Arity, Context,
_SeqNum),
% To handle a `:- finalise finalpred.' declaration for C backends we need
% to:
% (1) construct a new C function name, CName, to use to export finalpred,
% (2) add `:- pragma foreign_export("C", finalpred(di, uo), CName).',
% (3) record the finalpred/cname pair in the ModuleInfo so that
% code generation can ensure cname is called during module finalisation.
%
% For the Erlang backend, we need to have the finalpred recorded in the
% ModuleInfo. This is implied by the handling for the C backends.
(
Origin = compiler(_),
unexpected($module, $pred, "Bad introduced finalise declaration")
;
Origin = user
),
module_info_get_predicate_table(!.ModuleInfo, PredTable),
predicate_table_lookup_pred_sym_arity(PredTable,
may_be_partially_qualified, SymName, Arity, PredIds),
(
PredIds = [],
Pieces = [words("Error:"), sym_name_and_arity(SymName/Arity),
words("used in finalise declaration"),
words("does not have a corresponding pred declaration."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs]
;
PredIds = [HeadPredId | TailPredIds],
(
TailPredIds = [],
PredId = HeadPredId,
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
pred_info_get_arg_types(PredInfo, ArgTypes),
pred_info_get_procedures(PredInfo, ProcTable),
ProcInfos = map.values(ProcTable),
module_info_get_globals(!.ModuleInfo, Globals),
globals.get_target(Globals, CompilationTarget),
ExportLang = target_lang_to_foreign_export_lang(CompilationTarget),
(
ArgTypes = [Arg1Type, Arg2Type],
type_is_io_state(Arg1Type),
type_is_io_state(Arg2Type),
list.member(ProcInfo, ProcInfos),
proc_info_get_maybe_declared_argmodes(ProcInfo,
MaybeHeadModes),
MaybeHeadModes = yes(HeadModes),
HeadModes = [ di_mode, uo_mode ],
proc_info_get_declared_determinism(ProcInfo, MaybeDetism),
MaybeDetism = yes(Detism),
( Detism = detism_det ; Detism = detism_cc_multi ),
pred_info_get_purity(PredInfo, Purity),
Purity = purity_pure
->
module_info_new_user_final_pred(SymName, Arity, CName,
!ModuleInfo),
PredNameModesPF = pred_name_modes_pf(SymName,
[di_mode, uo_mode], pf_predicate),
FPEInfo = pragma_info_foreign_proc_export(ExportLang,
PredNameModesPF, CName),
ExportPragma = pragma_foreign_proc_export(FPEInfo),
ExportItemPragma = item_pragma_info(compiler(finalise_decl),
ExportPragma, Context, -1),
ExportItem = item_pragma(ExportItemPragma),
add_item_pass_3(ExportItem, Status, _,
!ModuleInfo, !QualInfo, !Specs)
;
ArgTypes = [],
list.member(ProcInfo, ProcInfos),
proc_info_get_maybe_declared_argmodes(ProcInfo,
MaybeHeadModes),
MaybeHeadModes = yes(HeadModes),
HeadModes = [],
proc_info_get_declared_determinism(ProcInfo, MaybeDetism),
MaybeDetism = yes(Detism),
( Detism = detism_det; Detism = detism_cc_multi ),
pred_info_get_purity(PredInfo, Purity),
Purity = purity_impure
->
module_info_new_user_final_pred(SymName, Arity, CName,
!ModuleInfo),
PredNameModesPF = pred_name_modes_pf(SymName,
[], pf_predicate),
FPEInfo = pragma_info_foreign_proc_export(ExportLang,
PredNameModesPF, CName),
ExportPragma = pragma_foreign_proc_export(FPEInfo),
ExportItemPragma = item_pragma_info(compiler(finalise_decl),
ExportPragma, Context, -1),
ExportItem = item_pragma(ExportItemPragma),
add_item_pass_3(ExportItem, Status, _,
!ModuleInfo, !QualInfo, !Specs)
;
Pieces = [words("Error:"), sym_name_and_arity(SymName/Arity),
words("used in finalise declaration"),
words("has invalid signature."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds,
[Msg]),
!:Specs = [Spec | !.Specs]
)
;
TailPredIds = [_ | _],
Pieces = [words("Error:"), sym_name_and_arity(SymName/Arity),
words("used in finalise declaration"),
words("has multiple pred declarations."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs]
)
).
:- func target_lang_to_foreign_export_lang(compilation_target)
= foreign_language.
target_lang_to_foreign_export_lang(CompilationTarget) = ExportLang :-
(
( CompilationTarget = target_c
; CompilationTarget = target_asm
; CompilationTarget = target_x86_64
),
ExportLang = lang_c
;
CompilationTarget = target_erlang,
ExportLang = lang_erlang
;
CompilationTarget = target_il,
ExportLang = lang_il
;
CompilationTarget = target_csharp,
ExportLang = lang_csharp
;
CompilationTarget = target_java,
ExportLang = lang_java
).
:- pred add_pass_3_mutable(item_mutable_info::in,
import_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_pass_3_mutable(ItemMutable, Status, !ModuleInfo, !QualInfo, !Specs) :-
ItemMutable = item_mutable_info(MercuryMutableName, Type, _InitTerm, _Inst,
MutAttrs, _MutVarset, Context, _SeqNum),
IsConstant = mutable_var_constant(MutAttrs),
% The transformation here is documented in the comments at the
% beginning of prog_mutable.m.
DefinedThisModule = status_defined_in_this_module(Status),
(
DefinedThisModule = yes,
module_info_get_name(!.ModuleInfo, ModuleName),
module_info_get_globals(!.ModuleInfo, Globals),
globals.get_target(Globals, CompilationTarget),
(
CompilationTarget = target_c,
% Work out what name to give the global in the target language.
decide_mutable_target_var_name(!.ModuleInfo, MutAttrs,
ModuleName, MercuryMutableName, lang_c, Context,
TargetMutableName, !Specs),
% Add foreign_decl and foreign_code items that declare/define the
% global variable used to implement the mutable. If the mutable is
% not constant then add a mutex to synchronize access to it as
% well.
IsThreadLocal = mutable_var_thread_local(MutAttrs),
add_c_mutable_defn_and_decl(TargetMutableName, Type, IsConstant,
IsThreadLocal, Context, !ModuleInfo, !QualInfo, !Specs),
% Add all the predicates related to mutables.
add_c_mutable_preds(ItemMutable, TargetMutableName,
Status, _, !ModuleInfo, !QualInfo, !Specs)
;
(
CompilationTarget = target_java,
Lang = lang_java
;
CompilationTarget = target_csharp,
Lang = lang_csharp
),
% Work out what name to give the global in the target language.
decide_mutable_target_var_name(!.ModuleInfo, MutAttrs,
ModuleName, MercuryMutableName, Lang, Context,
TargetMutableName, !Specs),
% Add foreign_code item that defines the global variable used to
% implement the mutable.
IsThreadLocal = mutable_var_thread_local(MutAttrs),
add_csharp_java_mutable_defn(Lang, TargetMutableName, Type,
IsThreadLocal, Context, !ModuleInfo, !QualInfo, !Specs),
% Add all the predicates related to mutables.
add_csharp_java_mutable_preds(ItemMutable, Lang, TargetMutableName,
Status, _, !ModuleInfo, !QualInfo, !Specs)
;
CompilationTarget = target_erlang,
% Work out what name to give the global in the target language.
decide_mutable_target_var_name(!.ModuleInfo, MutAttrs,
ModuleName, MercuryMutableName, lang_erlang, Context,
TargetMutableName, !Specs),
% Add all the predicates related to mutables.
add_erlang_mutable_preds(ItemMutable, TargetMutableName,
Status, _, !ModuleInfo, !QualInfo, !Specs)
;
( CompilationTarget = target_il
; CompilationTarget = target_asm
; CompilationTarget = target_x86_64
)
% Not supported yet.
)
;
DefinedThisModule = no
).
% 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,
list(error_spec)::in, list(error_spec)::out) is det.
decide_mutable_target_var_name(ModuleInfo, MutAttrs, ModuleName, Name,
ForeignLanguage, Context, TargetMutableName, !Specs) :-
mutable_var_maybe_foreign_names(MutAttrs) = MaybeForeignNames,
(
MaybeForeignNames = no,
% This works for Erlang as well.
TargetMutableName = mutable_c_var_name(ModuleName, Name)
;
MaybeForeignNames = yes(ForeignNames),
ReportErrors = no, % We've already reported them during pass 2.
get_global_name_from_foreign_names(ModuleInfo, ReportErrors, Context,
ModuleName, Name, ForeignLanguage, ForeignNames, TargetMutableName,
!Specs)
).
%-----------------------------------------------------------------------------%
%
% C mutables.
%
% Add the foreign_decl and foreign_code items that declare/define
% the global variable used to hold the mutable.
%
:- pred add_c_mutable_defn_and_decl(string::in, mer_type::in, bool::in,
mutable_thread_local::in, prog_context::in,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_c_mutable_defn_and_decl(TargetMutableName, Type, IsConstant, IsThreadLocal,
Context, !ModuleInfo, !QualInfo, !Specs) :-
% We add the foreign code declaration and definition here rather than
% in pass 2 because the target-language-specific type name depends on
% whether there are any foreign_type declarations for Type.
get_c_mutable_global_foreign_decl(!.ModuleInfo, Type,
TargetMutableName, IsConstant, IsThreadLocal, Context,
ForeignDecl),
get_c_mutable_global_foreign_defn(!.ModuleInfo, Type,
TargetMutableName, IsConstant, IsThreadLocal, Context,
ForeignDefn),
ItemStatus0 = item_status(status_local, may_be_unqualified),
add_item_decl_pass_2(ForeignDecl, ItemStatus0, _, !ModuleInfo, !Specs),
add_item_decl_pass_2(ForeignDefn, ItemStatus0, _, !ModuleInfo, !Specs).
% Create the C foreign_decl for the mutable.
% The bool argument says whether the mutable is a constant mutable or not.
%
:- pred get_c_mutable_global_foreign_decl(module_info::in, mer_type::in,
string::in, bool::in, mutable_thread_local::in, prog_context::in,
item::out) is det.
get_c_mutable_global_foreign_decl(ModuleInfo, Type, TargetMutableName,
IsConstant, IsThreadLocal, Context, DeclItem) :-
% This declaration 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 first argument in the following calls to
% 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.
HighLevelTypeName = global_foreign_type_name(no, lang_c, ModuleInfo,
Type),
LowLevelTypeName = global_foreign_type_name(yes, lang_c, ModuleInfo,
Type)
;
IsThreadLocal = mutable_thread_local,
% For thread-local mutables, the variable holds an index into an
% array.
HighLevelTypeName = "MR_Unsigned",
LowLevelTypeName = "MR_Unsigned"
),
% Constant mutables do not require mutexes as their values are never
% updated. Thread-local mutables do not require mutexes either.
(
( IsConstant = yes
; IsThreadLocal = mutable_thread_local
)
->
LockDecl = []
;
LockDecl = [
"#ifdef MR_THREAD_SAFE\n",
" extern MercuryLock ",
mutable_mutex_var_name(TargetMutableName), ";\n",
"#endif\n"
]
),
DeclBody = string.append_list([
"#ifdef MR_HIGHLEVEL_CODE\n",
" extern ", HighLevelTypeName, " ", TargetMutableName, ";\n",
"#else\n",
" extern ", LowLevelTypeName, " ", TargetMutableName, ";\n",
"#endif\n" | LockDecl]),
FDInfo =
pragma_info_foreign_decl(lang_c, foreign_decl_is_exported, DeclBody),
DeclPragma = pragma_foreign_decl(FDInfo),
DeclItemPragma = item_pragma_info(compiler(mutable_decl), DeclPragma,
Context, -1),
DeclItem = item_pragma(DeclItemPragma).
% Create the C foreign_defn for the mutable.
% The bool argument says whether the mutable is a constant mutable
% or not.
%
:- pred get_c_mutable_global_foreign_defn(module_info::in, mer_type::in,
string::in, bool::in, mutable_thread_local::in, prog_context::in,
item::out) is det.
get_c_mutable_global_foreign_defn(ModuleInfo, Type, TargetMutableName,
IsConstant, IsThreadLocal, Context, DefnItem) :-
module_info_get_globals(ModuleInfo, Globals),
globals.lookup_bool_option(Globals, mutable_always_boxed, AlwaysBoxed),
(
IsThreadLocal = mutable_not_thread_local,
TypeName = global_foreign_type_name(AlwaysBoxed, lang_c, ModuleInfo,
Type)
;
IsThreadLocal = mutable_thread_local,
% For thread-local mutables, the variable holds an index into an
% array.
TypeName = "MR_Unsigned"
),
% Constant mutables do not require mutexes as their values are never
% updated. Thread-local mutables do not require mutexes either.
(
( IsConstant = yes
; IsThreadLocal = mutable_thread_local
)
->
LockDefn = []
;
LockDefn = [
"#ifdef MR_THREAD_SAFE\n",
" MercuryLock ",
mutable_mutex_var_name(TargetMutableName), ";\n",
"#endif\n"
]
),
DefnBody = string.append_list([
TypeName, " ", TargetMutableName, ";\n" | LockDefn]),
FCInfo = pragma_info_foreign_code(lang_c, DefnBody),
DefnPragma = pragma_foreign_code(FCInfo),
DefnItemPragma = item_pragma_info(compiler(mutable_decl), DefnPragma,
Context, -1),
DefnItem = item_pragma(DefnItemPragma).
:- func global_foreign_type_name(bool, foreign_language, module_info,
mer_type) = string.
global_foreign_type_name(yes, _, _, _) = "MR_Word".
global_foreign_type_name(no, Lang, ModuleInfo, Type) =
mercury_exported_type_to_string(ModuleInfo, Lang, Type).
%-----------------------------------------------------------------------------%
:- pred add_c_mutable_preds(item_mutable_info::in, string::in,
import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_c_mutable_preds(ItemMutableInfo, TargetMutableName, !Status, !ModuleInfo,
!QualInfo, !Specs) :-
module_info_get_name(!.ModuleInfo, ModuleName),
ItemMutableInfo = item_mutable_info(MercuryMutableName, Type, InitTerm,
Inst, MutAttrs, MutVarset, Context, _SeqNum),
IsConstant = mutable_var_constant(MutAttrs),
IsThreadLocal = mutable_var_thread_local(MutAttrs),
% Set up the default attributes for the foreign_procs used for the
% access predicates.
module_info_get_globals(!.ModuleInfo, Globals),
globals.lookup_bool_option(Globals, mutable_always_boxed, AlwaysBoxed),
(
AlwaysBoxed = yes,
BoxPolicy = always_boxed
;
AlwaysBoxed = no,
BoxPolicy = native_if_possible
),
Attrs0 = default_attributes(lang_c),
set_box_policy(BoxPolicy, Attrs0, Attrs1),
set_may_call_mercury(proc_will_not_call_mercury, Attrs1, Attrs),
(
IsConstant = yes,
InitSetPredName = mutable_secret_set_pred_sym_name(ModuleName,
MercuryMutableName),
add_ccsj_constant_mutable_access_preds(TargetMutableName,
ModuleName, MercuryMutableName, Attrs, Inst, BoxPolicy,
Context, !Status, !ModuleInfo, !QualInfo, !Specs)
;
IsConstant = no,
InitSetPredName = mutable_set_pred_sym_name(ModuleName,
MercuryMutableName),
TypeName = global_foreign_type_name(AlwaysBoxed, lang_c,
!.ModuleInfo, Type),
add_c_mutable_primitive_preds(TargetMutableName, ModuleName,
MercuryMutableName, MutAttrs, Attrs, Inst, BoxPolicy, TypeName,
Context, !Status, !ModuleInfo, !QualInfo, !Specs),
add_ccsj_mutable_user_access_preds(ModuleName, MercuryMutableName,
MutAttrs, lang_c, Context, !Status, !ModuleInfo, !QualInfo, !Specs)
),
add_c_mutable_initialisation(IsConstant, IsThreadLocal,
TargetMutableName, ModuleName, MercuryMutableName, MutVarset,
InitSetPredName, InitTerm, Attrs,
Context, !Status, !ModuleInfo, !QualInfo, !Specs).
% Add the access predicates for constant mutables.
% Shared between C, C# and Java.
%
:- pred add_ccsj_constant_mutable_access_preds(string::in, module_name::in,
string::in, pragma_foreign_proc_attributes::in, mer_inst::in,
box_policy::in, prog_context::in, import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_ccsj_constant_mutable_access_preds(TargetMutableName,
ModuleName, MutableName, Attrs, Inst, BoxPolicy, Context,
!Status, !ModuleInfo, !QualInfo, !Specs) :-
varset.new_named_var("X", X, varset.init, ProgVarSet),
InstVarSet = varset.init,
set_purity(purity_pure, Attrs, ConstantGetAttrs0),
set_thread_safe(proc_thread_safe, ConstantGetAttrs0, ConstantGetAttrs),
ConstantGetFCInfo = pragma_info_foreign_proc(
ConstantGetAttrs,
mutable_get_pred_sym_name(ModuleName, MutableName),
pf_predicate,
[pragma_var(X, "X", out_mode(Inst), BoxPolicy)],
ProgVarSet,
InstVarSet,
fc_impl_ordinary("X = " ++ TargetMutableName ++ ";\n", yes(Context))
),
ConstantGetForeignProc = pragma_foreign_proc(ConstantGetFCInfo),
ConstantGetItemPragma = item_pragma_info(compiler(mutable_decl),
ConstantGetForeignProc, Context, -1),
ConstantGetItem = item_pragma(ConstantGetItemPragma),
add_item_pass_3(ConstantGetItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% NOTE: we don't need to trail the set action, since it is executed
% only once at initialization time.
ConstantSetFCInfo = pragma_info_foreign_proc(Attrs,
mutable_secret_set_pred_sym_name(ModuleName, MutableName),
pf_predicate,
[pragma_var(X, "X", in_mode(Inst), BoxPolicy)],
ProgVarSet,
InstVarSet,
fc_impl_ordinary(TargetMutableName ++ " = X;\n", yes(Context))
),
ConstantSetForeignProc = pragma_foreign_proc(ConstantSetFCInfo),
ConstantSetItemPragma = item_pragma_info(compiler(mutable_decl),
ConstantSetForeignProc, Context, -1),
ConstantSetItem = item_pragma(ConstantSetItemPragma),
add_item_pass_3(ConstantSetItem, !Status, !ModuleInfo, !QualInfo, !Specs).
% Add the foreign clauses for the mutable's primitive access and
% locking predicates.
%
:- pred add_c_mutable_primitive_preds(string::in, module_name::in, string::in,
mutable_var_attributes::in, pragma_foreign_proc_attributes::in,
mer_inst::in, box_policy::in, string::in, prog_context::in,
import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_c_mutable_primitive_preds(TargetMutableName, ModuleName, MutableName,
MutAttrs, Attrs, Inst, BoxPolicy, TypeName,
Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
IsThreadLocal = mutable_var_thread_local(MutAttrs),
set_thread_safe(proc_thread_safe, Attrs, LockAndUnlockAttrs),
% Construct the lock predicate.
MutableMutexVarName = mutable_mutex_var_name(TargetMutableName),
% XXX the second argument should be the name of the mercury predicate,
% with chars escaped as appropriate.
(
IsThreadLocal = mutable_not_thread_local,
LockForeignProcBody = string.append_list([
"#ifdef MR_THREAD_SAFE\n",
" MR_LOCK(&" ++ MutableMutexVarName ++ ",
\"" ++ MutableMutexVarName ++ "\");\n" ++
"#endif\n"
])
;
IsThreadLocal = mutable_thread_local,
LockForeignProcBody = ""
),
LockFCInfo = pragma_info_foreign_proc(LockAndUnlockAttrs,
mutable_lock_pred_sym_name(ModuleName, MutableName),
pf_predicate,
[],
varset.init, % Prog varset.
varset.init, % Inst varset.
fc_impl_ordinary(LockForeignProcBody, yes(Context))
),
LockForeignProc = pragma_foreign_proc(LockFCInfo),
LockItemPragma = item_pragma_info(compiler(mutable_decl),
LockForeignProc, Context, -1),
LockItem = item_pragma(LockItemPragma),
add_item_pass_3(LockItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Construct the unlock predicate.
% XXX as above regarding the second argument to MR_UNLOCK.
(
IsThreadLocal = mutable_not_thread_local,
UnlockForeignProcBody = string.append_list([
"#ifdef MR_THREAD_SAFE\n",
" MR_UNLOCK(&" ++ MutableMutexVarName ++ ",
\"" ++ MutableMutexVarName ++ "\");\n" ++
"#endif\n"
])
;
IsThreadLocal = mutable_thread_local,
UnlockForeignProcBody = ""
),
UnlockFCInfo = pragma_info_foreign_proc(LockAndUnlockAttrs,
mutable_unlock_pred_sym_name(ModuleName, MutableName),
pf_predicate,
[],
varset.init, % Prog varset.
varset.init, % Inst varset.
fc_impl_ordinary(UnlockForeignProcBody, yes(Context))
),
UnlockForeignProc = pragma_foreign_proc(UnlockFCInfo),
UnlockItemPragma = item_pragma_info(compiler(mutable_decl),
UnlockForeignProc, Context, -1),
UnlockItem = item_pragma(UnlockItemPragma),
add_item_pass_3(UnlockItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Construct the semipure unsafe_get_predicate.
set_purity(purity_semipure, Attrs, UnsafeGetAttrs0),
set_thread_safe(proc_thread_safe, UnsafeGetAttrs0, UnsafeGetAttrs),
varset.new_named_var("X", X, varset.init, ProgVarSet),
(
IsThreadLocal = mutable_not_thread_local,
UnsafeGetCode = "X = " ++ TargetMutableName ++ ";\n"
;
IsThreadLocal = mutable_thread_local,
UnsafeGetCode = "MR_get_thread_local_mutable(" ++
TypeName ++ ", X, " ++ TargetMutableName ++ ");\n"
),
UnsafeGetFCInfo = pragma_info_foreign_proc(UnsafeGetAttrs,
mutable_unsafe_get_pred_sym_name(ModuleName, MutableName),
pf_predicate,
[pragma_var(X, "X", out_mode(Inst), BoxPolicy)],
ProgVarSet,
varset.init, % Inst varset.
fc_impl_ordinary(UnsafeGetCode, yes(Context))
),
UnsafeGetForeignProc = pragma_foreign_proc(UnsafeGetFCInfo),
UnsafeGetItemPragma = item_pragma_info(compiler(mutable_decl),
UnsafeGetForeignProc, Context, -1),
UnsafeGetItem = item_pragma(UnsafeGetItemPragma),
add_item_pass_3(UnsafeGetItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Construct the impure unsafe_set_predicate.
set_thread_safe(proc_thread_safe, Attrs, UnsafeSetAttrs),
TrailMutableUpdates = mutable_var_trailed(MutAttrs),
(
TrailMutableUpdates = mutable_untrailed,
TrailCode = ""
;
TrailMutableUpdates = mutable_trailed,
% If we require that the mutable to be trailed then we need to be
% compiling in a trailing grade.
module_info_get_globals(!.ModuleInfo, Globals),
globals.lookup_bool_option(Globals, use_trail, UseTrail),
(
UseTrail = yes,
TrailCode = "MR_trail_current_value(&" ++
TargetMutableName ++ ");\n"
;
UseTrail = no,
Pieces =
[words("Error: trailed mutable in non-trailing grade."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs],
% This is just a dummy value.
TrailCode = ""
)
),
(
IsThreadLocal = mutable_not_thread_local,
SetCode = TargetMutableName ++ " = X;\n"
;
IsThreadLocal = mutable_thread_local,
SetCode = "MR_set_thread_local_mutable(" ++
TypeName ++ ", X, " ++ TargetMutableName ++ ");\n"
),
UnsafeSetFCInfo = pragma_info_foreign_proc(UnsafeSetAttrs,
mutable_unsafe_set_pred_sym_name(ModuleName, MutableName),
pf_predicate,
[pragma_var(X, "X", in_mode(Inst), BoxPolicy)],
ProgVarSet,
varset.init, % Inst varset.
fc_impl_ordinary(TrailCode ++ SetCode, yes(Context))
),
UnsafeSetForeignProc = pragma_foreign_proc(UnsafeSetFCInfo),
UnsafeSetItemPragma = item_pragma_info(compiler(mutable_decl),
UnsafeSetForeignProc, Context, -1),
UnsafeSetItem = item_pragma(UnsafeSetItemPragma),
add_item_pass_3(UnsafeSetItem, !Status, !ModuleInfo, !QualInfo, !Specs).
:- inst lang_ccsj
---> lang_c
; lang_csharp
; lang_java.
% Add the access predicates for a non-constant mutable.
% If the mutable has the `attach_to_io_state' attribute then add the
% versions of the access preds that take the I/O state as well.
% Shared between C, C# and Java.
%
:- pred add_ccsj_mutable_user_access_preds(module_name::in, string::in,
mutable_var_attributes::in, foreign_language::in(lang_ccsj),
prog_context::in, import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_ccsj_mutable_user_access_preds(ModuleName, MutableName, MutAttrs,
Lang, Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
varset.new_named_var("X", X, varset.init, ProgVarSet0),
LockPredName = mutable_lock_pred_sym_name(ModuleName, MutableName),
UnlockPredName = mutable_unlock_pred_sym_name(ModuleName, MutableName),
CallLock = call_expr(LockPredName, [], purity_impure) - Context,
CallUnlock = call_expr(UnlockPredName, [], purity_impure) - Context,
GetterPredName = mutable_unsafe_get_pred_sym_name(ModuleName, MutableName),
SetterPredName = mutable_unsafe_set_pred_sym_name(ModuleName, MutableName),
CallGetter = call_expr(GetterPredName, [variable(X, Context)],
purity_semipure) - Context,
CallSetter = call_expr(SetterPredName, [variable(X, context_init)],
purity_impure) - Context,
GetPredName = mutable_get_pred_sym_name(ModuleName, MutableName),
SetPredName = mutable_set_pred_sym_name(ModuleName, MutableName),
(
Lang = lang_c,
GetBody = goal_list_to_conj(Context, [CallLock, CallGetter,
CallUnlock]),
StdSetBody = goal_list_to_conj(Context, [CallLock, CallSetter,
CallUnlock])
;
( Lang = lang_java
; Lang = lang_csharp
),
% There are no separate lock predicates for Java; the synchronisation
% is performed within the "unsafe" predicates.
% XXX C# needs investigation
GetBody = CallGetter,
StdSetBody = CallSetter
),
% Construct the semipure get predicate.
StdGetBody = promise_purity_expr(purity_semipure, GetBody) - Context,
StdGetItemClause = item_clause_info(compiler(mutable_decl),
ProgVarSet0, pf_predicate, GetPredName,
[variable(X, context_init)], StdGetBody, Context, -1),
StdGetItem = item_clause(StdGetItemClause),
add_item_pass_3(StdGetItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Construct the impure set predicate.
StdSetItemClause = item_clause_info(compiler(mutable_decl),
ProgVarSet0, pf_predicate, SetPredName,
[variable(X, context_init)], StdSetBody, Context, -1),
StdSetItem = item_clause(StdSetItemClause),
add_item_pass_3(StdSetItem, !Status, !ModuleInfo, !QualInfo, !Specs),
IOStateInterface = mutable_var_attach_to_io_state(MutAttrs),
(
IOStateInterface = yes,
varset.new_named_var("IO", IO, ProgVarSet0, ProgVarSet),
% Construct the pure get predicate.
IOGetBody = promise_purity_expr(purity_pure, GetBody) - Context,
Ctxt = context_init,
IOGetItemClause = item_clause_info(compiler(mutable_decl), ProgVarSet,
pf_predicate, GetPredName,
[variable(X, Ctxt), variable(IO, Ctxt), variable(IO, Ctxt)],
IOGetBody, Context, -1
),
IOGetItem = item_clause(IOGetItemClause),
add_item_pass_3(IOGetItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Construct the pure set predicate.
%
% We just use the body of impure version and attach a promise_pure
% pragma to the predicate. (The purity pragma was added during
% stage 2.)
IOSetBody = StdSetBody,
IOSetItemClause = item_clause_info(compiler(mutable_decl), ProgVarSet,
pf_predicate, SetPredName,
[variable(X, Ctxt), variable(IO, Ctxt), variable(IO, Ctxt)],
IOSetBody, Context, -1
),
IOSetItem = item_clause(IOSetItemClause),
add_item_pass_3(IOSetItem, !Status, !ModuleInfo, !QualInfo, !Specs)
;
IOStateInterface = no
).
% Add the code required to initialise a mutable.
%
:- pred add_c_mutable_initialisation(bool::in, mutable_thread_local::in,
string::in, module_name::in, string::in, prog_varset::in,
sym_name::in, prog_term::in, pragma_foreign_proc_attributes::in,
prog_context::in, import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_c_mutable_initialisation(IsConstant, IsThreadLocal, TargetMutableName,
ModuleName, MutableName, MutVarset0, InitSetPredName, InitTerm, Attrs,
Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
% Add the `:- initialise' declaration for the mutable initialisation
% predicate.
InitPredName = mutable_init_pred_sym_name(ModuleName, MutableName),
InitPredArity = 0,
InitItemInitialise = item_initialise_info(compiler(mutable_decl),
InitPredName, InitPredArity, Context, -1),
InitItem = item_initialise(InitItemInitialise),
add_item_pass_3(InitItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Add the clause for the mutable initialisation predicate.
varset.new_named_var("X", X, MutVarset0, MutVarset),
UnifyExpr =
unify_expr(variable(X, Context), InitTerm, purity_impure)
- Context,
(
IsConstant = yes,
CallExpr =
call_expr(InitSetPredName, [variable(X, Context)], purity_impure)
- Context,
InitClauseExpr = conj_expr(UnifyExpr, CallExpr) - Context
;
IsConstant = no,
(
IsThreadLocal = mutable_not_thread_local,
% Construct the clause for the mutex initialisation predicate.
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 = string.append_list([
TargetMutableName,
" = MR_new_thread_local_mutable_index();\n"
])
),
PreInitPredName = mutable_pre_init_pred_sym_name(ModuleName,
MutableName),
PreInitFCInfo = pragma_info_foreign_proc(Attrs,
PreInitPredName,
pf_predicate,
[],
varset.init, % ProgVarSet
varset.init, % InstVarSet
fc_impl_ordinary(PreInitCode, yes(Context))
),
PreInitForeignProc = pragma_foreign_proc(PreInitFCInfo),
PreInitItemPragma = item_pragma_info(compiler(mutable_decl),
PreInitForeignProc, Context, -1),
PreInitItem = item_pragma(PreInitItemPragma),
add_item_pass_3(PreInitItem, !Status, !ModuleInfo, !QualInfo,
!Specs),
CallPreInitExpr =
call_expr(PreInitPredName, [], purity_impure) - Context,
CallSetPredExpr =
call_expr(InitSetPredName, [variable(X, Context)], purity_impure)
- Context,
InitClauseExpr = goal_list_to_conj(Context,
[CallPreInitExpr, UnifyExpr, CallSetPredExpr])
),
% See the comments for prog_io.parse_mutable_decl for the reason
% why we _must_ use MutVarset here.
PredItemClause = item_clause_info(compiler(mutable_decl), MutVarset,
pf_predicate, InitPredName, [], InitClauseExpr, Context, -1),
PredItem = item_clause(PredItemClause),
add_item_pass_3(PredItem, !Status, !ModuleInfo, !QualInfo, !Specs).
%-----------------------------------------------------------------------------%
%
% C#/Java mutables
%
:- inst lang_csharp_java
---> lang_csharp
; lang_java.
% Add foreign_code item that defines the global variable used to hold the
% mutable.
%
:- pred add_csharp_java_mutable_defn(foreign_language::in(lang_csharp_java),
string::in, mer_type::in, mutable_thread_local::in, prog_context::in,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_csharp_java_mutable_defn(Lang, TargetMutableName, Type, IsThreadLocal,
Context, !ModuleInfo, !QualInfo, !Specs) :-
get_csharp_java_mutable_global_foreign_defn(Lang, TargetMutableName,
Type, IsThreadLocal, Context, DefnBody),
DefnFCInfo = pragma_info_foreign_code(Lang, DefnBody),
DefnPragma = pragma_foreign_code(DefnFCInfo),
DefnItemPragma = item_pragma_info(compiler(mutable_decl), DefnPragma,
Context, -1),
ForeignDefn = item_pragma(DefnItemPragma),
ItemStatus0 = item_status(status_local, may_be_unqualified),
add_item_decl_pass_2(ForeignDefn, ItemStatus0, _, !ModuleInfo, !Specs).
:- pred get_csharp_java_mutable_global_foreign_defn(
foreign_language::in(lang_csharp_java), string::in, mer_type::in,
mutable_thread_local::in, prog_context::in, string::out) is det.
get_csharp_java_mutable_global_foreign_defn(Lang, TargetMutableName, Type,
IsThreadLocal, _Context, DefnBody) :-
(
Lang = lang_csharp,
(
IsThreadLocal = mutable_not_thread_local,
( Type = int_type ->
TypeStr = "int"
;
TypeStr = "object"
)
;
IsThreadLocal = mutable_thread_local,
TypeStr = "int"
),
DefnBody = string.append_list([
"static ", TypeStr, " ", TargetMutableName, ";\n"])
;
Lang = lang_java,
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).
( Type = int_type ->
TypeStr = "int"
;
TypeStr = "java.lang.Object"
),
DefnBody = string.append_list([
"static ", TypeStr, " ", TargetMutableName, ";\n"])
;
Lang = lang_java,
IsThreadLocal = mutable_thread_local,
( Type = int_type ->
TypeStr = "java.lang.Integer"
;
TypeStr = "java.lang.Object"
),
DefnBody = string.append_list([
"static java.lang.ThreadLocal<", TypeStr, "> ",
TargetMutableName,
" = new java.lang.InheritableThreadLocal<", TypeStr, ">();\n"
])
).
:- pred add_csharp_java_mutable_preds(item_mutable_info::in,
foreign_language::in(lang_csharp_java), string::in,
import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_csharp_java_mutable_preds(ItemMutable, Lang, TargetMutableName,
!Status, !ModuleInfo, !QualInfo, !Specs) :-
module_info_get_name(!.ModuleInfo, ModuleName),
ItemMutable = item_mutable_info(MercuryMutableName, Type, InitTerm, Inst,
MutAttrs, MutVarset, Context, _SeqNum),
IsConstant = mutable_var_constant(MutAttrs),
Attrs0 = default_attributes(Lang),
% The mutable variable name is not module-qualified so cannot 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),
module_info_get_globals(!.ModuleInfo, Globals),
globals.lookup_bool_option(Globals, mutable_always_boxed, AlwaysBoxed),
(
AlwaysBoxed = yes,
BoxPolicy = always_boxed
;
AlwaysBoxed = no,
BoxPolicy = native_if_possible
),
(
IsConstant = yes,
InitSetPredName = mutable_secret_set_pred_sym_name(ModuleName,
MercuryMutableName),
add_ccsj_constant_mutable_access_preds(TargetMutableName,
ModuleName, MercuryMutableName, Attrs, Inst, BoxPolicy,
Context, !Status, !ModuleInfo, !QualInfo, !Specs)
;
IsConstant = no,
InitSetPredName = mutable_set_pred_sym_name(ModuleName,
MercuryMutableName),
add_csharp_java_mutable_primitive_preds(Lang, TargetMutableName,
ModuleName, MercuryMutableName, Type, MutAttrs, Attrs, Inst,
BoxPolicy, Context, !Status, !ModuleInfo, !QualInfo, !Specs),
add_ccsj_mutable_user_access_preds(ModuleName, MercuryMutableName,
MutAttrs, Lang,
Context, !Status, !ModuleInfo, !QualInfo, !Specs)
),
% The C# thread-local mutable implementation requires array indices to be
% allocated in pre-init predicates.
(
Lang = lang_csharp,
mutable_var_thread_local(MutAttrs) = mutable_thread_local
->
add_csharp_thread_local_mutable_pre_init_pred(TargetMutableName,
ModuleName, MercuryMutableName, Attrs, CallPreInitExpr,
Context, !Status, !ModuleInfo, !QualInfo, !Specs)
;
CallPreInitExpr = true_expr - Context
),
add_csharp_java_mutable_initialisation(ModuleName, MercuryMutableName,
MutVarset, CallPreInitExpr, InitSetPredName, InitTerm,
Context, !Status, !ModuleInfo, !QualInfo, !Specs).
:- pred add_csharp_thread_local_mutable_pre_init_pred(string::in,
module_name::in, string::in, pragma_foreign_proc_attributes::in, goal::out,
prog_context::in, import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_csharp_thread_local_mutable_pre_init_pred(TargetMutableName,
ModuleName, MutableName, Attrs, CallPreInitExpr,
Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
PreInitCode = string.append_list([
TargetMutableName, " = runtime.ThreadLocalMutables.new_index();\n"
]),
PreInitPredName = mutable_pre_init_pred_sym_name(ModuleName,
MutableName),
PreInitFCInfo = pragma_info_foreign_proc(Attrs,
PreInitPredName,
pf_predicate,
[],
varset.init, % ProgVarSet
varset.init, % InstVarSet
fc_impl_ordinary(PreInitCode, yes(Context))
),
PreInitForeignProc = pragma_foreign_proc(PreInitFCInfo),
PreInitItemPragma = item_pragma_info(compiler(mutable_decl),
PreInitForeignProc, Context, -1),
PreInitItem = item_pragma(PreInitItemPragma),
add_item_pass_3(PreInitItem, !Status, !ModuleInfo, !QualInfo,
!Specs),
CallPreInitExpr =
call_expr(PreInitPredName, [], purity_impure) - Context.
% Add the foreign clauses for the mutable's primitive access and
% locking predicates.
%
:- pred add_csharp_java_mutable_primitive_preds(
foreign_language::in(lang_csharp_java), string::in, module_name::in,
string::in, mer_type::in, mutable_var_attributes::in,
pragma_foreign_proc_attributes::in, mer_inst::in, box_policy::in,
prog_context::in, import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_csharp_java_mutable_primitive_preds(Lang, TargetMutableName, ModuleName,
MutableName, Type, MutAttrs, Attrs, Inst, BoxPolicy,
Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
IsThreadLocal = mutable_var_thread_local(MutAttrs),
% Construct the semipure get predicate.
set_purity(purity_semipure, Attrs, GetAttrs0),
set_thread_safe(proc_thread_safe, GetAttrs0, GetAttrs),
varset.new_named_var("X", X, varset.init, ProgVarSet),
(
IsThreadLocal = mutable_not_thread_local,
GetCode = "\tX = " ++ TargetMutableName ++ ";\n"
;
IsThreadLocal = mutable_thread_local,
Lang = lang_java,
IsThreadLocal = mutable_thread_local,
GetCode = "\tX = " ++ TargetMutableName ++ ".get();\n"
;
IsThreadLocal = mutable_thread_local,
Lang = lang_csharp,
( Type = int_type ->
Cast = "(int) "
;
Cast = ""
),
GetCode = string.append_list([
"\tX = ", Cast, "runtime.ThreadLocalMutables.get(",
TargetMutableName, ");\n"
])
),
GetFCInfo = pragma_info_foreign_proc(GetAttrs,
mutable_unsafe_get_pred_sym_name(ModuleName, MutableName),
pf_predicate,
[pragma_var(X, "X", out_mode(Inst), BoxPolicy)],
ProgVarSet,
varset.init, % Inst varset.
fc_impl_ordinary(GetCode, yes(Context))
),
GetForeignProc = pragma_foreign_proc(GetFCInfo),
GetItemPragma = item_pragma_info(compiler(mutable_decl),
GetForeignProc, Context, -1),
GetItem = item_pragma(GetItemPragma),
add_item_pass_3(GetItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Construct the impure set predicate.
set_thread_safe(proc_thread_safe, Attrs, SetAttrs),
TrailMutableUpdates = mutable_var_trailed(MutAttrs),
(
TrailMutableUpdates = mutable_untrailed,
TrailCode = ""
;
TrailMutableUpdates = mutable_trailed,
Pieces = [words("Error: trailed mutable in non-trailed grade."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs],
% This is just a dummy value.
TrailCode = ""
),
(
IsThreadLocal = mutable_not_thread_local,
SetCode = "\t" ++ TargetMutableName ++ " = X;\n"
;
IsThreadLocal = mutable_thread_local,
Lang = lang_java,
SetCode = "\t" ++ TargetMutableName ++ ".set(X);\n"
;
IsThreadLocal = mutable_thread_local,
Lang = lang_csharp,
SetCode = "\truntime.ThreadLocalMutables.set(" ++
TargetMutableName ++ ", X);\n"
),
SetFCInfo = pragma_info_foreign_proc(SetAttrs,
mutable_unsafe_set_pred_sym_name(ModuleName, MutableName),
pf_predicate,
[pragma_var(X, "X", in_mode(Inst), BoxPolicy)],
ProgVarSet,
varset.init, % Inst varset.
fc_impl_ordinary(TrailCode ++ SetCode, yes(Context))
),
SetForeignProc = pragma_foreign_proc(SetFCInfo),
SetItemPragma = item_pragma_info(compiler(mutable_decl),
SetForeignProc, Context, -1),
SetItem = item_pragma(SetItemPragma),
add_item_pass_3(SetItem, !Status, !ModuleInfo, !QualInfo, !Specs).
% Add the code required to initialise a mutable.
%
:- pred add_csharp_java_mutable_initialisation(module_name::in, string::in,
prog_varset::in, goal::in, sym_name::in, prog_term::in,
prog_context::in, import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_csharp_java_mutable_initialisation(ModuleName, MutableName, MutVarset0,
CallPreInitExpr, InitSetPredName, InitTerm,
Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
% Add the `:- initialise' declaration for the mutable initialisation
% predicate.
InitPredName = mutable_init_pred_sym_name(ModuleName, MutableName),
InitPredArity = 0,
InitItemInitialise = item_initialise_info(compiler(mutable_decl),
InitPredName, InitPredArity, Context, -1),
InitItem = item_initialise(InitItemInitialise),
add_item_pass_3(InitItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Add the clause for the mutable initialisation predicate.
varset.new_named_var("X", X, MutVarset0, MutVarset),
UnifyExpr =
unify_expr(variable(X, Context), InitTerm, purity_impure)
- Context,
CallSetPredExpr =
call_expr(InitSetPredName, [variable(X, Context)], purity_impure)
- Context,
InitClauseExpr = goal_list_to_conj(Context,
[CallPreInitExpr, UnifyExpr, CallSetPredExpr]),
% See the comments for prog_io.parse_mutable_decl for the reason
% why we _must_ use MutVarset here.
PredItemClause = item_clause_info(compiler(mutable_decl), MutVarset,
pf_predicate, InitPredName, [], InitClauseExpr, Context, -1),
PredItem = item_clause(PredItemClause),
add_item_pass_3(PredItem, !Status, !ModuleInfo, !QualInfo, !Specs).
%-----------------------------------------------------------------------------%
%
% Erlang mutables
%
:- pred add_erlang_mutable_preds(item_mutable_info::in, string::in,
import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_erlang_mutable_preds(ItemMutable, TargetMutableName,
!Status, !ModuleInfo, !QualInfo, !Specs) :-
module_info_get_name(!.ModuleInfo, ModuleName),
ItemMutable = item_mutable_info(MutableName, _Type, InitTerm, Inst,
MutAttrs, MutVarset, Context, _SeqNum),
IsConstant = mutable_var_constant(MutAttrs),
(
IsConstant = yes,
InitSetPredName = mutable_secret_set_pred_sym_name(ModuleName,
MutableName),
add_erlang_constant_mutable_access_preds(TargetMutableName,
ModuleName, MutableName, Inst,
Context, !Status, !ModuleInfo, !QualInfo, !Specs)
;
IsConstant = no,
InitSetPredName = mutable_set_pred_sym_name(ModuleName,
MutableName),
add_erlang_mutable_user_access_preds(TargetMutableName,
ModuleName, MutableName, MutAttrs, Inst,
Context, !Status, !ModuleInfo, !QualInfo, !Specs)
),
add_erlang_mutable_initialisation(ModuleName, MutableName,
MutVarset, InitSetPredName, InitTerm,
Context, !Status, !ModuleInfo, !QualInfo, !Specs).
% Add the access predicates for constant mutables.
%
:- pred add_erlang_constant_mutable_access_preds(string::in,
module_name::in, string::in, mer_inst::in, prog_context::in,
import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_erlang_constant_mutable_access_preds(TargetMutableName,
ModuleName, MutableName, Inst,
Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
varset.new_named_var("X", X, varset.init, ProgVarSet),
InstVarSet = varset.init,
Attrs = default_attributes(lang_erlang),
set_purity(purity_pure, Attrs, ConstantGetAttrs0),
set_thread_safe(proc_thread_safe, ConstantGetAttrs0, ConstantGetAttrs),
% Getter.
GetCode = erlang_mutable_get_code(TargetMutableName),
ConstantGetFCInfo = pragma_info_foreign_proc(
ConstantGetAttrs,
mutable_get_pred_sym_name(ModuleName, MutableName),
pf_predicate,
[pragma_var(X, "X", out_mode(Inst), native_if_possible)],
ProgVarSet,
InstVarSet,
fc_impl_ordinary(GetCode, yes(Context))
),
ConstantGetForeignProc = pragma_foreign_proc(ConstantGetFCInfo),
ConstantGetItemPragma = item_pragma_info(compiler(mutable_decl),
ConstantGetForeignProc, Context, -1),
ConstantGetItem = item_pragma(ConstantGetItemPragma),
add_item_pass_3(ConstantGetItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Secret setter.
SetCode = erlang_mutable_set_code(TargetMutableName),
ConstantSetFCInfo = pragma_info_foreign_proc(Attrs,
mutable_secret_set_pred_sym_name(ModuleName, MutableName),
pf_predicate,
[pragma_var(X, "X", in_mode(Inst), native_if_possible)],
ProgVarSet,
InstVarSet,
fc_impl_ordinary(SetCode, yes(Context))
),
ConstantSetForeignProc = pragma_foreign_proc(ConstantSetFCInfo),
ConstantSetItemPragma = item_pragma_info(compiler(mutable_decl),
ConstantSetForeignProc, Context, -1),
ConstantSetItem = item_pragma(ConstantSetItemPragma),
add_item_pass_3(ConstantSetItem, !Status, !ModuleInfo, !QualInfo, !Specs).
% Add the access predicates for a non-constant mutable.
% If the mutable has the `attach_to_io_state' attribute then add the
% versions of the access preds that take the I/O state as well.
%
:- pred add_erlang_mutable_user_access_preds(string::in,
module_name::in, string::in, mutable_var_attributes::in, mer_inst::in,
prog_context::in, import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_erlang_mutable_user_access_preds(TargetMutableName,
ModuleName, MutableName, MutAttrs, Inst, Context,
!Status, !ModuleInfo, !QualInfo, !Specs) :-
IsThreadLocal = mutable_var_thread_local(MutAttrs),
Attrs = default_attributes(lang_erlang),
varset.new_named_var("X", X, varset.init, ProgVarSet0),
% Construct the semipure get predicate.
set_purity(purity_semipure, Attrs, GetAttrs0),
set_thread_safe(proc_thread_safe, GetAttrs0, GetAttrs),
(
IsThreadLocal = mutable_not_thread_local,
GetCode = erlang_mutable_get_code(TargetMutableName)
;
IsThreadLocal = mutable_thread_local,
% XXX this will need to change. `thread_local' mutables are supposed
% to be inherited when a child process is spawned, but Erlang process
% dictionary values are not automatically inherited. Hence we will
% probably need another level of indirection.
GetCode = "X = get({'MR_thread_local_mutable', " ++
TargetMutableName ++ "})"
),
GetPredName = mutable_get_pred_sym_name(ModuleName, MutableName),
GetFCInfo = pragma_info_foreign_proc(GetAttrs,
GetPredName,
pf_predicate,
[pragma_var(X, "X", out_mode(Inst), native_if_possible)],
ProgVarSet0,
varset.init, % Inst varset.
fc_impl_ordinary(GetCode, yes(Context))
),
GetForeignProc = pragma_foreign_proc(GetFCInfo),
GetItemPragma = item_pragma_info(compiler(mutable_decl), GetForeignProc,
Context, -1),
GetItem = item_pragma(GetItemPragma),
add_item_pass_3(GetItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Construct the impure set predicate.
set_purity(purity_impure, Attrs, SetAttrs0),
set_thread_safe(proc_thread_safe, SetAttrs0, SetAttrs),
(
IsThreadLocal = mutable_not_thread_local,
SetCode = erlang_mutable_set_code(TargetMutableName)
;
IsThreadLocal = mutable_thread_local,
% XXX this will need to change (see the comment for the getter)
SetCode = "put({'MR_thread_local_mutable', " ++
TargetMutableName ++ "}, X)"
),
SetPredName = mutable_set_pred_sym_name(ModuleName, MutableName),
SetFCInfo = pragma_info_foreign_proc(SetAttrs,
SetPredName,
pf_predicate,
[pragma_var(X, "X", in_mode(Inst), native_if_possible)],
ProgVarSet0,
varset.init, % Inst varset.
fc_impl_ordinary(SetCode, yes(Context))
),
SetForeignProc = pragma_foreign_proc(SetFCInfo),
SetItemPragma = item_pragma_info(compiler(mutable_decl), SetForeignProc,
Context, -1),
SetItem = item_pragma(SetItemPragma),
add_item_pass_3(SetItem, !Status, !ModuleInfo, !QualInfo, !Specs),
IOStateInterface = mutable_var_attach_to_io_state(MutAttrs),
(
IOStateInterface = yes,
varset.new_named_var("IO", IO, ProgVarSet0, ProgVarSet),
Ctxt = context_init,
% Construct the pure get predicate.
% This just calls the semipure get predicate with a promise_pure
% around it.
CallSemipureGet = call_expr(GetPredName, [variable(X, Context)],
purity_semipure) - Context,
IOGetBody = promise_purity_expr(purity_pure, CallSemipureGet)
- Context,
IOGetItemClause = item_clause_info(compiler(mutable_decl), ProgVarSet,
pf_predicate, GetPredName,
[variable(X, Ctxt), variable(IO, Ctxt), variable(IO, Ctxt)],
IOGetBody, Context, -1),
IOGetItem = item_clause(IOGetItemClause),
add_item_pass_3(IOGetItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Construct the pure set predicate.
%
% We just call the impure version and attach a promise_pure
% pragma to the predicate. (The purity pragma was added during
% stage 2.)
CallImpureSet = call_expr(SetPredName, [variable(X, Context)],
purity_impure) - Context,
IOSetItemClause = item_clause_info(compiler(mutable_decl), ProgVarSet,
pf_predicate, SetPredName,
[variable(X, Ctxt), variable(IO, Ctxt), variable(IO, Ctxt)],
CallImpureSet, Context, -1),
IOSetItem = item_clause(IOSetItemClause),
add_item_pass_3(IOSetItem, !Status, !ModuleInfo, !QualInfo, !Specs)
;
IOStateInterface = no
).
:- func erlang_mutable_get_code(string) = string.
erlang_mutable_get_code(TargetMutableName) =
string.append_list([
"'ML_erlang_global_server' ! {get_mutable, ",
TargetMutableName, ", self()},\n",
"receive\n",
" {get_mutable_ack, Value} ->\n",
" X = Value\n",
"end\n"
]).
:- func erlang_mutable_set_code(string) = string.
erlang_mutable_set_code(TargetMutableName) =
"'ML_erlang_global_server' ! {set_mutable, " ++
TargetMutableName ++ ", X}".
% Add the code required to initialise a mutable.
%
:- pred add_erlang_mutable_initialisation(module_name::in, string::in,
prog_varset::in, sym_name::in, prog_term::in, prog_context::in,
import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_erlang_mutable_initialisation(ModuleName, MutableName,
MutVarset0, InitSetPredName, InitTerm,
Context, !Status, !ModuleInfo, !QualInfo, !Specs) :-
% Add the `:- initialise' declaration for the mutable initialisation
% predicate.
InitPredName = mutable_init_pred_sym_name(ModuleName, MutableName),
InitPredArity = 0,
InitItemInitialise = item_initialise_info(compiler(mutable_decl),
InitPredName, InitPredArity, Context, -1),
InitItem = item_initialise(InitItemInitialise),
add_item_pass_3(InitItem, !Status, !ModuleInfo, !QualInfo, !Specs),
% Add the clause for the mutable initialisation predicate.
%
% See the comments for prog_io.parse_mutable_decl for the reason
% why we _must_ use MutVarset here.
varset.new_named_var("X", X, MutVarset0, MutVarset),
UnifyExpr =
unify_expr(variable(X, Context), InitTerm, purity_impure)
- Context,
CallExpr =
call_expr(InitSetPredName, [variable(X, Context)], purity_impure)
- Context,
InitClauseExpr = conj_expr(UnifyExpr, CallExpr) - Context,
PredItemClause = item_clause_info(compiler(mutable_decl), MutVarset,
pf_predicate, InitPredName, [], InitClauseExpr, Context, -1),
PredItem = item_clause(PredItemClause),
add_item_pass_3(PredItem, !Status, !ModuleInfo, !QualInfo, !Specs).
%-----------------------------------------------------------------------------%
:- pred add_solver_type_mutable_items_clauses(list(item_mutable_info)::in,
import_status::in, import_status::out,
module_info::in, module_info::out, qual_info::in, qual_info::out,
list(error_spec)::in, list(error_spec)::out) is det.
add_solver_type_mutable_items_clauses([], !Status,
!ModuleInfo, !QualInfo, !Specs).
add_solver_type_mutable_items_clauses([MutableInfo | MutableInfos], !Status,
!ModuleInfo, !QualInfo, !Specs) :-
add_pass_3_mutable(MutableInfo, !.Status, !ModuleInfo, !QualInfo, !Specs),
add_solver_type_mutable_items_clauses(MutableInfos, !Status,
!ModuleInfo, !QualInfo, !Specs).
%-----------------------------------------------------------------------------%
% If a module_defn updates the import_status, return the new status
% and whether uses of the following items must be module qualified,
% otherwise fail.
%
:- pred module_defn_update_import_status(module_defn::in, item_status::out)
is semidet.
module_defn_update_import_status(ModuleDefn, Status) :-
(
ModuleDefn = md_interface,
Status = item_status(status_exported, may_be_unqualified)
;
ModuleDefn = md_implementation,
Status = item_status(status_local, may_be_unqualified)
;
ModuleDefn = md_implementation_but_exported_to_submodules,
Status = item_status(status_exported_to_submodules, may_be_unqualified)
;
ModuleDefn = md_imported(Section),
Status = item_status(status_imported(Section), may_be_unqualified)
;
ModuleDefn = md_used(Section),
Status = item_status(status_imported(Section), must_be_qualified)
;
ModuleDefn = md_opt_imported,
Status = item_status(status_opt_imported, must_be_qualified)
;
ModuleDefn = md_abstract_imported,
Status = item_status(status_abstract_imported, must_be_qualified)
).
%-----------------------------------------------------------------------------%
:- pred add_promise_clause(promise_type::in, list(term(prog_var_type))::in,
prog_varset::in, goal::in, prog_context::in, import_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_promise_clause(PromiseType, HeadVars, VarSet, Goal, Context, Status,
!ModuleInfo, !QualInfo, !Specs) :-
term.context_line(Context, Line),
term.context_file(Context, File),
string.format(prog_out.promise_to_string(PromiseType) ++
"__%d__%s", [i(Line), s(File)], Name),
% Promise declarations are recorded as a predicate with a goal_type
% of goal_type_promise(X), where X is of promise_type. This allows us
% to leverage off all the other checks in the compiler that operate
% on predicates.
%
% :- promise all [A,B,R] ( R = A + B <=> R = B + A ).
%
% becomes
%
% promise.lineno_filename(A, B, R) :-
% ( R = A + B <=> R = B + A ).
module_info_get_name(!.ModuleInfo, ModuleName),
module_add_clause(VarSet, pf_predicate, qualified(ModuleName, Name),
HeadVars, Goal, Status, Context, no, goal_type_promise(PromiseType),
!ModuleInfo, !QualInfo, !Specs).
add_stratified_pred(PragmaName, Name, Arity, Context, !ModuleInfo, !Specs) :-
module_info_get_predicate_table(!.ModuleInfo, PredTable0),
predicate_table_lookup_sym_arity(PredTable0, is_fully_qualified,
Name, Arity, PredIds),
(
PredIds = [_ | _],
module_info_get_stratified_preds(!.ModuleInfo, StratPredIds0),
set.insert_list(PredIds, StratPredIds0, StratPredIds),
module_info_set_stratified_preds(StratPredIds, !ModuleInfo)
;
PredIds = [],
DescPieces = [quote(":- pragma " ++ PragmaName), words("declaration")],
undefined_pred_or_func_error(Name, Arity, Context, DescPieces, !Specs)
).
%-----------------------------------------------------------------------------%
add_pred_marker(PragmaName, Name, Arity, Status, Context, Marker,
ConflictMarkers, !ModuleInfo, !Specs) :-
( marker_must_be_exported(Marker) ->
MustBeExported = yes
;
MustBeExported = no
),
do_add_pred_marker(PragmaName, Name, Arity, Status, MustBeExported,
Context, add_marker_pred_info(Marker), !ModuleInfo, PredIds, !Specs),
module_info_get_preds(!.ModuleInfo, Preds),
pragma_check_markers(Preds, PredIds, ConflictMarkers, Conflict),
(
Conflict = yes,
pragma_conflict_error(Name, Arity, Context, PragmaName, !Specs)
;
Conflict = no
).
do_add_pred_marker(PragmaName, Name, Arity, Status, MustBeExported, Context,
UpdatePredInfo, !ModuleInfo, PredIds, !Specs) :-
get_matching_pred_ids(!.ModuleInfo, Name, Arity, PredIds),
(
PredIds = [_ | _],
module_info_get_predicate_table(!.ModuleInfo, PredTable0),
predicate_table_get_preds(PredTable0, Preds0),
pragma_add_marker(PredIds, UpdatePredInfo, Status,
MustBeExported, Preds0, Preds, WrongStatus),
(
WrongStatus = yes,
pragma_status_error(Name, Arity, Context, PragmaName, !Specs)
;
WrongStatus = no
),
predicate_table_set_preds(Preds, PredTable0, PredTable),
module_info_set_predicate_table(PredTable, !ModuleInfo)
;
PredIds = [],
DescPieces = [quote(":- pragma " ++ PragmaName), words("declaration")],
undefined_pred_or_func_error(Name, Arity, Context, DescPieces, !Specs)
).
:- pred get_matching_pred_ids(module_info::in, sym_name::in, arity::in,
list(pred_id)::out) is det.
get_matching_pred_ids(Module0, Name, Arity, PredIds) :-
module_info_get_predicate_table(Module0, PredTable0),
% Check that the pragma is module qualified.
(
Name = unqualified(_),
unexpected($module, $pred, "unqualified name")
;
Name = qualified(_, _),
predicate_table_lookup_sym_arity(PredTable0, is_fully_qualified,
Name, Arity, PredIds)
).
module_mark_as_external(PredName, Arity, Context, !ModuleInfo, !Specs) :-
% `external' declarations can only apply to things defined in this module,
% since everything else is already external.
module_info_get_predicate_table(!.ModuleInfo, PredicateTable0),
predicate_table_lookup_sym_arity(PredicateTable0, is_fully_qualified,
PredName, Arity, PredIds),
(
PredIds = [_ | _],
module_mark_preds_as_external(PredIds, !ModuleInfo)
;
PredIds = [],
undefined_pred_or_func_error(PredName, Arity, Context,
[quote(":- external"), words("declaration")], !Specs)
).
:- pred module_mark_preds_as_external(list(pred_id)::in,
module_info::in, module_info::out) is det.
module_mark_preds_as_external([], !ModuleInfo).
module_mark_preds_as_external([PredId | PredIds], !ModuleInfo) :-
module_info_get_preds(!.ModuleInfo, Preds0),
map.lookup(Preds0, PredId, PredInfo0),
pred_info_mark_as_external(PredInfo0, PredInfo),
map.det_update(PredId, PredInfo, Preds0, Preds),
module_info_set_preds(Preds, !ModuleInfo),
module_mark_preds_as_external(PredIds, !ModuleInfo).
% For each pred_id in the list, check whether markers present in the list
% of conflicting markers are also present in the corresponding pred_info.
% The bool indicates whether there was a conflicting marker present.
%
:- pred pragma_check_markers(pred_table::in, list(pred_id)::in,
list(marker)::in, bool::out) is det.
pragma_check_markers(_, [], _, no).
pragma_check_markers(PredTable, [PredId | PredIds], ConflictList,
WasConflict) :-
map.lookup(PredTable, PredId, PredInfo),
pred_info_get_markers(PredInfo, Markers),
(
list.member(Marker, ConflictList),
check_marker(Markers, Marker)
->
WasConflict = yes
;
pragma_check_markers(PredTable, PredIds, ConflictList, WasConflict)
).
% For each pred_id in the list, add the given markers to the
% list of markers in the corresponding pred_info.
%
:- pred pragma_add_marker(list(pred_id)::in,
add_marker_pred_info::in(add_marker_pred_info), import_status::in,
bool::in, pred_table::in, pred_table::out, bool::out) is det.
pragma_add_marker([], _, _, _, !PredTable, no).
pragma_add_marker([PredId | PredIds], UpdatePredInfo, Status, MustBeExported,
!PredTable, WrongStatus) :-
map.lookup(!.PredTable, PredId, PredInfo0),
UpdatePredInfo(PredInfo0, PredInfo),
(
pred_info_is_exported(PredInfo),
MustBeExported = yes,
Status \= status_exported
->
WrongStatus0 = yes
;
WrongStatus0 = no
),
map.det_update(PredId, PredInfo, !PredTable),
pragma_add_marker(PredIds, UpdatePredInfo, Status, MustBeExported,
!PredTable, WrongStatus1),
bool.or(WrongStatus0, WrongStatus1, WrongStatus).
:- pred add_marker_pred_info(marker::in, pred_info::in, pred_info::out) is det.
add_marker_pred_info(Marker, !PredInfo) :-
pred_info_get_markers(!.PredInfo, Markers0),
add_marker(Marker, Markers0, Markers),
pred_info_set_markers(Markers, !PredInfo).
% Succeed if a marker for an exported procedure must also be exported.
%
:- pred marker_must_be_exported(marker::in) is semidet.
marker_must_be_exported(_) :-
semidet_fail.
maybe_check_field_access_function(FuncName, FuncArity, Status, Context,
ModuleInfo, !Specs) :-
(
is_field_access_function_name(ModuleInfo, FuncName, FuncArity,
AccessType, FieldName)
->
check_field_access_function(AccessType, FieldName, FuncName,
FuncArity, Status, Context, ModuleInfo, !Specs)
;
true
).
:- pred check_field_access_function(field_access_type::in, ctor_field_name::in,
sym_name::in, arity::in, import_status::in, prog_context::in,
module_info::in, list(error_spec)::in, list(error_spec)::out) is det.
check_field_access_function(_AccessType, FieldName, FuncName, FuncArity,
FuncStatus, Context, ModuleInfo, !Specs) :-
adjust_func_arity(pf_function, FuncArity, PredArity),
FuncCallId = simple_call_id(pf_function, FuncName, PredArity),
% Check that a function applied to an exported type is also exported.
module_info_get_ctor_field_table(ModuleInfo, CtorFieldTable),
(
% Abstract types have status `abstract_exported', so errors won't be
% reported for local field access functions for them.
map.search(CtorFieldTable, FieldName, [FieldDefn]),
FieldDefn = hlds_ctor_field_defn(_, DefnStatus, _, _, _),
DefnStatus = status_exported,
FuncStatus \= status_exported
->
report_field_status_mismatch(Context, FuncCallId, !Specs)
;
true
).
%-----------------------------------------------------------------------------%
:- pred report_field_status_mismatch(prog_context::in, simple_call_id::in,
list(error_spec)::in, list(error_spec)::out) is det.
report_field_status_mismatch(Context, CallId, !Specs) :-
Pieces = [words("In declaration of"), simple_call(CallId), suffix(":"), nl,
words("error: a field access function for an exported field"),
words("must also be exported."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs].
:- pred report_unexpected_decl(string::in, prog_context::in,
list(error_spec)::in, list(error_spec)::out) is det.
report_unexpected_decl(Descr, Context, !Specs) :-
Pieces = [words("Error: unexpected or incorrect"),
quote(Descr), words("declaration."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs].
:- pred pragma_status_error(sym_name::in, int::in, prog_context::in,
string::in, list(error_spec)::in, list(error_spec)::out) is det.
pragma_status_error(Name, Arity, Context, PragmaName, !Specs) :-
Pieces = [words("Error: `:- pragma " ++ PragmaName ++ "'"),
words("declaration for exported predicate or function"),
sym_name_and_arity(Name / Arity),
words("must also be exported."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs].
:- pred pragma_conflict_error(sym_name::in, int::in, prog_context::in,
string::in, list(error_spec)::in, list(error_spec)::out) is det.
pragma_conflict_error(Name, Arity, Context, PragmaName, !Specs) :-
Pieces = [words("Error: `:- pragma " ++ PragmaName ++ "'"),
words("declaration conflicts with previous pragma for"),
sym_name_and_arity(Name / Arity), suffix("."), nl],
Msg = simple_msg(Context, [always(Pieces)]),
Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
!:Specs = [Spec | !.Specs].
%-----------------------------------------------------------------------------%
:- end_module hlds.make_hlds.make_hlds_passes.
%-----------------------------------------------------------------------------%