%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et wm=0 tw=0
%---------------------------------------------------------------------------%
%
% rotd-2006-06-30 and before incorrectly hoisted the method call
% new_literal/1 in the function literal_list/1. The problem was that
% loop invariant hoisting was considering calls (and generic_calls) with
% modes that contained an inst `any' component as candidates for hoisting.
%
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%

:- module any_call_hoist_bug.
:- interface.

:- import_module io.

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

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

:- implementation.

:- import_module int.
:- import_module list.
:- import_module pair.

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

:- typeclass foo(L) where [
    pred new_literal(L::oa) is det
].

:- instance foo(literal) where [
    new_literal(A) :- make_new_literal(A)
].

:- type lit_list(L) == list(pair(int, L)).

main(!IO) :-
    LitList0 = literal_list(5) : lit_list(literal),
    LitList  = cast_to_ground(LitList0),
    io.write(LitList, !IO).

:- func literal_list(int::in) = (lit_list(L)::oa) is det <= foo(L).

literal_list(N) = LitList :-
    ( if N =< 0 then
        LitList = []
    else
        new_literal(A),   % XXX This is incorrectly hoisted.
        LitList0 = literal_list(N - 1),
        LitList  = [ N - A | LitList0 ]
    ).

:- mutable(literal_supply, int, 561, ground, [untrailed]).

:- solver type literal
    where   representation is int.

:- pred make_new_literal(literal::oa) is det.

make_new_literal(NewLiteral) :-
    promise_pure (
        semipure get_literal_supply(NextLiteral),
        impure   set_literal_supply(NextLiteral + 1),
        impure   NewLiteral = 'representation to any literal/0'(NextLiteral)
    ).

:- func cast_to_ground(T::ia) = (T::out) is det.
:- pragma foreign_proc("C",
    cast_to_ground(A::ia) = (B::out),
    [promise_pure, will_not_call_mercury],
"
    B = A;
").

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