%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2009-2012 The University of Melbourne.
% Copyright (C) 2014-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_out_pred.m.
% Main authors: conway, fjh.
%
%---------------------------------------------------------------------------%

:- module hlds.hlds_out.hlds_out_pred.
:- interface.

:- import_module hlds.hlds_clauses.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_out.hlds_out_util.
:- import_module hlds.hlds_pred.
:- import_module libs.
:- import_module libs.indent.
:- import_module mdbcomp.
:- import_module mdbcomp.prim_data.
:- import_module parse_tree.
:- import_module parse_tree.parse_tree_out_info.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.var_db.
:- import_module parse_tree.var_table.

:- import_module io.
:- import_module list.
:- import_module string.
:- import_module string.builder.

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

    % write_pred(Info, Stream, Lang, ModuleInfo, PredId, PredInfo, !IO):
    %
:- pred write_pred(hlds_out_info::in, io.text_output_stream::in,
    output_lang::in, module_info::in, pred_id::in, pred_info::in,
    io::di, io::uo) is det.
:- pred format_pred(hlds_out_info::in, output_lang::in, module_info::in,
    pred_id::in, pred_info::in,
    string.builder.state::di, string.builder.state::uo) is det.

:- type write_which_modes
    --->    write_actual_modes
    ;       write_declared_modes.

    % write_clause(Info, Stream, Lang, ModuleInfo, PredId, PredOrFunc,
    %   VarTable, TypeQual, VarNamePrint, WriteWhichModes, Indent,
    %   HeadTerms, Clause, !IO).
    %
:- pred write_clause(hlds_out_info::in, io.text_output_stream::in,
    output_lang::in, module_info::in, pred_id::in, pred_or_func::in,
    var_name_source::in, type_qual::in,
    var_name_print::in, write_which_modes::in, indent::in,
    list(prog_term)::in, clause::in, io::di, io::uo) is det.
:- pred format_clause(hlds_out_info::in, output_lang::in, module_info::in,
    pred_id::in, pred_or_func::in, var_name_source::in, type_qual::in,
    var_name_print::in, write_which_modes::in, indent::in,
    list(prog_term)::in, clause::in,
    string.builder.state::di, string.builder.state::uo) is det.

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

:- pred write_table_arg_infos(io.text_output_stream::in, tvarset::in,
    table_arg_infos::in, io::di, io::uo) is det.
:- pred format_table_arg_infos(tvarset::in, table_arg_infos::in,
    string.builder.state::di, string.builder.state::uo) is det.

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

:- implementation.

:- import_module hlds.hlds_args.
:- import_module hlds.hlds_class.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_llds.
:- import_module hlds.hlds_out.hlds_out_goal.
:- import_module hlds.hlds_proc_util.
:- import_module hlds.hlds_rtti.
:- import_module hlds.pred_name.
:- import_module hlds.status.
:- import_module hlds.var_table_hlds.
:- import_module libs.globals.
:- import_module libs.trace_params.
:- import_module mdbcomp.goal_path.
:- import_module mdbcomp.program_representation.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.parse_tree_out_misc.
:- import_module parse_tree.parse_tree_out_pragma.
:- import_module parse_tree.parse_tree_out_pred_decl.
:- import_module parse_tree.parse_tree_out_term.
:- import_module parse_tree.parse_tree_out_type.
:- import_module parse_tree.parse_tree_to_term.
:- import_module parse_tree.prog_ctgc.
:- import_module parse_tree.prog_data_pragma.
:- import_module parse_tree.prog_type.
:- import_module parse_tree.prog_util.
:- import_module parse_tree.set_of_var.
:- import_module transform_hlds.
:- import_module transform_hlds.term_util.

:- import_module assoc_list.
:- import_module bool.
:- import_module int.
:- import_module map.
:- import_module maybe.
:- import_module one_or_more.
:- import_module pair.
:- import_module require.
:- import_module set.
:- import_module term.
:- import_module term_subst.
:- import_module uint.
:- import_module varset.

%---------------------------------------------------------------------------%
%
% Write out predicates.
%

write_pred(Info, Stream, Lang, ModuleInfo, PredId, PredInfo, !IO) :-
    State0 = string.builder.init,
    format_pred(Info, Lang, ModuleInfo, PredId, PredInfo, State0, State),
    Str = string.builder.to_string(State),
    io.write_string(Stream, Str, !IO).

format_pred(Info, Lang, ModuleInfo, PredId, PredInfo, !State) :-
    PredModuleName = pred_info_module(PredInfo),
    PredName = pred_info_name(PredInfo),
    PredOrFunc = pred_info_is_pred_or_func(PredInfo),
    pred_info_get_arg_types(PredInfo, ArgTypes),
    pred_info_get_exist_quant_tvars(PredInfo, ExistQVars),
    pred_info_get_typevarset(PredInfo, TVarSet),
    pred_info_get_clauses_info(PredInfo, ClausesInfo),
    pred_info_get_status(PredInfo, PredStatus),
    pred_info_get_markers(PredInfo, Markers),
    pred_info_get_class_context(PredInfo, ClassContext),
    pred_info_get_constraint_proof_map(PredInfo, ProofMap),
    pred_info_get_constraint_map(PredInfo, ConstraintMap),
    pred_info_get_purity(PredInfo, Purity),
    pred_info_get_external_type_params(PredInfo, ExternalTypeParams),
    pred_info_get_var_name_remap(PredInfo, VarNameRemap),
    DumpOptions = Info ^ hoi_dump_hlds_options,
    ( if string.contains_char(DumpOptions, 'v') then
        VarNamePrint = print_name_and_num
    else
        VarNamePrint = print_name_only
    ),
    pred_info_get_proc_table(PredInfo, ProcTable),
    map.to_assoc_list(ProcTable, ProcIdsInfos),
    find_filled_in_procs(ProcIdsInfos, FilledInProcIdsInfos),
    ( if string.contains_char(DumpOptions, 'C') then
        % Information about predicates is dumped if 'C' suboption is on.
        PredSymName = qualified(PredModuleName, PredName),
        (
            PredOrFunc = pf_predicate,
            MaybeDet = maybe.no,
            mercury_format_pred_type(TVarSet, VarNamePrint, ExistQVars,
                PredSymName, ArgTypes, MaybeDet, Purity, ClassContext,
                string.builder.handle, !State)
        ;
            PredOrFunc = pf_function,
            pred_args_to_func_args(ArgTypes, FuncArgTypes, FuncRetType),
            MaybeDet = maybe.no,
            mercury_format_func_type(TVarSet, VarNamePrint, ExistQVars,
                PredSymName, FuncArgTypes, FuncRetType, MaybeDet, Purity,
                ClassContext, string.builder.handle, !State)
        ),
        ClausesInfo = clauses_info(VarSet, ExplicitVarTypes,
            VarTable0, RttiVarMaps, _TVarSet, HeadVars, ClausesRep,
            _ItemNumbers, _HaveForeignClauses, _HadSyntaxErrors),
        ( if varset.is_empty(VarSet) then
            VarNameSrc = vns_var_table(VarTable0)
        else
            VarNameSrc = vns_varset(VarSet)
        ),
        ( if var_table_is_empty(VarTable0) then
            make_var_table(ModuleInfo, VarSet, ExplicitVarTypes, VarTable)
        else
            VarTable = VarTable0
        ),
        pred_id_to_int(PredId, PredIdInt),
        PredOrFuncStr = pred_or_func_to_full_str(PredOrFunc),
        PredStatusStr = pred_import_status_to_string(PredStatus),
        pred_info_get_goal_type(PredInfo, GoalType),

        string.builder.format("%% pred id: %d, category: %s, status %s\n",
            [i(PredIdInt), s(PredOrFuncStr), s(PredStatusStr)], !State),
        string.builder.format("%% goal_type: %s\n",
            [s(string.string(GoalType))], !State),

        format_pred_markers(Markers, !State),
        pred_info_get_obsolete_in_favour_of(PredInfo, MaybeObsoleteInFavourOf),
        (
            MaybeObsoleteInFavourOf = no
        ;
            MaybeObsoleteInFavourOf = yes(ObsoleteInFavourOf),
            string.builder.append_string("% obsolete in favour of one of\n",
                !State),
            list.foldl(format_obsolete_in_favour_of,
                ObsoleteInFavourOf, !State)
        ),
        pred_info_get_format_call(PredInfo, MaybeFormatCall),
        (
            MaybeFormatCall = no
        ;
            MaybeFormatCall = yes(FormatCall),
            FormatCall = format_call(_Context, OoMFormatStringValues),
            FormatStringValues = one_or_more_to_list(OoMFormatStringValues),
            output_format_string_values(FormatStringValues, !State)
        ),
        format_pred_types(VarNamePrint, TVarSet, VarTable, RttiVarMaps,
            ProofMap, ConstraintMap, ExternalTypeParams, !State),
        format_pred_proc_var_name_remap(VarNameSrc, VarNameRemap, !State),

        get_clause_list_maybe_repeated(ClausesRep, Clauses),
        (
            FilledInProcIdsInfos = [],
            GetProcNum =
                ( func(ProcId - _) = N :-
                    proc_id_to_int(ProcId, N)
                ),
            ProcNums = list.map(GetProcNum, ProcIdsInfos),
            FilledInProcNums = list.map(GetProcNum, FilledInProcIdsInfos),
            string.builder.format("%% procedures: %s, filled in %s\n",
                [s(string.string(ProcNums)),
                s(string.string(FilledInProcNums))], !State),
            (
                Clauses = [],
                ClauseCountReport = report_clause_count(Clauses),
                string.builder.format("%% clause count: %s\n",
                    [s(ClauseCountReport)], !State)
            ;
                Clauses = [_ | _],
                set_dump_opts_for_clauses(Info, InfoForClauses),
                format_clauses(InfoForClauses, Lang, ModuleInfo,
                    PredId, PredOrFunc, VarNameSrc, no_tvarset_var_table,
                    VarNamePrint, HeadVars, Clauses, !State)
            )
        ;
            FilledInProcIdsInfos = [_ | _],
            ClauseCountReport = report_clause_count(Clauses),
            string.builder.format("%% clause count: %s\n",
                [s(ClauseCountReport)], !State)
        ),

        pred_info_get_origin(PredInfo, Origin),
        OriginStr = dump_origin(TVarSet, VarNamePrint, "% origin: ", Origin),
        string.builder.append_string(OriginStr, !State),
        PrintedPred = yes
    else
        PrintedPred = no
    ),
    ( if
        string.contains_char(DumpOptions, 'x'),
        FilledInProcIdsInfos = [_ | _]
    then
        format_procs_loop(Info, VarNamePrint, ModuleInfo, PredId, PredInfo,
            FilledInProcIdsInfos, !State),
        PrintedProc = yes
    else
        PrintedProc = no
    ),
    ( if
        ( PrintedPred = yes
        ; PrintedProc = yes
        )
    then
        string.builder.append_string("\n", !State)
    else
        true
    ).

:- pred output_format_string_values(list(format_string_values)::in,
    string.builder.state::di, string.builder.state::uo) is det.

output_format_string_values([], !State).
output_format_string_values([FmtStringValue | FmtStringValues], !State) :-
    FmtStringValue = format_string_values(OrigFmtStr, OrigValues,
        CurFmtStr, CurValues),
    string.builder.format(
        "%% format call: format string in arg %d/%d, values in arg %d/%d\n",
        [i(OrigFmtStr), i(CurFmtStr), i(OrigValues), i(CurValues)], !State),
    output_format_string_values(FmtStringValues, !State).

:- func report_clause_count(list(clause)) = string.

report_clause_count(Clauses) = Report :-
    count_clause_langs(Clauses, 0, Mer, 0, C, 0, Cs, 0, Java),
    Total = Mer + C + Cs + Java,
    ( if Total = 0 then
        Report = "0"
    else
        MerStr = clause_count_to_str("Mercury", Mer),
        CStr = clause_count_to_str("C", C),
        CsStr = clause_count_to_str("C#", Cs),
        JavaStr = clause_count_to_str("Java", Java),
        list.filter(unify(""), [MerStr, CStr, CsStr, JavaStr], _, LangStrs),
        (
            LangStrs = [],
            unexpected($pred, "LangStrs = []")
        ;
            LangStrs = [Report]
        ;
            LangStrs = [_, _  | _],
            LangsStr = string.join_list(", ", LangStrs),
            string.format("%d total (%s)", [i(Total), s(LangsStr)], Report)
        )
    ).

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

count_clause_langs([], !Mer, !C, !Cs, !Java).
count_clause_langs([Clause | Clauses], !Mer, !C, !Cs, !Java) :-
    Lang = Clause ^ clause_lang,
    (
        Lang = impl_lang_mercury,
        !:Mer = !.Mer + 1
    ;
        Lang = impl_lang_foreign(ForeignLang),
        (
            ForeignLang = lang_c,
            !:C = !.C + 1
        ;
            ForeignLang = lang_csharp,
            !:Cs = !.Cs + 1
        ;
            ForeignLang = lang_java,
            !:Java = !.Java + 1
        )
    ),
    count_clause_langs(Clauses, !Mer, !C, !Cs, !Java).

:- func clause_count_to_str(string, int) = string.

clause_count_to_str(LangStr, Count) = LangReport :-
    ( if Count = 0 then
        LangReport = ""
    else
        string.format("%d %s", [i(Count), s(LangStr)], LangReport)
    ).

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

:- pred find_filled_in_procs(assoc_list(proc_id, proc_info)::in,
    assoc_list(proc_id, proc_info)::out) is det.

find_filled_in_procs([], []).
find_filled_in_procs([ProcIdInfo | ProcIdsInfos], FilledInProcIdsInfos) :-
    find_filled_in_procs(ProcIdsInfos, TailFilledInProcIdsInfos),
    ProcIdInfo = _ProcId - ProcInfo,
    proc_info_get_goal(ProcInfo, Goal),
    Goal = hlds_goal(GoalExpr, _),
    ( if GoalExpr = conj(plain_conj, []) then
        FilledInProcIdsInfos = TailFilledInProcIdsInfos
    else
        FilledInProcIdsInfos = [ProcIdInfo | TailFilledInProcIdsInfos]
    ).

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

:- pred format_pred_markers(pred_markers::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_pred_markers(Markers, !State) :-
    markers_to_marker_list(Markers, MarkerList),
    (
        MarkerList = []
    ;
        MarkerList = [_ | _],
        list.map(marker_name, MarkerList, MarkerNames),
        MarkerNamesStr = string.join_list(", ", MarkerNames),
        string.builder.format("%% markers: %s\n", [s(MarkerNamesStr)], !State)
    ).

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

:- pred format_obsolete_in_favour_of(sym_name_arity::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_obsolete_in_favour_of(ObsoleteInFavourOf, !State) :-
    ObsoleteInFavourOf = sym_name_arity(SymName, Arity),
    string.builder.format("%%    %s/%d\n",
        [s(sym_name_to_string(SymName)), i(Arity)], !State).

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

:- pred format_pred_types(var_name_print::in, tvarset::in, var_table::in,
    rtti_varmaps::in, constraint_proof_map::in, constraint_map::in,
    list(tvar)::in, string.builder.state::di, string.builder.state::uo) is det.

format_pred_types(VarNamePrint, TVarSet, VarTable, RttiVarMaps,
        ProofMap, ConstraintMap, ExternalTypeParams, !State) :-
    format_rtti_varmaps(VarNamePrint, TVarSet, VarTable, RttiVarMaps, !State),
    ( if map.is_empty(ProofMap) then
        true
    else
        format_constraint_proof_map(0u, VarNamePrint, TVarSet,
            ProofMap, !State),
        string.builder.append_string("\n", !State)
    ),
    ( if map.is_empty(ConstraintMap) then
        true
    else
        format_constraint_map(VarNamePrint, TVarSet, ConstraintMap, !State)
    ),
    (
        ExternalTypeParams = []
    ;
        ExternalTypeParams = [_ | _],
        string.builder.append_string("% external_type_params:\n", !State),
        string.builder.append_string("% ", !State),
        mercury_format_vars_vs(TVarSet, VarNamePrint, ExternalTypeParams,
            string.builder.handle, !State),
        string.builder.append_string("\n", !State)
    ),
    format_var_types(VarNamePrint, TVarSet, VarTable, !State).

:- pred format_constraint_map(var_name_print::in, tvarset::in,
    constraint_map::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_constraint_map(VarNamePrint, VarSet, ConstraintMap, !State) :-
    string.builder.append_string("% constraint map:\n", !State),
    map.foldl(format_constraint_map_entry(VarNamePrint, VarSet),
        ConstraintMap, !State).

:- pred format_constraint_map_entry(var_name_print::in, tvarset::in,
    constraint_id::in, prog_constraint::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_constraint_map_entry(VarNamePrint, VarSet,
        ConstraintId, ProgConstraint, !State) :-
    string.builder.append_string("% ", !State),
    format_constraint_id(ConstraintId, !State),
    string.builder.append_string(": ", !State),
    mercury_format_constraint(VarSet, VarNamePrint, ProgConstraint,
        string.builder.handle, !State),
    string.builder.append_string("\n", !State).

:- pred format_constraint_id(constraint_id::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_constraint_id(ConstraintId, !State) :-
    ConstraintId = constraint_id(ConstraintType, GoalId, N),
    (
        ConstraintType = assumed,
        ConstraintTypeChar = 'E'
    ;
        ConstraintType = unproven,
        ConstraintTypeChar = 'A'
    ),
    GoalId = goal_id(GoalIdNum),
    string.builder.format("(%c, %u, %d)",
        [c(ConstraintTypeChar), u(GoalIdNum), i(N)], !State).

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

:- pred set_dump_opts_for_clauses(hlds_out_info::in, hlds_out_info::out)
    is det.

set_dump_opts_for_clauses(Info, ClausesInfo) :-
    OptionsStr = Info ^ hoi_dump_hlds_options,
    some [!DumpStr] (
        !:DumpStr = "",
        ( if string.contains_char(OptionsStr, 'c') then
            !:DumpStr = !.DumpStr ++ "c"
        else
            true
        ),
        ( if string.contains_char(OptionsStr, 'n') then
            !:DumpStr = !.DumpStr ++ "n"
        else
            true
        ),
        ( if string.contains_char(OptionsStr, 'v') then
            !:DumpStr = !.DumpStr ++ "v"
        else
            true
        ),
        ( if string.contains_char(OptionsStr, 'g') then
            !:DumpStr = !.DumpStr ++ "g"
        else
            true
        ),
        ( if string.contains_char(OptionsStr, 'P') then
            !:DumpStr = !.DumpStr ++ "P"
        else
            true
        ),
        DumpStr = !.DumpStr
    ),
    ClausesInfo = Info ^ hoi_dump_hlds_options := DumpStr.

:- pred format_clauses(hlds_out_info::in, output_lang::in, module_info::in,
    pred_id::in, pred_or_func::in, var_name_source::in, type_qual::in,
    var_name_print::in, proc_arg_vector(prog_var)::in, list(clause)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_clauses(Info, Lang, ModuleInfo, PredId, PredOrFunc, VarNameSrc,
        TypeQual, VarNamePrint, HeadVarsVector, Clauses, !State) :-
    HeadVars = proc_arg_vector_to_list(HeadVarsVector),
    term_subst.var_list_to_term_list(HeadVars, HeadTerms),
    format_clauses_loop(Info, Lang, ModuleInfo, PredId, PredOrFunc,
        VarNameSrc, TypeQual, VarNamePrint, HeadTerms, 1, Clauses, !State).

:- pred format_clauses_loop(hlds_out_info::in,
    output_lang::in, module_info::in, pred_id::in, pred_or_func::in,
    var_name_source::in, type_qual::in, var_name_print::in,
    list(prog_term)::in, int::in, list(clause)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_clauses_loop(Info, Lang, ModuleInfo, PredId, PredOrFunc,
        VarNameSrc, TypeQual, VarNamePrint, HeadTerms,
        CurClauseNum, Clauses, !State) :-
    (
        Clauses = []
    ;
        Clauses = [FirstClause | LaterClauses],
        string.builder.format("%% clause %d\n", [i(CurClauseNum)], !State),
        format_clause(Info, Lang, ModuleInfo, PredId, PredOrFunc,
            VarNameSrc, TypeQual, VarNamePrint, write_actual_modes, 0u,
            HeadTerms, FirstClause, !State),
        format_clauses_loop(Info, Lang, ModuleInfo, PredId, PredOrFunc,
            VarNameSrc, TypeQual, VarNamePrint, HeadTerms,
            CurClauseNum + 1, LaterClauses, !State)
    ).

write_clause(Info, Stream, Lang, ModuleInfo, PredId, PredOrFunc, VarNameSrc,
        TypeQual, VarNamePrint, WriteWhichModes, Indent, HeadTerms,
        Clause, !IO) :-
    State0 = string.builder.init,
    format_clause(Info, Lang, ModuleInfo, PredId, PredOrFunc, VarNameSrc,
        TypeQual, VarNamePrint, WriteWhichModes, Indent, HeadTerms,
        Clause, State0, State),
    Str = string.builder.to_string(State),
    io.write_string(Stream, Str, !IO).

format_clause(Info, Lang, ModuleInfo, PredId, PredOrFunc, VarNameSrc,
        TypeQual, VarNamePrint, WriteWhichModes, Indent, HeadTerms,
        Clause, !State) :-
    Clause = clause(ApplicableModes, Goal, ImplLang, Context,
        _StateVarWarnings),
    IndentStr = indent2_string(Indent),
    Indent1 = Indent + 1u,
    DumpOptions = Info ^ hoi_dump_hlds_options,
    (
        ApplicableModes = all_modes
    ;
        ApplicableModes = selected_modes(Modes),
        ( if string.contains_char(DumpOptions, 'm') then
            string.builder.format(
                "%s%% modes for which this clause applies: ",
                [s(IndentStr)], !State),
            ModeInts = list.map(proc_id_to_int, Modes),
            format_intlist(ModeInts, !State),
            string.builder.append_string("\n", !State)
        else
            true
        )
    ;
        ApplicableModes = unify_in_in_modes,
        ( if string.contains_char(DumpOptions, 'm') then
            string.builder.format(
                "%s%% this clause applies only to <in,in> unify modes.\n",
                [s(IndentStr)], !State)
        else
            true
        )
    ;
        ApplicableModes = unify_non_in_in_modes,
        ( if string.contains_char(DumpOptions, 'm') then
            string.builder.format(
                "%s%% this clause applies only to non <in,in> unify modes.\n",
                [s(IndentStr)], !State)
        else
            true
        )
    ),
    (
        ImplLang = impl_lang_mercury
    ;
        ImplLang = impl_lang_foreign(ForeignLang),
        string.builder.format("%s%% language of implementation: %s\n",
            [s(IndentStr), s(foreign_language_string(ForeignLang))], !State)
    ),
    module_info_pred_info(ModuleInfo, PredId, PredInfo),
    AllProcIds = pred_info_all_procids(PredInfo),
    ( if
        ApplicableModes = selected_modes(SelectedProcIds),
        SelectedProcIds \= AllProcIds
    then
        % If SelectedProcIds contains more than one mode, the output will have
        % multiple clause heads. This won't be pretty and it won't be
        % syntactically valid, but it is more useful for debugging
        % than a compiler abort during the dumping process.
        format_annotated_clause_heads(ModuleInfo, Lang, VarNameSrc,
            VarNamePrint, WriteWhichModes, PredId, PredOrFunc, SelectedProcIds,
            Context, HeadTerms, !State)
    else
        format_clause_head(ModuleInfo, VarNameSrc, VarNamePrint,
            PredId, PredOrFunc, HeadTerms, !State)
    ),
    ( if Goal = hlds_goal(conj(plain_conj, []), _GoalInfo) then
        string.builder.append_string(".\n", !State)
    else
        string.builder.append_string(" :-\n", !State),
        pred_info_get_typevarset(PredInfo, TVarSet),
        varset.init(InstVarSet),
        InfoGoal = hlds_out_info_goal(Info, ModuleInfo,
            VarNameSrc, VarNamePrint, TVarSet, InstVarSet, TypeQual),
        do_format_goal(InfoGoal, Indent1, ".\n", Goal, !State)
    ).

:- pred format_annotated_clause_heads(module_info::in, output_lang::in,
    var_name_source::in, var_name_print::in, write_which_modes::in,
    pred_id::in, pred_or_func::in, list(proc_id)::in, term.context::in,
    list(prog_term)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_annotated_clause_heads(_, _, _, _, _, _, _, [], _, _, !State).
format_annotated_clause_heads(ModuleInfo, Lang, VarNameSrc,
        VarNamePrint, WriteWhichModes, PredId, PredOrFunc, [ProcId | ProcIds],
        Context, HeadTerms, !State) :-
    format_annotated_clause_head(ModuleInfo, Lang, VarNameSrc,
        VarNamePrint, WriteWhichModes, PredId, PredOrFunc, ProcId,
        Context, HeadTerms, !State),
    format_annotated_clause_heads(ModuleInfo, Lang, VarNameSrc,
        VarNamePrint, WriteWhichModes, PredId, PredOrFunc, ProcIds,
        Context, HeadTerms, !State).

:- pred format_annotated_clause_head(module_info::in, output_lang::in,
    var_name_source::in, var_name_print::in, write_which_modes::in,
    pred_id::in, pred_or_func::in, proc_id::in, term.context::in,
    list(prog_term)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_annotated_clause_head(ModuleInfo, Lang, VarNameSrc, VarNamePrint,
        WriteWhichModes, PredId, PredOrFunc, ProcId,
        Context, HeadTerms, !State) :-
    module_info_pred_info(ModuleInfo, PredId, PredInfo),
    pred_info_get_proc_table(PredInfo, Procedures),
    ( if map.search(Procedures, ProcId, ProcInfo) then
        % When writing `.opt' files, use the declared argument modes so that
        % the modes are guaranteed to be syntactically identical to those
        % in the original program. The test in add_clause.m to check whether
        % a clause matches a procedure tests for syntactic identity (roughly).
        % The modes returned by proc_info_get_argmodes may have been slightly
        % expanded by propagate_types_into_modes.
        %
        % We can't use the declared argument modes when writing HLDS dumps
        % because the modes of the type-infos will not have been added,
        % so the call to assoc_list.from_corresponding_lists below
        % will abort. `.opt' files are written before the polymorphism pass.
        (
            WriteWhichModes = write_actual_modes,
            proc_info_get_argmodes(ProcInfo, ArgModes)
        ;
            WriteWhichModes = write_declared_modes,
            proc_info_declared_argmodes(ProcInfo, ArgModes)
        ),
        assoc_list.from_corresponding_lists(HeadTerms, ArgModes,
            AnnotatedPairs),
        AnnotatedHeadTerms = list.map(add_mode_qualifier(Lang, Context),
            AnnotatedPairs),
        format_clause_head(ModuleInfo, VarNameSrc,
            VarNamePrint, PredId, PredOrFunc, AnnotatedHeadTerms, !State)
    else
        % This procedure, even though it existed in the past, has been
        % eliminated.
        true
    ).

:- func add_mode_qualifier(output_lang, prog_context,
    pair(prog_term, mer_mode)) = prog_term.

add_mode_qualifier(Lang, Context, HeadTerm - Mode) = AnnotatedTerm :-
    construct_qualified_term_with_context(unqualified("::"),
        [HeadTerm, mode_to_term_with_context(Lang, Context, Mode)],
        Context, AnnotatedTerm).

:- pred format_clause_head(module_info::in, var_name_source::in,
    var_name_print::in, pred_id::in, pred_or_func::in, list(prog_term)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_clause_head(ModuleInfo, VarNameSrc, VarNamePrint, PredId,
        PredOrFunc, HeadTerms, !State) :-
    PredName = predicate_name(ModuleInfo, PredId),
    ModuleName = predicate_module(ModuleInfo, PredId),
    (
        PredOrFunc = pf_function,
        pred_args_to_func_args(HeadTerms, FuncArgs, RetVal),
        ArgsStr = qualified_functor_with_term_args_to_string(VarNameSrc,
            VarNamePrint, ModuleName, term.atom(PredName), FuncArgs),
        string.builder.append_string(ArgsStr, !State),
        string.builder.append_string(" = ", !State),
        mercury_format_term_nq_src(VarNameSrc, VarNamePrint,
            next_to_graphic_token, RetVal, string.builder.handle, !State)
    ;
        PredOrFunc = pf_predicate,
        ArgsStr = qualified_functor_with_term_args_to_string(VarNameSrc,
            VarNamePrint, ModuleName, term.atom(PredName), HeadTerms),
        string.builder.append_string(ArgsStr, !State)
    ).

%---------------------------------------------------------------------------%
%
% Write out procedures.
%

:- pred format_procs_loop(hlds_out_info::in, var_name_print::in,
    module_info::in, pred_id::in, pred_info::in,
    assoc_list(proc_id, proc_info)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_procs_loop(_, _, _, _, _, [], !State).
format_procs_loop(Info, VarNamePrint, ModuleInfo,
        PredId, PredInfo, [ProcId - ProcInfo | ProcIdsInfos], !State) :-
    format_proc(Info, VarNamePrint, ModuleInfo,
        PredId, PredInfo, ProcId, ProcInfo, !State),
    format_procs_loop(Info, VarNamePrint, ModuleInfo,
        PredId, PredInfo, ProcIdsInfos, !State).

:- pred format_proc(hlds_out_info::in, var_name_print::in, module_info::in,
    pred_id::in, pred_info::in, proc_id::in, proc_info::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_proc(Info, VarNamePrint, ModuleInfo, PredId, PredInfo,
        ProcId, ProcInfo, !State) :-
    pred_info_get_typevarset(PredInfo, TVarSet),
    proc_info_get_can_process(ProcInfo, CanProcess),
    proc_info_get_var_table(ProcInfo, VarTable),
    proc_info_get_declared_determinism(ProcInfo, DeclaredDeterminism),
    proc_info_get_inferred_determinism(ProcInfo, InferredDeterminism),
    proc_info_get_headvars(ProcInfo, HeadVars),
    proc_info_get_argmodes(ProcInfo, HeadModes),
    proc_info_get_maybe_arglives(ProcInfo, MaybeArgLives),
    proc_info_get_reg_r_headvars(ProcInfo, RegR_HeadVars),
    proc_info_get_maybe_arg_info(ProcInfo, MaybeArgInfos),
    proc_info_get_goal(ProcInfo, Goal),
    proc_info_get_maybe_arg_size_info(ProcInfo, MaybeArgSize),
    proc_info_get_maybe_termination_info(ProcInfo, MaybeTermination),
    proc_info_get_structure_sharing(ProcInfo, MaybeStructureSharing),
    proc_info_get_structure_reuse(ProcInfo, MaybeStructureReuse),
    proc_info_get_rtti_varmaps(ProcInfo, RttiVarMaps),
    proc_info_get_cse_nopull_contexts(ProcInfo, CseNoPullContexts),
    proc_info_get_eval_method(ProcInfo, EvalMethod),
    proc_info_get_deleted_call_callees(ProcInfo, DeletedCallCalleeSet),
    proc_info_get_is_address_taken(ProcInfo, IsAddressTaken),
    proc_info_get_has_parallel_conj(ProcInfo, HasParallelConj),
    proc_info_get_has_user_event(ProcInfo, HasUserEvent),
    proc_info_get_maybe_proc_table_io_info(ProcInfo, MaybeProcTableIOInfo),
    proc_info_get_call_table_tip(ProcInfo, MaybeCallTableTip),
    proc_info_get_maybe_deep_profile_info(ProcInfo, MaybeDeepProfileInfo),
    proc_info_get_maybe_untuple_info(ProcInfo, MaybeUntupleInfo),
    proc_info_get_var_name_remap(ProcInfo, VarNameRemap),
    Indent1 = 1u,
    Indent1Str = indent2_string(Indent1),

    DumpOptions = Info ^ hoi_dump_hlds_options,
    pred_id_to_int(PredId, PredIdInt),
    proc_id_to_int(ProcId, ProcIdInt),
    PredIdStr = pred_id_to_dev_string(ModuleInfo, PredId),
    DetismStr = determinism_to_string(InferredDeterminism),
    string.builder.format("%s%% pred id %d: %s\n",
        [s(Indent1Str), i(PredIdInt), s(PredIdStr)], !State),
    string.builder.format("%s%% mode number %d (%s)\n",
        [s(Indent1Str), i(ProcIdInt), s(DetismStr)], !State),

    format_var_types(VarNamePrint, TVarSet, VarTable, !State),
    format_rtti_varmaps(VarNamePrint, TVarSet, VarTable, RttiVarMaps, !State),

    format_proc_flags(CanProcess, IsAddressTaken,
        HasParallelConj, HasUserEvent, !State),
    string.builder.format("%% cse_nopull_contexts: %s\n",
        [s(string.string(CseNoPullContexts))], !State),
    format_proc_tabling_info(VarTable, TVarSet, VarNamePrint,
        EvalMethod, MaybeProcTableIOInfo, MaybeCallTableTip, !State),
    format_proc_deep_profiling_info(VarTable, VarNamePrint,
        MaybeDeepProfileInfo, !State),
    format_proc_termination_info(DumpOptions,
        MaybeArgSize, MaybeTermination, !State),
    format_proc_opt_info(DumpOptions, VarTable, TVarSet,
        VarNamePrint, MaybeStructureSharing, MaybeStructureReuse,
        MaybeUntupleInfo, !State),
    format_proc_deleted_callee_set(DeletedCallCalleeSet, !State),
    format_pred_proc_var_name_remap(vns_var_table(VarTable),
        VarNameRemap, !State),
    format_eff_trace_level(ModuleInfo, PredInfo, ProcInfo, !State),

    PredSymName = unqualified(predicate_name(ModuleInfo, PredId)),
    PredOrFunc = pred_info_is_pred_or_func(PredInfo),
    varset.init(ModeVarSet),
    (
        PredOrFunc = pf_predicate,
        MaybeWithInst = maybe.no,
        mercury_format_pred_mode_decl(output_debug, ModeVarSet,
            PredSymName, HeadModes, MaybeWithInst,
            DeclaredDeterminism, string.builder.handle, !State)
    ;
        PredOrFunc = pf_function,
        pred_args_to_func_args(HeadModes, FuncHeadModes, RetHeadMode),
        mercury_format_func_mode_decl(output_debug, ModeVarSet,
            PredSymName, FuncHeadModes, RetHeadMode,
            DeclaredDeterminism, string.builder.handle, !State)
    ),
    format_proc_arg_info(DumpOptions, VarTable, VarNamePrint,
        MaybeArgLives, RegR_HeadVars, MaybeArgInfos, !State),
    pred_info_get_status(PredInfo, PredStatus),
    ( if
        PredStatus = pred_status(status_pseudo_imported),
        hlds_pred.in_in_unification_proc_id(ProcId)
    then
        true
    else
        proc_info_get_stack_slots(ProcInfo, StackSlots),
        format_stack_slots(VarTable, VarNamePrint, StackSlots, !State),
        term_subst.var_list_to_term_list(HeadVars, HeadTerms),
        format_clause_head(ModuleInfo, vns_var_table(VarTable),
            VarNamePrint, PredId, PredOrFunc, HeadTerms, !State),
        string.builder.append_string(" :-\n", !State),
        proc_info_get_inst_varset(ProcInfo, InstVarSet),
        format_goal(Info, ModuleInfo, vns_var_table(VarTable),
            VarNamePrint, TVarSet, InstVarSet, Indent1, ".\n", Goal, !State)
    ).

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

:- pred format_var_types(var_name_print::in, tvarset::in, var_table::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_var_types(VarNamePrint, TVarSet, VarTable, !State) :-
    var_table_count(VarTable, NumVars),
    string.builder.format("%% variable table (%d entries):\n",
        [i(NumVars)], !State),
    var_table_to_sorted_assoc_list(VarTable, VarsEntries),
    format_var_types_loop(VarNamePrint, TVarSet, VarsEntries, !State).

:- pred format_var_types_loop(var_name_print::in,
    tvarset::in, assoc_list(prog_var, var_table_entry)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_var_types_loop(_, _, [], !State).
format_var_types_loop(VarNamePrint, TypeVarSet,
        [Var - Entry | VarsEntries], !State) :-
    Entry = vte(Name, Type, IsDummy),
    term.var_to_int(Var, VarNum),
    VarStr = mercury_var_raw_to_string(VarNamePrint, Var, Name),
    TypeStr = mercury_type_to_string(TypeVarSet, VarNamePrint, Type),
    (
        IsDummy = is_dummy_type,
        DummySuffix = " (dummy type)"
    ;
        IsDummy = is_not_dummy_type,
        DummySuffix = ""
    ),
    string.builder.format("%% var #%d, %s: %s%s\n",
        [i(VarNum), s(VarStr), s(TypeStr), s(DummySuffix)], !State),
    format_var_types_loop(VarNamePrint, TypeVarSet, VarsEntries, !State).

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

:- pred format_rtti_varmaps(var_name_print::in, tvarset::in, var_table::in,
    rtti_varmaps::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_rtti_varmaps(VarNamePrint, TVarSet, VarTable, RttiVarMaps, !State) :-
    string.builder.append_string("% type_info varmap:\n", !State),
    rtti_varmaps_tvars(RttiVarMaps, TypeVars),
    list.foldl(
        format_type_info_locn(VarNamePrint, TVarSet, VarTable, RttiVarMaps),
        TypeVars, !State),
    string.builder.append_string("% typeclass_info varmap:\n", !State),
    rtti_varmaps_reusable_constraints(RttiVarMaps, Constraints),
    list.foldl(
        format_typeclass_info_var(VarNamePrint, TVarSet, VarTable, RttiVarMaps),
        Constraints, !State),
    string.builder.append_string("% rtti_var_info:\n", !State),
    rtti_varmaps_rtti_prog_vars(RttiVarMaps, ProgVars),
    list.foldl(
        format_rtti_var_info(VarNamePrint, TVarSet, VarTable, RttiVarMaps),
        ProgVars, !State).

:- pred format_type_info_locn(var_name_print::in,
    tvarset::in, var_table::in, rtti_varmaps::in, tvar::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_type_info_locn(VarNamePrint, TVarSet, VarTable, RttiVarMaps, TVar,
        !State) :-
    TVarStr = mercury_var_to_string_vs(TVarSet, VarNamePrint, TVar),
    term.var_to_int(TVar, TVarNum),
    string.builder.format("%% %s(number %d) -> ",
        [s(TVarStr), i(TVarNum)], !State),
    rtti_lookup_type_info_locn(RttiVarMaps, TVar, Locn),
    (
        Locn = type_info(Var),
        VarStr = mercury_var_to_string(VarTable, VarNamePrint, Var),
        string.builder.format("type_info(%s)", [s(VarStr)], !State)
    ;
        Locn = typeclass_info(Var, Index),
        VarStr = mercury_var_to_string(VarTable, VarNamePrint, Var),
        string.builder.format("typeclass_info(%s, %d)",
            [s(VarStr), i(Index)], !State)
    ),
    term.var_to_int(Var, VarNum),
    string.builder.format(" (number %d)\n", [i(VarNum)], !State).

:- pred format_typeclass_info_var(var_name_print::in, tvarset::in,
    var_table::in, rtti_varmaps::in, prog_constraint::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_typeclass_info_var(VarNamePrint, TVarSet, VarTable, RttiVarMaps,
        Constraint, !State) :-
    string.builder.append_string("% ", !State),
    mercury_format_constraint(TVarSet, VarNamePrint, Constraint,
        string.builder.handle, !State),
    string.builder.append_string(" -> ", !State),
    rtti_lookup_typeclass_info_var(RttiVarMaps, Constraint, Var),
    mercury_format_var(VarTable, VarNamePrint, Var,
        string.builder.handle, !State),
    string.builder.append_string("\n", !State).

:- pred format_rtti_var_info(var_name_print::in,
    tvarset::in, var_table::in, rtti_varmaps::in, prog_var::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_rtti_var_info(VarNamePrint, TVarSet, VarTable, RttiVarMaps, Var,
        !State) :-
    term.var_to_int(Var, VarNum),
    VarStr = mercury_var_to_string(VarTable, VarNamePrint, Var),
    string.builder.format("%% %s (number %d) -> ",
        [s(VarStr), i(VarNum)], !State),
    rtti_varmaps_var_info(RttiVarMaps, Var, VarInfo),
    (
        VarInfo = type_info_var(Type),
        string.builder.append_string("type_info for ", !State),
        mercury_format_type(TVarSet, VarNamePrint, Type,
            string.builder.handle, !State)
    ;
        VarInfo = typeclass_info_var(Constraint),
        string.builder.append_string("typeclass_info for ", !State),
        mercury_format_constraint(TVarSet, VarNamePrint, Constraint,
            string.builder.handle, !State)
    ;
        VarInfo = non_rtti_var,
        unexpected($pred, "non rtti var")
    ),
    string.builder.append_string("\n", !State).

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

:- pred format_proc_flags(can_process::in, is_address_taken::in,
    has_parallel_conj::in, has_user_event::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_proc_flags(CanProcess, IsAddressTaken, HasParallelConj,
        HasUserEvent, !State) :-
    (
        CanProcess = can_process_now
    ;
        CanProcess = cannot_process_yet,
        string.builder.append_string("% cannot_process_yet\n", !State)
    ),
    (
        IsAddressTaken = address_is_taken,
        string.builder.append_string("% address is taken\n", !State)
    ;
        IsAddressTaken = address_is_not_taken,
        string.builder.append_string("% address is not taken\n", !State)
    ),
    (
        HasParallelConj = has_parallel_conj,
        string.builder.append_string(
            "% contains parallel conjunction\n", !State)
    ;
        HasParallelConj = has_no_parallel_conj,
        string.builder.append_string(
            "% does not contain parallel conjunction\n", !State)
    ),
    (
        HasUserEvent = has_user_event,
        string.builder.append_string("% contains user event\n", !State)
    ;
        HasUserEvent = has_no_user_event,
        string.builder.append_string("% does not contain user event\n", !State)
    ).

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

:- pred format_proc_tabling_info(
    var_table::in, tvarset::in, var_name_print::in, eval_method::in,
    maybe(proc_table_io_info)::in, maybe(prog_var)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_proc_tabling_info(VarTable, TVarSet, VarNamePrint,
        EvalMethod, MaybeProcTableIOInfo, MaybeCallTableTip, !State) :-
    (
        EvalMethod = eval_normal
    ;
        EvalMethod = eval_tabled(TabledMethod),
        string.builder.format("%% eval method: %s\n",
            [s(tabled_eval_method_to_string(TabledMethod))], !State)
    ),
    (
        MaybeProcTableIOInfo = yes(ProcTableIOInfo),
        format_proc_table_io_info(TVarSet, ProcTableIOInfo, !State)
    ;
        MaybeProcTableIOInfo = no
    ),
    (
        MaybeCallTableTip = yes(CallTableTip),
        string.builder.append_string("% call table tip: ", !State),
        mercury_format_var(VarTable, VarNamePrint, CallTableTip,
            string.builder.handle, !State),
        string.builder.append_string("\n", !State)
    ;
        MaybeCallTableTip = no
    ).

:- pred format_proc_table_io_info(tvarset::in, proc_table_io_info::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_proc_table_io_info(TVarSet, ProcTableIOInfo, !State) :-
    ProcTableIOInfo = proc_table_io_info(MaybeArgInfos),
    (
        MaybeArgInfos = no,
        string.builder.append_string(
            "% proc table io info: io tabled, no arg_infos\n", !State)
    ;
        MaybeArgInfos = yes(ArgInfos),
        string.builder.append_string(
            "% proc table io info: io tabled, arg_infos:\n", !State),
        format_table_arg_infos(TVarSet, ArgInfos, !State)
    ).

write_table_arg_infos(Stream, TVarSet, TableArgInfos, !IO) :-
    State0 = string.builder.init,
    format_table_arg_infos(TVarSet, TableArgInfos, State0, State),
    Str = string.builder.to_string(State),
    io.write_string(Stream, Str, !IO).

format_table_arg_infos(TVarSet, TableArgInfos, !State) :-
    TableArgInfos = table_arg_infos(ArgInfos, TVarMap),
    string.builder.append_string("% arg infos:\n", !State),
    list.foldl(format_table_arg_info(TVarSet), ArgInfos, !State),
    map.to_assoc_list(TVarMap, TVarPairs),
    (
        TVarPairs = []
    ;
        TVarPairs = [_ | _],
        string.builder.append_string("% type var map:\n", !State),
        list.foldl(format_table_tvar_map_entry(TVarSet), TVarPairs, !State)
    ).

:- pred format_table_arg_info(tvarset::in, table_arg_info::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_table_arg_info(TVarSet, ArgInfo, !State) :-
    ArgInfo = table_arg_info(HeadVarNum, HeadVarName, SlotNum, Type),
    TVarStr = mercury_type_to_string(TVarSet, print_name_and_num, Type),
    string.builder.format("%% %s / %d in slot %d, type %s\n",
        [s(HeadVarName), i(HeadVarNum), i(SlotNum), s(TVarStr)], !State).

:- pred format_table_tvar_map_entry(tvarset::in, pair(tvar, table_locn)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_table_tvar_map_entry(TVarSet, TVar - Locn, !State) :-
    TVarStr = mercury_var_to_string_vs(TVarSet, print_name_and_num, TVar),
    string.builder.format("%% typeinfo for %s -> ", [s(TVarStr)], !State),
    (
        Locn = table_locn_direct(N),
        string.builder.format("direct in register %d\n", [i(N)], !State)
    ;
        Locn = table_locn_indirect(N, O),
        string.builder.format("indirect from register %d, offset %d\n",
            [i(N), i(O)], !State)
    ).

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

:- pred format_proc_deep_profiling_info(var_table::in, var_name_print::in,
    maybe(deep_profile_proc_info)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_proc_deep_profiling_info(VarTable, VarNamePrint,
        MaybeDeepProfileInfo, !State) :-
    (
        MaybeDeepProfileInfo = yes(DeepProfileInfo),
        DeepProfileInfo = deep_profile_proc_info(MaybeRecInfo,
            MaybeDeepLayout, _),
        (
            MaybeRecInfo = yes(DeepRecInfo),
            DeepRecInfo = deep_recursion_info(Role, _),
            string.builder.append_string("% deep recursion info: ", !State),
            (
                Role = deep_prof_inner_proc(DeepPredProcId),
                string.builder.append_string("inner, outer is ", !State)
            ;
                Role = deep_prof_outer_proc(DeepPredProcId),
                string.builder.append_string("outer, inner is ", !State)
            ),
            DeepPredProcId = proc(DeepPredId, DeepProcId),
            pred_id_to_int(DeepPredId, DeepPredInt),
            proc_id_to_int(DeepProcId, DeepProcInt),
            string.builder.format("%d/%d\n",
                [i(DeepPredInt), i(DeepProcInt)], !State)
        ;
            MaybeRecInfo = no
        ),
        (
            MaybeDeepLayout = yes(DeepLayout),
            DeepLayout = hlds_deep_layout(ProcStatic, ExcpVars),
            format_hlds_proc_static(ProcStatic, !State),
            ExcpVars = hlds_deep_excp_vars(TopCSD, MiddleCSD,
                MaybeOldOutermost),
            string.builder.append_string("% deep layout info: ", !State),
            string.builder.append_string("TopCSD is ", !State),
            mercury_format_var(VarTable, VarNamePrint, TopCSD,
                string.builder.handle, !State),
            string.builder.append_string(", MiddleCSD is ", !State),
            mercury_format_var(VarTable, VarNamePrint, MiddleCSD,
                string.builder.handle, !State),
            (
                MaybeOldOutermost = yes(OldOutermost),
                string.builder.append_string(", OldOutermost is ", !State),
                mercury_format_var(VarTable, VarNamePrint, OldOutermost,
                    string.builder.handle, !State)
            ;
                MaybeOldOutermost = no
            ),
            string.builder.append_string("\n", !State)
        ;
            MaybeDeepLayout = no
        )
    ;
        MaybeDeepProfileInfo = no
    ).

:- pred format_hlds_proc_static(hlds_proc_static::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_hlds_proc_static(ProcStatic, !State) :-
    ProcStatic = hlds_proc_static(FileName, LineNumber,
        InInterface, CallSiteStatics, CoveragePoints),
    string.builder.format("%% proc static filename: %s\n",
        [s(FileName)], !State),
    string.builder.format("%% proc static line number: %d\n",
        [i(LineNumber)], !State),
    string.builder.format("%% proc static is interface: %s\n",
        [s(string.string(InInterface))], !State),
    list.foldl2(format_hlds_ps_call_site,
        CallSiteStatics, 0, _, !State),
    list.foldl2(format_hlds_ps_coverage_point,
        CoveragePoints, 0, _, !State).

:- pred format_hlds_ps_call_site(call_site_static_data::in, int::in, int::out,
    string.builder.state::di, string.builder.state::uo) is det.

format_hlds_ps_call_site(CallSiteStaticData, !SlotNum, !State) :-
    string.builder.format("%% call site static slot %d\n",
        [i(!.SlotNum)], !State),
    (
        CallSiteStaticData = normal_call(CalleeRttiProcLabel, TypeSubst,
            FileName, LineNumber, GoalPath),
        string.builder.format("%% normal call to %s\n",
            [s(string.string(CalleeRttiProcLabel))], !State),
        string.builder.format("%% type subst <%s>, goal path <%s>\n",
            [s(TypeSubst), s(goal_path_to_string(GoalPath))], !State),
        string.builder.format("%% filename <%s>, line number <%d>\n",
            [s(FileName), i(LineNumber)], !State)
    ;
        (
            CallSiteStaticData = special_call(FileName, LineNumber, GoalPath),
            string.builder.append_string("% special call\n", !State)
        ;
            CallSiteStaticData = higher_order_call(FileName, LineNumber,
                GoalPath),
            string.builder.append_string("% higher order call\n", !State)
        ;
            CallSiteStaticData = method_call(FileName, LineNumber, GoalPath),
            string.builder.append_string("% method call\n", !State)
        ;
            CallSiteStaticData = callback(FileName, LineNumber, GoalPath),
            string.builder.append_string("% callback\n", !State)
        ),
        string.builder.format(
            "%% filename <%s>, line number <%d>, goal path <%s>\n",
            [s(FileName), i(LineNumber), s(goal_path_to_string(GoalPath))],
            !State)
    ),
    !:SlotNum = !.SlotNum + 1.

:- pred format_hlds_ps_coverage_point(coverage_point_info::in,
    int::in, int::out,
    string.builder.state::di, string.builder.state::uo) is det.

format_hlds_ps_coverage_point(CoveragePointInfo, !SlotNum, !State) :-
    CoveragePointInfo = coverage_point_info(RevGoalPath, PointType),
    string.builder.format(
        "%% coverage point slot %d: goal path <%s>, type %s\n",
        [i(!.SlotNum), s(rev_goal_path_to_string(RevGoalPath)),
            s(coverage_point_to_string(PointType))], !State),
    !:SlotNum = !.SlotNum + 1.

:- func coverage_point_to_string(cp_type) = string.

coverage_point_to_string(cp_type_coverage_after) = "after".
coverage_point_to_string(cp_type_branch_arm) = "branch arm".

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

:- pred format_proc_termination_info(string::in,
    maybe(arg_size_info)::in, maybe(termination_info)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_proc_termination_info(DumpOptions,  MaybeArgSize,
        MaybeTermination, !State) :-
    ( if string.contains_char(DumpOptions, 't') then
        SizeStr = maybe_arg_size_info_to_string(yes, MaybeArgSize),
        TermStr = maybe_termination_info_to_string(yes, MaybeTermination),
        string.builder.format("%% arg size properties: %s\n",
            [s(SizeStr)], !State),
        string.builder.format("%% termination properties: %s\n",
            [s(TermStr)], !State)
    else
        true
    ).

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

:- pred format_proc_opt_info(string::in, var_table::in, tvarset::in,
    var_name_print::in, maybe(structure_sharing_domain_and_status)::in,
    maybe(structure_reuse_domain_and_status)::in, maybe(untuple_proc_info)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_proc_opt_info(DumpOptions, VarTable, TVarSet, VarNamePrint,
        MaybeStructureSharing, MaybeStructureReuse, MaybeUntupleInfo,
        !State) :-
    ( if
        string.contains_char(DumpOptions, 'S'),
        MaybeStructureSharing = yes(StructureSharing)
    then
        string.builder.append_string("% structure sharing: \n", !State),
        StructureSharing =
            structure_sharing_domain_and_status(SharingAs, _Status),
        dump_structure_sharing_domain(string.builder.handle,
            vns_var_table(VarTable), TVarSet, SharingAs, !State)
    else
        true
    ),
    ( if
        string.contains_char(DumpOptions, 'R'),
        MaybeStructureReuse = yes(StructureReuse)
    then
        string.builder.append_string("% structure reuse: \n", !State),
        StructureReuse =
            structure_reuse_domain_and_status(ReuseAs, _ReuseStatus),
        dump_structure_reuse_domain(string.builder.handle,
            vns_var_table(VarTable), TVarSet, ReuseAs, !State)
    else
        true
    ),
    (
        MaybeUntupleInfo = yes(UntupleInfo),
        format_untuple_info(VarTable, VarNamePrint, UntupleInfo, !State)
    ;
        MaybeUntupleInfo = no
    ).

:- pred format_untuple_info(var_table::in, var_name_print::in,
    untuple_proc_info::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_untuple_info(VarTable, VarNamePrint, UntupleInfo, !State) :-
    UntupleInfo = untuple_proc_info(UntupleMap),
    string.builder.append_string("% untuple:\n", !State),
    map.foldl(format_untuple_info_loop(VarTable, VarNamePrint),
        UntupleMap, !State).

:- pred format_untuple_info_loop(var_table::in, var_name_print::in,
    prog_var::in, list(prog_var)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_untuple_info_loop(VarTable, VarNamePrint,
        OldVar, NewVars, !State) :-
    string.builder.format("%%\t%s -> %s\n",
        [s(mercury_var_to_string(VarTable, VarNamePrint, OldVar)),
        s(mercury_vars_to_string(VarTable, VarNamePrint, NewVars))],
        !State).

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

:- pred format_proc_deleted_callee_set(set(pred_proc_id)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_proc_deleted_callee_set(DeletedCallCalleeSet, !State) :-
    set.to_sorted_list(DeletedCallCalleeSet, DeletedCallCallees),
    (
        DeletedCallCallees = []
    ;
        DeletedCallCallees = [_ | _],
        string.builder.format("%% procedures called from deleted goals: %s\n",
            [s(string.string(DeletedCallCallees))], !State)
    ).

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

:- pred format_pred_proc_var_name_remap(var_name_source::in,
    map(prog_var, string)::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_pred_proc_var_name_remap(VarNameSrc, VarNameRemap, !State) :-
    map.to_assoc_list(VarNameRemap, VarNameRemapList),
    (
        VarNameRemapList = []
    ;
        VarNameRemapList = [VarNameRemapHead | VarNameRemapTail],
        string.builder.append_string("% var name remap: ", !State),
        format_var_name_remap(VarNameSrc,
            VarNameRemapHead, VarNameRemapTail, !State),
        string.builder.append_string("\n", !State)
    ).

:- pred format_var_name_remap(var_name_source::in,
    pair(prog_var, string)::in, list(pair(prog_var, string))::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_var_name_remap(VarNameSrc, Head, Tail, !State) :-
    Head = Var - NewName,
    VarName = mercury_var_to_string_src(VarNameSrc, print_name_and_num, Var),
    string.builder.format("%s -> %s", [s(VarName), s(NewName)], !State),
    (
        Tail = []
    ;
        Tail = [TailHead | TailTail],
        string.builder.append_string(", ", !State),
        format_var_name_remap(VarNameSrc, TailHead, TailTail, !State)
    ).

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

:- pred format_eff_trace_level(module_info::in, pred_info::in, proc_info::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_eff_trace_level(ModuleInfo, PredInfo, ProcInfo, !State) :-
    module_info_get_globals(ModuleInfo, Globals),
    globals.get_trace_level(Globals, TraceLevel),
    EffTraceLevel =
        eff_trace_level_for_proc(ModuleInfo, PredInfo, ProcInfo, TraceLevel),
    string.builder.format("%% effective trace level: %s\n",
        [s(eff_trace_level_dump(EffTraceLevel))], !State).

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

:- pred format_proc_arg_info(string::in, var_table::in, var_name_print::in,
    maybe(list(is_live))::in, set_of_progvar::in, maybe(list(arg_info))::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_proc_arg_info(DumpOptions, VarTable, VarNamePrint,
        MaybeArgLives, RegR_HeadVars, MaybeArgInfos, !State) :-
    (
        MaybeArgLives = yes(ArgLives),
        string.builder.format("%% arg lives: %s\n",
            [s(string.string(ArgLives))], !State)
    ;
        MaybeArgLives = no
    ),
    ( if set_of_var.is_non_empty(RegR_HeadVars) then
        string.builder.append_string("% reg_r headvars: ", !State),
        mercury_format_vars(VarTable, VarNamePrint,
            set_of_var.to_sorted_list(RegR_HeadVars),
            string.builder.handle, !State),
        string.builder.append_string("\n", !State)
    else
        true
    ),
    ( if
        string.contains_char(DumpOptions, 'A'),
        MaybeArgInfos = yes(ArgInfos)
    then
        string.builder.format("%% arg_infos: %s\n",
            [s(string.string(ArgInfos))], !State)
    else
        true
    ).

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

:- pred format_stack_slots(var_table::in, var_name_print::in, stack_slots::in,
    string.builder.state::di, string.builder.state::uo) is det.

format_stack_slots(VarTable, VarNamePrint, StackSlots, !State) :-
    map.to_assoc_list(StackSlots, VarSlotList0),
    VarSlotList = assoc_list.map_values_only(stack_slot_to_abs_locn,
        VarSlotList0),
    format_var_to_abs_locns(vns_var_table(VarTable), VarNamePrint,
        0u, VarSlotList, !State).

%---------------------------------------------------------------------------%
:- end_module hlds.hlds_out.hlds_out_pred.
%---------------------------------------------------------------------------%
