%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2006-2007, 2010, 2012 The University of Melbourne.
% Copyright (C) 2015, 2018-2019, 2024 The Mercury team.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: hlds_args.m.
% Main authors: juliensf.
%
% This module defines the part of the HLDS that deals with procedure and call
% site arguments. (See comments at the head of polymorphism.m for further
% details.)
%
%-----------------------------------------------------------------------------%

:- module hlds.hlds_args.
:- interface.

:- import_module mdbcomp.
:- import_module mdbcomp.prim_data.

:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module set.

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

    % This type represents the arguments to a predicate symbol, or
    % the arguments and result of a function symbol. The arguments may be
    % variables, types, modes, etc, depending on the context.
    %
    % Rather than keep all arguments in a single list, we retain information
    % about the origin of each argument (such as whether it was introduced
    % by polymorphism.m to hold a type_info). This simplifies the processing
    % in polymorphism.m, and also abstracts away the specific calling
    % convention that we use.
    %
:- type proc_arg_vector(T).

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

    % Create a new proc_arg_vector from the given list.
    % If the first argument is `function' then the last element in the
    % list is used to set the return value field of the proc_arg_vector.
    %
:- func proc_arg_vector_init(pred_or_func, list(T)) = proc_arg_vector(T).

%-----------------------------------------------------------------------------%
%
% Access predicates for the proc_arg_vector structure
%

:- func proc_arg_vector_get_instance_type_infos(proc_arg_vector(T))
    = list(T).
:- func proc_arg_vector_get_instance_typeclass_infos(proc_arg_vector(T))
    = list(T).
:- func proc_arg_vector_get_univ_type_infos(proc_arg_vector(T)) = list(T).
:- func proc_arg_vector_get_exist_type_infos(proc_arg_vector(T)) = list(T).
:- func proc_arg_vector_get_univ_typeclass_infos(proc_arg_vector(T)) = list(T).
:- func proc_arg_vector_get_exist_typeclass_infos(proc_arg_vector(T))
    = list(T).
:- func proc_arg_vector_get_user_args(proc_arg_vector(T)) = list(T).
:- func proc_arg_vector_get_maybe_ret_value(proc_arg_vector(T)) = maybe(T).

:- pred proc_arg_vector_set_instance_type_infos(list(T)::in,
    proc_arg_vector(T)::in, proc_arg_vector(T)::out) is det.
:- pred proc_arg_vector_set_instance_typeclass_infos(list(T)::in,
    proc_arg_vector(T)::in, proc_arg_vector(T)::out) is det.
:- pred proc_arg_vector_set_univ_type_infos(list(T)::in,
    proc_arg_vector(T)::in, proc_arg_vector(T)::out) is det.
:- pred proc_arg_vector_set_exist_type_infos(list(T)::in,
    proc_arg_vector(T)::in, proc_arg_vector(T)::out) is det.
:- pred proc_arg_vector_set_univ_typeclass_infos(list(T)::in,
    proc_arg_vector(T)::in, proc_arg_vector(T)::out) is det.
:- pred proc_arg_vector_set_exist_typeclass_infos(list(T)::in,
    proc_arg_vector(T)::in, proc_arg_vector(T)::out) is det.
:- pred proc_arg_vector_set_user_args(list(T)::in,
    proc_arg_vector(T)::in, proc_arg_vector(T)::out) is det.
:- pred proc_arg_vector_set_maybe_ret_value(maybe(T)::in,
    proc_arg_vector(T)::in, proc_arg_vector(T)::out) is det.

%-----------------------------------------------------------------------------%
%
% Utility predicates that operate on proc_arg_vectors.
%

    % Return a list of the items in a arg_vector. The order of the list
    % corresponds to that required by the calling conventions.
    % See comments at the head of polymorphism.m for details.
    % If the arg_vector is for a function, then the last element in the list
    % will correspond to the function return value.
    %
:- func proc_arg_vector_to_list(proc_arg_vector(T)) = list(T).

    % Return the set of items in an arg_vector.
    %
:- func proc_arg_vector_to_set(proc_arg_vector(T)) = set(T).

    % Apply a renaming to an arg_vector.
    % Useful for renaming variables etc.
    %
:- pred apply_renaming_to_proc_arg_vector(map(T, T)::in,
    proc_arg_vector(T)::in, proc_arg_vector(T)::out) is det.

    % proc_arg_vector_partition_poly_args(Vec, PolyArgs, NonPolyArgs):
    %
    % Partition the argument vector into two lists depending on whether
    % something was introduced by the polymorphism transformation or not.
    %
:- pred proc_arg_vector_partition_poly_args(proc_arg_vector(T)::in,
    list(T)::out, list(T)::out) is det.

    % proc_arg_vector_member(Vector, V):
    %
    % Succeeds iff V is a member of the argument vector `Vector'.
    %
:- pred proc_arg_vector_member(proc_arg_vector(T)::in, T::in) is semidet.

    % Partition the given arg_vector into a list of arguments and
    % a function return value. Throws an exception if the arg_vector does
    % not correspond to a function.
    %
:- pred proc_arg_vector_to_func_args(proc_arg_vector(T)::in,
    list(T)::out, T::out) is det.

%-----------------------------------------------------------------------------%
%
% Higher-order operations on proc_arg_vectors.
%

%
% NOTE these higher-order operations all work in a similar fashion
% to their counterparts in library/list.m.

:- func proc_arg_vector_map(func(T) = U, proc_arg_vector(T)) =
    proc_arg_vector(U).

:- pred proc_arg_vector_map(pred(T, U)::in(pred(in, out) is det),
    proc_arg_vector(T)::in, proc_arg_vector(U)::out) is det.

:- pred proc_arg_vector_map_corresponding(
    pred(T, U, V)::in(pred(in, in, out) is det),
    proc_arg_vector(T)::in, proc_arg_vector(U)::in, proc_arg_vector(V)::out)
    is det.

:- pred proc_arg_vector_all_true(pred(T)::in(pred(in) is semidet),
    proc_arg_vector(T)::in) is semidet.

:- pred proc_arg_vector_map_corresponding_foldl2(
    pred(A, B, C, D, D, E, E)::in(pred(in, in, out, in, out, in, out) is det),
    proc_arg_vector(A)::in, proc_arg_vector(B)::in, proc_arg_vector(C)::out,
    D::in, D::out, E::in, E::out) is det.

:- pred proc_arg_vector_foldl3_corresponding(
    pred(A, B, C, C, D, D, E, E)
    ::in(pred(in, in, in, out, in, out, in, out) is det),
    proc_arg_vector(A)::in, proc_arg_vector(B)::in,
    C::in, C::out, D::in, D::out, E::in, E::out) is det.

:- pred proc_arg_vector_foldl2_corresponding3(
    pred(A, B, C, D, D, E, E)
    ::in(pred(in, in, in, in, out, in, out) is det),
    proc_arg_vector(A)::in, proc_arg_vector(B)::in, proc_arg_vector(C)::in,
    D::in, D::out, E::in, E::out) is det.

:- pred proc_arg_vector_foldl3_corresponding3(
    pred(A, B, C, D, D, E, E, F, F)
    ::in(pred(in, in, in, in, out, in, out, in, out) is det),
    proc_arg_vector(A)::in, proc_arg_vector(B)::in, proc_arg_vector(C)::in,
    D::in, D::out, E::in, E::out, F::in, F::out) is det.

:- pred proc_arg_vector_foldl4_corresponding3(
    pred(A, B, C, D, D, E, E, F, F, G, G)
    ::in(pred(in, in, in, in, out, in, out, in, out, in, out) is det),
    proc_arg_vector(A)::in, proc_arg_vector(B)::in, proc_arg_vector(C)::in,
    D::in, D::out, E::in, E::out, F::in, F::out, G::in, G::out) is det.

%-----------------------------------------------------------------------------%
%
% Stuff related to the polymorphism pass.
%

    % This type represents those arguments of a predicate or function
    % symbol that are introduced by the polymorphism transformation.
    % The arguments may be variables, types, modes, etc, depending on the
    % context.
    %
    % Values of this type are used to pass around intermediate values
    % during the polymorphism transformation.
    %
:- type poly_arg_vector(T).

:- func poly_arg_vector_init = poly_arg_vector(T).

:- pred poly_arg_vector_set_instance_type_infos(list(T)::in,
    poly_arg_vector(T)::in, poly_arg_vector(T)::out) is det.
:- pred poly_arg_vector_set_instance_typeclass_infos(list(T)::in,
    poly_arg_vector(T)::in, poly_arg_vector(T)::out) is det.
:- pred poly_arg_vector_set_univ_type_infos(list(T)::in,
    poly_arg_vector(T)::in, poly_arg_vector(T)::out) is det.
:- pred poly_arg_vector_set_exist_type_infos(list(T)::in,
    poly_arg_vector(T)::in, poly_arg_vector(T)::out) is det.
:- pred poly_arg_vector_set_univ_typeclass_infos(list(T)::in,
    poly_arg_vector(T)::in, poly_arg_vector(T)::out) is det.
:- pred poly_arg_vector_set_exist_typeclass_infos(list(T)::in,
    poly_arg_vector(T)::in, poly_arg_vector(T)::out) is det.

    % Convert a poly_arg_vector into a list.
    % XXX ARGVEC - this is only temporary until the proc_info structure use
    % proc_arg_vectors. We should then provide a predicate that merges a
    % poly_arg_vector with a proc_arg_vector.
    %
:- func poly_arg_vector_to_list(poly_arg_vector(T)) = list(T).

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

:- implementation.

:- import_module parse_tree.
:- import_module parse_tree.prog_type.
    % Required for apply_partial_map_to_list.
    % XXX That should really live in a different module.

:- import_module require.

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

    % The first six fields are set by the polymorphism pass.
    %
:- type proc_arg_vector(T)
    --->    proc_arg_vector(
                % Type_infos for the unconstrained type variables in an
                % instance declaration in the order which they first appear
                % in the instance arguments. For procedures that are not
                % class methods, this will always be the empty list.
                pav_instance_type_infos         :: list(T),

                % Typeclass_infos for the class constraints on an instance
                % declaration, in the order in which they appear in the
                % declaration. For procedures that are not class methods,
                % this will always be the empty list.
                pav_instance_typeclass_infos    :: list(T),

                % Type_infos for unconstrained universally quantified type
                % variables, in the order in which they first appear in the
                % argument types.
                pav_univ_type_infos             :: list(T),

                % Type_infos for unconstrained existentially quantified type
                % variables, in the order that the type variables first
                % appear in the argument types.
                pav_exist_type_infos            :: list(T),

                % Typeclass_infos for universally quantified constraints
                % in the order in which the constraints appear in the class
                % context.
                pav_univ_typeclass_infos        :: list(T),

                % Typeclass_infos for existentially quantified constraints
                % in the order in which the constraints appear in the class
                % context.
                pav_exist_typeclass_infos       :: list(T),

                % The original procedure arguments.
                % XXX Plus at the moment any arguments that may be
                % introduced by transformations performed by the compiler.
                % Eventually these should be separated out.
                pav_user_args                   :: list(T),

                % For predicates this field is no; for functions
                % it corresponds to the function's return value.
                pav_maybe_ret_value             :: maybe(T)
            ).

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

proc_arg_vector_init(PredOrFunc, Args0) = ArgVec :-
    (
        PredOrFunc = pf_predicate,
        Args = Args0,
        MaybeRetVal = no
    ;
        PredOrFunc = pf_function,
        list.det_split_last(Args0, Args, RetVal),
        MaybeRetVal = yes(RetVal)
    ),
    ArgVec = proc_arg_vector([], [], [], [], [], [], Args, MaybeRetVal).

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

proc_arg_vector_get_instance_type_infos(V)
    = V ^ pav_instance_type_infos.
proc_arg_vector_get_instance_typeclass_infos(V)
    = V ^ pav_instance_typeclass_infos.
proc_arg_vector_get_univ_type_infos(V)
    = V ^ pav_univ_type_infos.
proc_arg_vector_get_exist_type_infos(V)
    = V ^ pav_exist_type_infos.
proc_arg_vector_get_univ_typeclass_infos(V)
    = V ^ pav_univ_typeclass_infos.
proc_arg_vector_get_exist_typeclass_infos(V)
    = V ^ pav_exist_typeclass_infos.
proc_arg_vector_get_user_args(V)
    = V ^ pav_user_args.
proc_arg_vector_get_maybe_ret_value(V)
    = V ^ pav_maybe_ret_value.

proc_arg_vector_set_instance_type_infos(ITI, !V) :-
    !V ^ pav_instance_type_infos := ITI.
proc_arg_vector_set_instance_typeclass_infos(ITCI, !V) :-
    !V ^ pav_instance_typeclass_infos := ITCI.
proc_arg_vector_set_univ_type_infos(UTI, !V) :-
    !V ^ pav_univ_type_infos := UTI.
proc_arg_vector_set_exist_type_infos(ETI, !V) :-
    !V ^ pav_exist_type_infos := ETI.
proc_arg_vector_set_univ_typeclass_infos(UTCI, !V) :-
    !V ^ pav_univ_typeclass_infos := UTCI.
proc_arg_vector_set_exist_typeclass_infos(ETCI, !V) :-
    !V ^ pav_exist_typeclass_infos := ETCI.
proc_arg_vector_set_user_args(UA, !V) :-
    !V ^ pav_user_args := UA.
proc_arg_vector_set_maybe_ret_value(RV, !V) :-
    !V ^ pav_maybe_ret_value := RV.

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

proc_arg_vector_to_list(ArgVec) = List :-
    ArgVec = proc_arg_vector(InstanceTypeInfos, InstanceTypeClassInfos,
        UnivTypeInfos, ExistTypeInfos, UnivTypeClassInfos,
        ExistTypeClassInfos, OrigArgs, MaybeRetValue),
    (
        MaybeRetValue = yes(Value),
        RetValue = [Value]
    ;
        MaybeRetValue = no,
        RetValue = []
    ),
    list.condense([InstanceTypeInfos,
           InstanceTypeClassInfos,
           UnivTypeInfos,
           ExistTypeInfos,
           UnivTypeClassInfos,
           ExistTypeClassInfos,
           OrigArgs,
           RetValue], List).

proc_arg_vector_to_set(ArgVec) = Set :-
    List = proc_arg_vector_to_list(ArgVec),
    Set  = set.list_to_set(List).

apply_renaming_to_proc_arg_vector(Renaming, ArgVec0, ArgVec) :-
    ArgVec0 = proc_arg_vector(InstanceTypeInfos0, InstanceTypeClassInfos0,
        UnivTypeInfos0, ExistTypeInfos0, UnivTypeClassInfos0,
        ExistTypeClassInfos0, OrigArgs0, MaybeRetValue0),
    apply_partial_map_to_list(Renaming, InstanceTypeInfos0, InstanceTypeInfos),
    apply_partial_map_to_list(Renaming, InstanceTypeClassInfos0,
        InstanceTypeClassInfos),
    apply_partial_map_to_list(Renaming, UnivTypeInfos0, UnivTypeInfos),
    apply_partial_map_to_list(Renaming, ExistTypeInfos0, ExistTypeInfos),
    apply_partial_map_to_list(Renaming, UnivTypeClassInfos0,
        UnivTypeClassInfos),
    apply_partial_map_to_list(Renaming, ExistTypeClassInfos0,
        ExistTypeClassInfos),
    apply_partial_map_to_list(Renaming, OrigArgs0, OrigArgs),
    (
        MaybeRetValue0 = yes(Value0),
        ( if map.search(Renaming, Value0, Value) then
            MaybeRetValue = yes(Value)
        else
            MaybeRetValue = yes(Value0)
        )
    ;
        MaybeRetValue0 = no,
        MaybeRetValue  = no
    ),
    ArgVec = proc_arg_vector(InstanceTypeInfos, InstanceTypeClassInfos,
        UnivTypeInfos, ExistTypeInfos, UnivTypeClassInfos,
        ExistTypeClassInfos, OrigArgs, MaybeRetValue).

proc_arg_vector_partition_poly_args(ArgVec, PolyArgs, NonPolyArgs) :-
    ArgVec = proc_arg_vector(InstanceTypeInfos, InstanceTypeClassInfos,
        UnivTypeInfos, ExistTypeInfos, UnivTypeClassInfos,
        ExistTypeClassInfos, OrigArgs, MaybeRetValue),
    list.condense([InstanceTypeInfos, InstanceTypeClassInfos,
        UnivTypeInfos, ExistTypeInfos, UnivTypeClassInfos,
        ExistTypeClassInfos], PolyArgs),
    (
        MaybeRetValue = yes(RetValue),
        NonPolyArgs = OrigArgs ++ [RetValue]
    ;
        MaybeRetValue = no,
        NonPolyArgs = OrigArgs
    ).

proc_arg_vector_member(ArgVec, Var) :-
    ArgVec = proc_arg_vector(InstanceTypeInfos, InstanceTypeClassInfos,
        UnivTypeInfos, ExistTypeInfos, UnivTypeClassInfos,
        ExistTypeClassInfos, OrigArgs, MaybeRetValue),
    ( list.member(Var, OrigArgs)
    ; MaybeRetValue = yes(Var)
    ; list.member(Var, InstanceTypeInfos)
    ; list.member(Var, InstanceTypeClassInfos)
    ; list.member(Var, UnivTypeInfos)
    ; list.member(Var, ExistTypeInfos)
    ; list.member(Var, UnivTypeClassInfos)
    ; list.member(Var, ExistTypeClassInfos)
    ).

proc_arg_vector_to_func_args(Vector, FuncArgs, FuncRetVal) :-
    Vector = proc_arg_vector(InstanceTypeInfos, InstanceTypeClassInfos,
        UnivTypeInfos, ExistTypeInfos, UnivTypeClassInfos,
        ExistTypeClassInfos, OrigArgs, MaybeRetValue),
    FuncArgs = list.condense([InstanceTypeInfos, InstanceTypeClassInfos,
        UnivTypeInfos, ExistTypeInfos, UnivTypeClassInfos,
        ExistTypeClassInfos, OrigArgs]),
    (
        MaybeRetValue = yes(FuncRetVal)
    ;
        MaybeRetValue = no,
        unexpected($pred, "predicate")
    ).

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

proc_arg_vector_map(Func, V0) = V :-
    V0 = proc_arg_vector(ITI0, ITCI0, UTI0, ETI0, UTCI0, ETCI0, Args0,
        MaybeRetVal0),
    ITI  = list.map(Func, ITI0),
    ITCI = list.map(Func, ITCI0),
    UTI  = list.map(Func, UTI0),
    ETI  = list.map(Func, ETI0),
    UTCI = list.map(Func, UTCI0),
    ETCI = list.map(Func, ETCI0),
    Args = list.map(Func, Args0),
    (
        MaybeRetVal0 = yes(RetVal0),
        RetVal = Func(RetVal0),
        MaybeRetVal = yes(RetVal)
    ;
        MaybeRetVal0 = no,
        MaybeRetVal  = no
    ),
    V = proc_arg_vector(ITI, ITCI, UTI, ETI, UTCI, ETCI, Args,
        MaybeRetVal).

proc_arg_vector_map(Pred, V0, V) :-
    V0 = proc_arg_vector(ITI0, ITCI0, UTI0, ETI0, UTCI0, ETCI0, Args0,
        MaybeRetVal0),
    list.map(Pred, ITI0,  ITI),
    list.map(Pred, ITCI0, ITCI),
    list.map(Pred, UTI0,  UTI),
    list.map(Pred, ETI0,  ETI),
    list.map(Pred, UTCI0, UTCI),
    list.map(Pred, ETCI0, ETCI),
    list.map(Pred, Args0, Args),
    (
        MaybeRetVal0 = yes(RetVal0),
        Pred(RetVal0, RetVal),
        MaybeRetVal = yes(RetVal)
    ;
        MaybeRetVal0 = no,
        MaybeRetVal  = no
    ),
    V = proc_arg_vector(ITI, ITCI, UTI, ETI, UTCI, ETCI, Args,
        MaybeRetVal).

proc_arg_vector_map_corresponding(P, A, B, C) :-
    A = proc_arg_vector(ITIA, ITCIA, UTIA, ETIA, UTCIA, ETCIA, ArgsA,
        MaybeRetValA),
    B = proc_arg_vector(ITIB, ITCIB, UTIB, ETIB, UTCIB, ETCIB, ArgsB,
        MaybeRetValB),
    list.map_corresponding(P, ITIA, ITIB, ITIC),
    list.map_corresponding(P, ITCIA, ITCIB, ITCIC),
    list.map_corresponding(P, UTIA, UTIB, UTIC),
    list.map_corresponding(P, ETIA, ETIB, ETIC),
    list.map_corresponding(P, UTCIA, UTCIB, UTCIC),
    list.map_corresponding(P, ETCIA, ETCIB, ETCIC),
    list.map_corresponding(P, ArgsA, ArgsB, ArgsC),
    (
        MaybeRetValA = yes(RetValA),
        MaybeRetValB = yes(RetValB),
        P(RetValA, RetValB, RetValC),
        MaybeRetValC = yes(RetValC)
    ;
        MaybeRetValA = yes(_),
        MaybeRetValB = no,
        unexpected($pred, "mismatched proc_arg_vectors")
    ;
        MaybeRetValA = no,
        MaybeRetValB = yes(_),
        unexpected($pred, "mismatched proc_arg_vectors")
    ;
        MaybeRetValA = no,
        MaybeRetValB = no,
        MaybeRetValC = no
    ),
    C = proc_arg_vector(ITIC, ITCIC, UTIC, ETIC, UTCIC, ETCIC, ArgsC,
        MaybeRetValC).

proc_arg_vector_all_true(P, V) :-
    V = proc_arg_vector(ITI, ITCI, UTI, ETI, UTCI, ETCI, Args, MaybeRetVal),
    list.all_true(P, ITI),
    list.all_true(P, ITCI),
    list.all_true(P, UTI),
    list.all_true(P, ETI),
    list.all_true(P, UTCI),
    list.all_true(P, ETCI),
    list.all_true(P, Args),
    (
        MaybeRetVal = yes(RetVal),
        P(RetVal)
    ;
        MaybeRetVal = no
    ).

proc_arg_vector_map_corresponding_foldl2(P, A, B, C, !Acc1, !Acc2) :-
    A = proc_arg_vector(ITIA, ITCIA, UTIA, ETIA, UTCIA, ETCIA, ArgsA,
        MaybeRetValA),
    B = proc_arg_vector(ITIB, ITCIB, UTIB, ETIB, UTCIB, ETCIB, ArgsB,
        MaybeRetValB),
    list.map_corresponding_foldl2(P, ITIA,  ITIB,  ITIC,  !Acc1, !Acc2),
    list.map_corresponding_foldl2(P, ITCIA, ITCIB, ITCIC, !Acc1, !Acc2),
    list.map_corresponding_foldl2(P, UTIA,  UTIB,  UTIC,  !Acc1, !Acc2),
    list.map_corresponding_foldl2(P, ETIA,  ETIB,  ETIC,  !Acc1, !Acc2),
    list.map_corresponding_foldl2(P, UTCIA, UTCIB, UTCIC, !Acc1, !Acc2),
    list.map_corresponding_foldl2(P, ETCIA, ETCIB, ETCIC, !Acc1, !Acc2),
    list.map_corresponding_foldl2(P, ArgsA, ArgsB, ArgsC, !Acc1, !Acc2),
    (
        MaybeRetValA = yes(RetValA),
        MaybeRetValB = yes(RetValB),
        P(RetValA, RetValB, RetValC, !Acc1, !Acc2),
        MaybeRetValC = yes(RetValC)
    ;
        MaybeRetValA = yes(_),
        MaybeRetValB = no,
        unexpected($pred, "mismatched proc_arg_vectors")
    ;
        MaybeRetValA = no,
        MaybeRetValB = yes(_),
        unexpected($pred, "mismatched proc_arg_vectors")
    ;
        MaybeRetValA = no,
        MaybeRetValB = no,
        MaybeRetValC = no
    ),
    C = proc_arg_vector(ITIC, ITCIC, UTIC, ETIC, UTCIC, ETCIC, ArgsC,
        MaybeRetValC).

proc_arg_vector_foldl3_corresponding(P, A, B, !Acc1, !Acc2, !Acc3) :-
    A = proc_arg_vector(ITIA, ITCIA, UTIA, ETIA, UTCIA, ETCIA, ArgsA,
        MaybeRetValA),
    B = proc_arg_vector(ITIB, ITCIB, UTIB, ETIB, UTCIB, ETCIB, ArgsB,
        MaybeRetValB),
    list.foldl3_corresponding(P, ITIA,  ITIB,  !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding(P, ITCIA, ITCIB, !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding(P, UTIA,  UTIB,  !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding(P, ETIA,  ETIB,  !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding(P, UTCIA, UTCIB, !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding(P, ETCIA, ETCIB, !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding(P, ArgsA, ArgsB, !Acc1, !Acc2, !Acc3),
    (
        MaybeRetValA = yes(RetValA),
        MaybeRetValB = yes(RetValB),
        P(RetValA, RetValB, !Acc1, !Acc2, !Acc3)
    ;
        MaybeRetValA = yes(_),
        MaybeRetValB = no,
        unexpected($pred, "mismatched proc_arg_vectors")
    ;
        MaybeRetValA = no,
        MaybeRetValB = yes(_),
        unexpected($pred, "mismatched proc_arg_vectors")
    ;
        MaybeRetValA = no,
        MaybeRetValB = no
    ).

proc_arg_vector_foldl2_corresponding3(P, A, B, C, !Acc1, !Acc2) :-
    A = proc_arg_vector(ITIA, ITCIA, UTIA, ETIA, UTCIA, ETCIA, ArgsA,
        MaybeRetValA),
    B = proc_arg_vector(ITIB, ITCIB, UTIB, ETIB, UTCIB, ETCIB, ArgsB,
        MaybeRetValB),
    C = proc_arg_vector(ITIC, ITCIC, UTIC, ETIC, UTCIC, ETCIC, ArgsC,
        MaybeRetValC),
    list.foldl2_corresponding3(P, ITIA,  ITIB,  ITIC,  !Acc1, !Acc2),
    list.foldl2_corresponding3(P, ITCIA, ITCIB, ITCIC, !Acc1, !Acc2),
    list.foldl2_corresponding3(P, UTIA,  UTIB,  UTIC,  !Acc1, !Acc2),
    list.foldl2_corresponding3(P, ETIA,  ETIB,  ETIC,  !Acc1, !Acc2),
    list.foldl2_corresponding3(P, UTCIA, UTCIB, UTCIC, !Acc1, !Acc2),
    list.foldl2_corresponding3(P, ETCIA, ETCIB, ETCIC, !Acc1, !Acc2),
    list.foldl2_corresponding3(P, ArgsA, ArgsB, ArgsC, !Acc1, !Acc2),
    ( if
        MaybeRetValA = yes(RetValA),
        MaybeRetValB = yes(RetValB),
        MaybeRetValC = yes(RetValC)
    then
        P(RetValA, RetValB, RetValC, !Acc1, !Acc2)
    else if
        MaybeRetValA = no,
        MaybeRetValB = no,
        MaybeRetValC = no
    then
        true
    else
        unexpected($pred, "mismatched proc_arg_vectors")
    ).

proc_arg_vector_foldl3_corresponding3(P, A, B, C, !Acc1, !Acc2, !Acc3) :-
    A = proc_arg_vector(ITIA, ITCIA, UTIA, ETIA, UTCIA, ETCIA, ArgsA,
        MaybeRetValA),
    B = proc_arg_vector(ITIB, ITCIB, UTIB, ETIB, UTCIB, ETCIB, ArgsB,
        MaybeRetValB),
    C = proc_arg_vector(ITIC, ITCIC, UTIC, ETIC, UTCIC, ETCIC, ArgsC,
        MaybeRetValC),
    list.foldl3_corresponding3(P, ITIA,  ITIB,  ITIC,  !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding3(P, ITCIA, ITCIB, ITCIC, !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding3(P, UTIA,  UTIB,  UTIC,  !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding3(P, ETIA,  ETIB,  ETIC,  !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding3(P, UTCIA, UTCIB, UTCIC, !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding3(P, ETCIA, ETCIB, ETCIC, !Acc1, !Acc2, !Acc3),
    list.foldl3_corresponding3(P, ArgsA, ArgsB, ArgsC, !Acc1, !Acc2, !Acc3),
    ( if
        MaybeRetValA = yes(RetValA),
        MaybeRetValB = yes(RetValB),
        MaybeRetValC = yes(RetValC)
    then
        P(RetValA, RetValB, RetValC, !Acc1, !Acc2, !Acc3)
    else if
        MaybeRetValA = no,
        MaybeRetValB = no,
        MaybeRetValC = no
    then
        true
    else
        unexpected($pred, "mismatched proc_arg_vectors")
    ).

proc_arg_vector_foldl4_corresponding3(P, A, B, C, !Acc1, !Acc2, !Acc3,
        !Acc4) :-
    A = proc_arg_vector(ITIA, ITCIA, UTIA, ETIA, UTCIA, ETCIA, ArgsA,
        MaybeRetValA),
    B = proc_arg_vector(ITIB, ITCIB, UTIB, ETIB, UTCIB, ETCIB, ArgsB,
        MaybeRetValB),
    C = proc_arg_vector(ITIC, ITCIC, UTIC, ETIC, UTCIC, ETCIC, ArgsC,
        MaybeRetValC),
    list.foldl4_corresponding3(P, ITIA,  ITIB,  ITIC,  !Acc1, !Acc2, !Acc3,
        !Acc4),
    list.foldl4_corresponding3(P, ITCIA, ITCIB, ITCIC, !Acc1, !Acc2, !Acc3,
        !Acc4),
    list.foldl4_corresponding3(P, UTIA,  UTIB,  UTIC,  !Acc1, !Acc2, !Acc3,
        !Acc4),
    list.foldl4_corresponding3(P, ETIA,  ETIB,  ETIC,  !Acc1, !Acc2, !Acc3,
        !Acc4),
    list.foldl4_corresponding3(P, UTCIA, UTCIB, UTCIC, !Acc1, !Acc2, !Acc3,
        !Acc4),
    list.foldl4_corresponding3(P, ETCIA, ETCIB, ETCIC, !Acc1, !Acc2, !Acc3,
        !Acc4),
    list.foldl4_corresponding3(P, ArgsA, ArgsB, ArgsC, !Acc1, !Acc2, !Acc3,
        !Acc4),
    ( if
        MaybeRetValA = yes(RetValA),
        MaybeRetValB = yes(RetValB),
        MaybeRetValC = yes(RetValC)
    then
        P(RetValA, RetValB, RetValC, !Acc1, !Acc2, !Acc3, !Acc4)
    else if
        MaybeRetValA = no,
        MaybeRetValB = no,
        MaybeRetValC = no
    then
        true
    else
        unexpected($pred, "mismatched proc_arg_vectors")
    ).

%-----------------------------------------------------------------------------%
%
% Stuff related to the polymorphism transformation.
%

    % Internally we represent a poly_arg_vector as a proc_arg_vector.
    % This ensures that poly_arg_vectors obey the same calling convention
    % w.r.t introduced type_info and typeclass_info arguments that
    % proc_arg_vectors do. For the proc_arg_vectors that are used to represent
    % poly_arg_vectors, we insist that the last two fields are
    % the empty list and `no' respectively.
    %
:- type poly_arg_vector(T) == proc_arg_vector(T).

poly_arg_vector_init = proc_arg_vector_init(pf_predicate, []).

poly_arg_vector_set_instance_type_infos(ITI, !A) :-
    proc_arg_vector_set_instance_type_infos(ITI, !A).
poly_arg_vector_set_instance_typeclass_infos(ITCI, !A) :-
    proc_arg_vector_set_instance_typeclass_infos(ITCI, !A).
poly_arg_vector_set_univ_type_infos(UTI, !A) :-
    proc_arg_vector_set_univ_type_infos(UTI, !A).
poly_arg_vector_set_exist_type_infos(ETI, !A) :-
    proc_arg_vector_set_exist_type_infos(ETI, !A).
poly_arg_vector_set_univ_typeclass_infos(UTCI, !A) :-
    proc_arg_vector_set_univ_typeclass_infos(UTCI, !A).
poly_arg_vector_set_exist_typeclass_infos(ETCI, !A) :-
    proc_arg_vector_set_exist_typeclass_infos(ETCI, !A).

poly_arg_vector_to_list(V) =
    proc_arg_vector_to_list(V).

%-----------------------------------------------------------------------------%
:- end_module hlds.hlds_args.
%-----------------------------------------------------------------------------%
