%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1994-2007, 2010 The University of Melbourne.
% Copyright (C) 2013-2016, 2018 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% File: mutvar.m.
%
% This module is used in the implementation of all-solutions predicates
% (solutions.m) and stores (store.m).  This module is a private part of the
% Mercury implementation; user modules should never explicitly import this
% module.  The interface for this module does not get included in the Mercury
% library reference manual.
%
% XXX When we move to use submodules in the standard library, we should
% make this module a private submodule.
%
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%

:- module mutvar.
:- interface.

    % A non-backtrackably destructively modifiable reference type.
    %
:- type mutvar(T).

    % Create a new mutvar given a term for it to reference.
    %
:- impure pred new_mutvar(T, mutvar(T)).
:-        mode new_mutvar(in, out) is det.
:-        mode new_mutvar(di, uo) is det.

    % Create a new mutvar with undefined initial value.
    %
:- impure pred new_mutvar0(mutvar(T)).
:-        mode new_mutvar0(uo) is det.

    % Get the value currently referred to by a reference.
    %
:- impure pred get_mutvar(mutvar(T), T).
:-        mode get_mutvar(in, uo) is det.   % XXX this is a work-around
/*
XXX `ui' modes don't work yet
:-        mode get_mutvar(in, uo) is det.
:-        mode get_mutvar(ui, uo) is det.   % unsafe, but we use it safely
*/

    % Destructively modify a reference to refer to a new object.
    %
:- impure pred set_mutvar(mutvar(T), T).
:-        mode set_mutvar(in, in) is det.
/*
XXX `ui' modes don't work yet
:-        pred set_mutvar(ui, di) is det.
*/

    % Destructively clear a reference to avoid retaining the value.
    %
:- impure pred clear_mutvar(mutvar(T)).
:-        mode clear_mutvar(in) is det.
/*
XXX `ui' modes don't work yet
:-        pred clear_mutvar(ui) is det.
*/

%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%

:- implementation.

:- pragma inline(pred(new_mutvar/2)).
:- pragma inline(pred(new_mutvar0/1)).
:- pragma inline(pred(get_mutvar/2)).
:- pragma inline(pred(set_mutvar/2)).
:- pragma inline(pred(clear_mutvar/1)).

%---------------------------------------------------------------------------%

new_mutvar(X, Ref) :-
    impure new_mutvar0(Ref0),
    impure set_mutvar(Ref0, X),
    Ref = unsafe_promise_unique(Ref0).

%---------------------------------------------------------------------------%

:- pragma foreign_code("Java",
"
    public static class Mutvar implements java.io.Serializable {
        public Object object;

        public Mutvar() {
        }

        public Mutvar(Object o) {
            object = o;
        }
    }
").

    %  This type is a builtin type whose operations are implemented in C.
    %
:- type mutvar(T)
    --->    mutvar(private_builtin.ref(T)).

:- pragma foreign_type("C#", mutvar(T), "object[]").
:- pragma foreign_type("Java", mutvar(T), "mutvar.Mutvar").

%---------------------%

:- pragma foreign_proc("C",
    new_mutvar0(Ref::uo),
    [will_not_call_mercury, thread_safe],
"
    MR_offset_incr_hp_msg(Ref, MR_SIZE_SLOT_SIZE, MR_SIZE_SLOT_SIZE + 1,
        MR_ALLOC_ID, ""mutvar.mutvar/1"");
    MR_define_size_slot(0, Ref, 1);
").
:- pragma foreign_proc("C#",
    new_mutvar0(Ref::uo),
    [will_not_call_mercury, thread_safe],
"
    Ref = new object[1];
").
:- pragma foreign_proc("Java",
    new_mutvar0(Ref::uo),
    [will_not_call_mercury, thread_safe],
"
    Ref = new mutvar.Mutvar();
").

%---------------------%

:- pragma foreign_proc("C",
    get_mutvar(Ref::in, X::uo),
    [will_not_call_mercury, thread_safe],
"
    X = * (MR_Word *) Ref;
").
:- pragma foreign_proc("C#",
    get_mutvar(Ref::in, X::uo),
    [will_not_call_mercury, thread_safe],
"
    X = Ref[0];
").
:- pragma foreign_proc("Java",
    get_mutvar(Ref::in, X::uo),
    [will_not_call_mercury, thread_safe],
"
    X = Ref.object;
").

%---------------------%

:- pragma foreign_proc("C",
    set_mutvar(Ref::in, X::in),
    [will_not_call_mercury, thread_safe],
"
    *(MR_Word *) Ref = X;
").
:- pragma foreign_proc("C#",
    set_mutvar(Ref::in, X::in),
    [will_not_call_mercury, thread_safe],
"
    Ref[0] = X;
").
:- pragma foreign_proc("Java",
    set_mutvar(Ref::in, X::in),
    [will_not_call_mercury, thread_safe],
"
    Ref.object = X;
").

%---------------------%

:- pragma foreign_proc("C",
    clear_mutvar(Ref::in),
    [will_not_call_mercury, thread_safe],
"
    *(MR_Word *) Ref = 0;
").
:- pragma foreign_proc("C#",
    clear_mutvar(Ref::in),
    [will_not_call_mercury, thread_safe],
"
    Ref[0] = null;
").
:- pragma foreign_proc("Java",
    clear_mutvar(Ref::in),
    [will_not_call_mercury, thread_safe],
"
    Ref.object = null;
").

%---------------------------------------------------------------------------%
:- end_module mutvar.
%---------------------------------------------------------------------------%
