% Various checks that promise_pure goals are treated properly.

:- module promise_pure_test.

:- interface.
:- import_module io.

:- pred main(io::di, io::uo) is det.

:- implementation.
:- import_module int, list, string.

main(!IO) :-
	test1(5, List1),
	io__write(List1, !IO),
	io__nl(!IO),
	test2(21, List2),
	io__write(List2, !IO),
	io__nl(!IO).

:- impure pred set_x(int::in) is det.
:- pragma foreign_proc("C", set_x(X::in), [will_not_call_mercury], "x=X;" ).
:- pragma foreign_proc("C#", set_x(X::in), [will_not_call_mercury], "x=X;" ).
:- pragma foreign_proc("Erlang", set_x(X::in), [], "put(x, X)" ).
:- pragma no_inline(set_x/1).

:- semipure pred get_x(int::out) is det.
:- pragma promise_semipure(get_x/1).
:- pragma foreign_proc("C", get_x(X::out), [will_not_call_mercury], "X=x;").
:- pragma foreign_proc("C#", get_x(X::out), [will_not_call_mercury], "X=x;").
:- pragma foreign_proc("Erlang", get_x(X::out), [], "
    X0 = get(x),
    case X0 of
        undefined ->
            X = 0;
        _ ->
            X = X0
    end
").
:- pragma no_inline(get_x/1).

:- impure pred incr_x is det.
:- pragma foreign_proc("C", incr_x, [will_not_call_mercury], "++x;" ).
:- pragma foreign_proc("C#", incr_x, [will_not_call_mercury], "++x;" ).
:- pragma foreign_proc("Erlang", incr_x, [], "put(x, get(x) + 1)" ).
:- pragma no_inline(incr_x/0).

:- pragma foreign_decl("C", "extern int x;").
:- pragma foreign_code("C", "int x = 0;").
:- pragma foreign_code("C#", "static int x = 0;").

:- pred test1(int::in, list(int)::out) is det.

% Tempt compiler to optimize away duplicate semipure goals.
% The promise_pure is actually a lie, since the value of Y depends
% on the initial value of the global. This is why we call test1 only once.

test1(A, [X, Y, Z]) :-
	X = A * 2,
	promise_pure (
		semipure get_x(Y),
		impure set_x(X),
		semipure get_x(Z)
	).

:- pred test2(int::in, list(int)::out) is det.

% Tempt compiler to optimize away duplicate impure goals,
% or to compile away det goals with no outputs.

test2(A, [X, Y, Z]) :-
	X = A * 3,
	promise_pure (
		impure set_x(X),
		impure incr_x,
		impure incr_x,
		semipure get_x(Y)
	),
	Z = Y + 10.
