[m-rev.] Add `:- mutable' declaration to the language.
Julien Fischer
juliensf at cs.mu.OZ.AU
Thu Sep 1 16:43:49 AEST 2005
On Thu, 1 Sep 2005, Ralph Becket wrote:
> Estimated hours taken: 24
> Branches: main
>
> Add `:- mutable' directives to the language, providing modules with private
> mutable variables. A directives
>
> :- mutable(x, int, 0, ground, [thread_safe]).
>
> leads to the compiler generating the following:
>
> :- semipure pred get_x(int::out(ground)) is det.
> :- impure pred set_x(int::in(ground)) is det.
> :- pred initialise_mutable_x(io::di, io::uo) is det.
> :- initialise initialise_mutable_x/2.
>
> initialise_mutable_x(!IO) :-
> promise_pure(
> impure set_x(0)
> ).
>
> :- pragma foreign_decl("C", "MR_Word mutable_variable_x;").
>
> :- pragma foreign_proc("C", get_x(X::out(ground)), [thread_safe],
> "MR_trail_current_value(&mutable_variable_x); X = x;").
>
> :- pragma foreign_proc("C", set_x(X::in(ground)), [thread_safe],
> "x = X;").
>
> Possible attributes for a mutable variable are `thread_safe' and
> `untrailed'.
>
My preferred option here would be that it mutables be untrailed
by default.
>
>
> NEWS:
> Mention the new language feature.
>
> compiler/make_hlds_passes.m:
> Handle the new mutable/5 item.
>
> Pass 1 expands a mutable directives into the pred
> declaration items.
>
> Pass 2 expands a mutable directives into the initialise
> and foreign_decl declaration items.
>
> Pass 3 expands a mutable directives into the initialise
> declaration and the clauses for the preds.
>
> compiler/mercury_to_mercury.m:
> compiler/mercury_qual.m:
> compiler/modules.m:
> compiler/recompilation.check.m:
> compiler/recompilation.version.m:
> Cover the new mutable/5 item.
>
> compiler/prog_data.m:
> Add a new mutable/5 program item.
>
> compiler/prog_io.m:
> Parse `:- mutable' directives.
>
> compiler/prog_io_typeclass.m:
> compiler/prog_io_util.m:
> Move list_term_to_term_list from prog_io_typeclass to prog_io_util
> (it's a generally useful predicate).
>
> doc/reference_manual.texi:
> Document the new `:- mutable' directive.
>
> tests/hard_coded/Mmakefile:
> tests/hard_coded/mutable_decl.m:
> tests/hard_coded/mutable_decl.exp:
> Added a test case.
>
> Index: NEWS
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/NEWS,v
> retrieving revision 1.382
> diff -u -r1.382 NEWS
> --- NEWS 29 Aug 2005 03:22:16 -0000 1.382
> +++ NEWS 1 Sep 2005 03:49:50 -0000
> @@ -8,6 +8,9 @@
> * We have added support for optional module initialisation. See the
> "Optional module initialisation" section of the Mercury Language Refence
> Manual for details.
> +* We have added support for impure module-local mutable variables.
> + See the "Module-local mutable variables" section of the Mercury Language
> + Refence Manual for details.
>
> Changes to the Mercury standard library:
> * We have added an `injection' module, for reversible maps that are injective.
> Index: compiler/make_hlds_passes.m
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/compiler/make_hlds_passes.m,v
> retrieving revision 1.4
> diff -u -r1.4 make_hlds_passes.m
> --- compiler/make_hlds_passes.m 29 Aug 2005 03:22:20 -0000 1.4
> +++ compiler/make_hlds_passes.m 31 Aug 2005 07:24:29 -0000
> @@ -432,6 +432,36 @@
> add_item_decl_pass_1(Item, _, !Status, !ModuleInfo, no, !IO) :-
> % We add initialise declarations on the second pass.
> Item = initialise(_).
> +add_item_decl_pass_1(Item, Context, !Status, !ModuleInfo, no, !IO) :-
> + % We add the initialise decl and the foreign_decl on the second pass and
> + % the foreign_code clauses on the third pass.
> + Item = mutable(Name, Type, _InitValue, Inst, _Attrs),
> + module_info_name(!.ModuleInfo, ModuleName),
> + VarSet = varset__init,
> + InstVarSet = varset__init,
> + ExistQVars = [],
> + Constraints = constraints([], []),
> + IOType = term__functor(term__atom("."), [
> + term__functor(term__atom("io"), [], Context),
> + term__functor(term__atom("state"), [], Context)], Context),
> + GetPredDecl = pred_or_func(VarSet, InstVarSet, ExistQVars, predicate,
> + mutable_get_pred_sym_name(ModuleName, Name),
> + [type_and_mode(Type, out_mode(Inst))],
> + no /* with_type */, no /* with_inst */, yes(det),
> + true /* condition */, (semipure), Constraints),
> + add_item_decl_pass_1(GetPredDecl, Context, !Status, !ModuleInfo, _, !IO),
> + SetPredDecl = pred_or_func(VarSet, InstVarSet, ExistQVars, predicate,
> + mutable_set_pred_sym_name(ModuleName, Name),
> + [type_and_mode(Type, in_mode(Inst))],
> + no /* with_type */, no /* with_inst */, yes(det),
> + true /* condition */, (impure), Constraints),
> + add_item_decl_pass_1(SetPredDecl, Context, !Status, !ModuleInfo, _, !IO),
> + InitPredDecl = pred_or_func(VarSet, InstVarSet, ExistQVars, predicate,
> + mutable_init_pred_sym_name(ModuleName, Name),
> + [type_and_mode(IOType, di_mode), type_and_mode(IOType, uo_mode)],
> + no /* with_type */, no /* with_inst */, yes(det),
> + true /* condition */, (pure), Constraints),
> + add_item_decl_pass_1(InitPredDecl, Context, !Status, !ModuleInfo, _, !IO).
>
> %-----------------------------------------------------------------------------%
>
> @@ -518,6 +548,36 @@
> PragmaExportItem =
> pragma(export(SymName, predicate, [di_mode, uo_mode], CName)),
> add_item_decl_pass_2(PragmaExportItem, Context, !Status, !ModuleInfo, !IO).
> +add_item_decl_pass_2(Item, Context, !Status, !ModuleInfo, !IO) :-
> + Item = mutable(Name, _Type, _InitTerm, _Inst, _MutAttrs),
> + module_info_name(!.ModuleInfo, ModuleName),
> + InitDecl = initialise(mutable_init_pred_sym_name(ModuleName, Name)),
> + add_item_decl_pass_2(InitDecl, Context, !Status, !ModuleInfo, !IO),
> + ForeignDecl = pragma(foreign_decl(c, foreign_decl_is_local,
> + "MR_Word " ++ mutable_c_var_name(Name) ++ ";")),
> + add_item_decl_pass_2(ForeignDecl, Context, !Status, !ModuleInfo, !IO).
> +
> +
> + % XXX We should probably mangle Name for safety...
> + %
> +:- func mutable_get_pred_sym_name(sym_name, string) = sym_name.
> +
> +mutable_get_pred_sym_name(ModuleName, Name) =
> + qualified(ModuleName, "get_" ++ Name).
> +
> +:- func mutable_set_pred_sym_name(sym_name, string) = sym_name.
> +
> +mutable_set_pred_sym_name(ModuleName, Name) =
> + qualified(ModuleName, "set_" ++ Name).
> +
> +:- func mutable_init_pred_sym_name(sym_name, string) = sym_name.
> +
> +mutable_init_pred_sym_name(ModuleName, Name) =
> + qualified(ModuleName, "initialise_mutable_" ++ Name).
> +
> +:- func mutable_c_var_name(string) = string.
> +
> +mutable_c_var_name(Name) = "mutable_variable_" ++ Name.
>
> %-----------------------------------------------------------------------------%
>
> @@ -737,6 +797,50 @@
> "not have a corresponding pred declaration.")], !IO),
> module_info_incr_errors(!ModuleInfo)
> ).
> +add_item_clause(Item, !Status, Context, !ModuleInfo, !QualInfo, !IO) :-
> + Item = mutable(Name, _Type, InitTerm, Inst, MutAttrs),
> + module_info_name(!.ModuleInfo, ModuleName),
> + varset__new_named_var(varset__init, "X", X, VarSet),
> + DefaultAttrs = default_attributes(c),
> + (
> + list__member(thread_safe, MutAttrs)
> + ->
> + set_thread_safe(thread_safe, DefaultAttrs, Attrs)
> + ;
> + Attrs = DefaultAttrs
> + ),
> + add_item_clause(initialise(mutable_init_pred_sym_name(ModuleName, Name)),
> + !Status, Context, !ModuleInfo, !QualInfo, !IO),
> + InitClause = clause(VarSet, predicate,
> + mutable_init_pred_sym_name(ModuleName, Name),
> + [term__variable(X), term__variable(X)],
> + promise_purity(dont_make_implicit_promises, (pure),
> + call(mutable_set_pred_sym_name(ModuleName, Name),
> + [InitTerm], (impure)) - Context
> + ) - Context),
> + add_item_clause(InitClause, !Status, Context, !ModuleInfo, !QualInfo, !IO),
> + set_purity((semipure), Attrs, GetAttrs),
> + GetClause = pragma(foreign_proc(GetAttrs,
> + mutable_get_pred_sym_name(ModuleName, Name), predicate,
> + [pragma_var(X, "X", out_mode(Inst))], VarSet,
> + ordinary("X = " ++ mutable_c_var_name(Name) ++ ";",
> + yes(Context)))),
> + add_item_clause(GetClause, !Status, Context, !ModuleInfo, !QualInfo, !IO),
> + (
> + list__member(untrailed, MutAttrs)
> + ->
> + TrailCode = ""
> + ;
> + TrailCode =
> + "MR_trail_current_value(&" ++ mutable_c_var_name(Name) ++ ");\n"
> + ),
> + SetClause = pragma(foreign_proc(Attrs,
> + mutable_set_pred_sym_name(ModuleName, Name), predicate,
> + [pragma_var(X, "X", in_mode(Inst))], VarSet,
> + ordinary(TrailCode ++ mutable_c_var_name(Name) ++ " = X;",
> + yes(Context)))),
> + add_item_clause(SetClause, !Status, Context, !ModuleInfo, !QualInfo, !IO).
> +
>
> % If a module_defn updates the import_status, return the new status
> % and whether uses of the following items must be module qualified,
> Index: compiler/prog_io.m
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/compiler/prog_io.m,v
> retrieving revision 1.244
> diff -u -r1.244 prog_io.m
> --- compiler/prog_io.m 29 Aug 2005 03:22:25 -0000 1.244
> +++ compiler/prog_io.m 31 Aug 2005 06:06:10 -0000
> @@ -1422,10 +1422,14 @@
> )
> ).
>
> -process_decl(ModuleName, VarSet, "initialise", Args, Attributes, Result):-
> +process_decl(ModuleName, VarSet, "initialise", Args, Attributes, Result) :-
> parse_initialise_decl(ModuleName, VarSet, Args, Result0),
> check_no_attributes(Result0, Attributes, Result).
>
> +process_decl(ModuleName, VarSet, "mutable", Args, Attributes, Result) :-
> + parse_mutable_decl(ModuleName, VarSet, Args, Result0),
> + check_no_attributes(Result0, Attributes, Result).
> +
> :- pred parse_decl_attribute(string::in, list(term)::in, decl_attribute::out,
> term::out) is semidet.
>
> @@ -1796,6 +1800,162 @@
> "an arity 2 predicate", Term)
> )
> )
> + ).
> +
> +%-----------------------------------------------------------------------------%
> +
> +% Mutable declaration yntax:
> +%
s/yntax/syntax/
> +% :- mutable(name, type, value, inst, [untrailed, promise_thread_safe]).
> +% (The list of attributes at the end is optional.)
> +%
> +% E.g.:
> +% :- mutable(counter, int, 0, ground, [promise_thread_safe]).
> +%
> +% This is eventually converted into the following:
> +%
Delete "eventually".
> +% :- semipure pred get_counter(int::out(ground)) is det.
> +% :- pragma foreign_proc("C",
> +% get_counter(X::out(ground)),
> +% [promise_semipure, will_not_call_mercury, thread_safe],
> +% "X = mutable_counter;").
> +%
> +% :- impure pred set_counter(int::in(ground)) is det.
> +% :- pragma foreign_proc("C",
> +% set_counter(X::in(ground)),
> +% [will_not_call_mercury, thread_safe],
> +% "MR_trail_current_value(&mutable_counter);
> +% mutable_counter = X;").
> +%
> +% :- pragma foreign_decl("C", "MR_Word mutable_counter;").
> +%
> +% :- import_module io.
> +% :- initialise initialise_counter.
> +% :- impure pred initialise_mutable_counter(io::di, io::uo) is det.
> +% initialise_mutable_counter(!IO) :-
> +% impure set_counter(0).
Perhaps we should just support impure initialisation predicates.
(Is the io module import implicity or do we rely on the user for it.)
> +%
> +% The `thread_safe' attributes are omitted if it is not listed in
> +% the mutable declaration attributes. Similarly, MR_trail_current_value()
> +% does not appear if `promise_thread_safe' appears in the mutable
> +% declaration attributes.
> +
Shouldn't that be "if `untrailed' appears in the mutable declaration..."
> +:- pred parse_mutable_decl(module_name::in, varset::in, list(term)::in,
> + maybe1(item)::out) is semidet.
> +
> +parse_mutable_decl(_ModuleName, _VarSet, Terms, Result) :-
> + Terms = [NameTerm, TypeTerm, ValueTerm, InstTerm | OptMutAttrsTerm],
> + parse_mutable_name(NameTerm, NameResult),
> + parse_mutable_type(TypeTerm, TypeResult),
> + term__coerce(ValueTerm, Value),
> + parse_mutable_inst(InstTerm, InstResult),
> + (
> + OptMutAttrsTerm = [],
> + MutAttrsResult = ok([])
> + ;
> + OptMutAttrsTerm = [MutAttrsTerm],
> + parse_mutable_attrs(MutAttrsTerm, MutAttrsResult)
> + ),
> + (
> + NameResult = ok(Name),
> + TypeResult = ok(Type),
> + InstResult = ok(Inst),
> + MutAttrsResult = ok(MutAttrs)
> + ->
> + Result = ok(mutable(Name, Type, Value, Inst, MutAttrs))
> + ;
> + NameResult = error(Msg, Term)
> + ->
> + Result = error(Msg, Term)
> + ;
> + TypeResult = error(Msg, Term)
> + ->
> + Result = error(Msg, Term)
> + ;
> + InstResult = error(Msg, Term)
> + ->
> + Result = error(Msg, Term)
> + ;
> + MutAttrsResult = error(Msg, Term)
> + ->
> + Result = error(Msg, Term)
> + ;
> + error("prog_io.parse_mutable_decl: shouldn't be here!")
> + ).
> +
> +
> +:- pred parse_mutable_name(term::in, maybe1(string)::out) is det.
> +
> +parse_mutable_name(NameTerm, NameResult) :-
> + (
> + NameTerm = term__functor(atom(Name), [], _)
> + ->
> + NameResult = ok(Name)
> + ;
> + NameResult = error("invalid mutable name", NameTerm)
> + ).
> +
> +
> +:- pred parse_mutable_type(term::in, maybe1(type)::out) is det.
> +
> +parse_mutable_type(TypeTerm, TypeResult) :-
> + (
> + term__contains_var(TypeTerm, _)
> + ->
> + TypeResult = error("the type in a mutable declaration " ++
> + "cannot contain variables", TypeTerm)
> + ;
> + parse_type(TypeTerm, TypeResult)
> + ).
> +
> +
> +:- pred parse_mutable_inst(term::in, maybe1(inst)::out) is det.
> +
> +parse_mutable_inst(InstTerm, InstResult) :-
> + (
> + term__contains_var(InstTerm, _)
> + ->
> + InstResult = error("the inst in a mutable declaration " ++
> + "cannot contain variables", InstTerm)
> + ;
> + convert_inst(no_allow_constrained_inst_var, InstTerm, Inst)
> + ->
> + InstResult = ok(Inst)
> + ;
> + InstResult = error("invalid inst in mutable declaration",
> + InstTerm)
> + ).
> +
> +
> +:- pred parse_mutable_attrs(term::in, maybe1(list(mutable_attr))::out) is det.
> +
> +parse_mutable_attrs(MutAttrsTerm, MutAttrsResult) :-
> + (
> + list_term_to_term_list(MutAttrsTerm, MutAttrTerms)
> + ->
> + map_parser(parse_mutable_attr, MutAttrTerms, MutAttrsResult)
> + ;
> + MutAttrsResult = error("malformed attribute list in " ++
> + "mutable declaration", MutAttrsTerm)
> + ).
> +
> +:- pred parse_mutable_attr(term::in, maybe1(mutable_attr)::out) is det.
> +
> +parse_mutable_attr(MutAttrTerm, MutAttrResult) :-
> + (
> + MutAttrTerm = term__functor(term__atom(String), [], _),
> + (
> + String = "untrailed",
> + MutAttr = untrailed
> + ;
> + String = "thread_safe",
> + MutAttr = thread_safe
> + )
> + ->
> + MutAttrResult = ok(MutAttr)
> + ;
> + MutAttrResult = error("unrecognised attribute in mutable " ++
> + "declaration", MutAttrTerm)
> ).
>
...
> Index: doc/reference_manual.texi
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/doc/reference_manual.texi,v
> retrieving revision 1.322
> diff -u -r1.322 reference_manual.texi
> --- doc/reference_manual.texi 29 Aug 2005 03:22:28 -0000 1.322
> +++ doc/reference_manual.texi 1 Sep 2005 03:48:07 -0000
> @@ -558,6 +558,7 @@
> :- pragma
> :- promise
> :- initialise
> +:- mutable
> :- module
> :- interface
> :- implementation
> @@ -4237,6 +4238,7 @@
> * An example module::
> * Sub-modules::
> * Optional module initialisation::
> +* Module-local mutable variables::
> @end menu
>
> @node The module system
> @@ -4582,6 +4584,44 @@
> order in which they are specified, although no order may be assumed between
> different modules or submodules.
>
> + at node Module-local mutable variables
> + at section Module-local mutable variables
> +
> +Certain special cases require a module to have one or more mutable (i.e.
> +destructively updatable) variables, for example to hold the constraint
> +store for a solver type.
> +
> +A mutable variable can be declared in the implementation section of
> +a module using the @samp{mutable} directive:
> +
You should mention that it is an error for a mutuable variable to
appear in the interface of a module.
> + at example
> +:- mutable(varname, vartype, initial_value, varinst, [attribute, ...]).
> + at end example
> +
> +This has the effect of constructing a new mutable variable with access
> +predicates with the following signatures:
> +
> + at example
> +:- semipure get_varname(vartype::out(varinst)) is det.
> +:- impure set_varname(vartype::in(varinst)) is det.
> + at end example
> +
> +The initial value of @samp{varname} is @samp{initial_value}, which is set
> +before the program's @samp{main/2} predicate is executed. The type
> + at samp{vartype} is not allowed to contain any type variables and the inst
> + at samp{varinst} is not allowed to contain any inst variables or have any type
> +class constraints; @samp{initial_value} can be any Mercury expression with
> +type @samp{vartype} and inst @samp{varinst}.
> +
> +The possible @samp{attributes} are @samp{thread_safe}, meaning that
> +access to the mutable need not be protected in parallel grades, and
> + at samp{untrailed}, meaning that the effects of calls to
> + at samp{set_varname/1} should not be undone on backtracking.
> +
> +C code in the same module can access the mutable variable using the name
> + at samp{mutable_variable_varname}, which is a global variable with type
> + at samp{MR_Word}.
s/with type/of type/
More to come (I have to reboot earth)
Julien.
--------------------------------------------------------------------------
mercury-reviews mailing list
post: mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------
More information about the reviews
mailing list