%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1993-2012 The University of Melbourne.
% Copyright (C) 2014-2018 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: pre_typecheck.m.
%
% Prepare the clauses in the HLDS for typechecking. This involves three steps.
%
% - We invoke goal_path.m to fill in the goal_id slots of goals.
%   We record goal_ids in constraint_ids, which allows us later to map
%   constraints back to the goal being constrained.
%
% - We supply field access functions with their default clauses, if the
%   user has not explicitly provided clauses for them. This pass is the
%   compiler's first chance to decide that there won't be any user-given
%   clauses coming, and that therefore the default clauses should be
%   the actual clauses.
%
% - The parser puts clauses into superhomogeneous form by filling the heads
%   of clauses with compiler-generated variables with compiler-generated names
%   such as HeadVar__1. We invoke headvar_names.m to give these variables
%   their user-provided names, provided the clauses agree on the names.
%   This is important for making any type error messages about these variables
%   easier to understand.
%
%---------------------------------------------------------------------------%

:- module check_hlds.pre_typecheck.
:- interface.

:- import_module hlds.
:- import_module hlds.hlds_module.

:- pred prepare_for_typecheck_module(module_info::in, module_info::out) is det.

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

:- implementation.

:- import_module hlds.goal_path.
:- import_module hlds.headvar_names.
:- import_module hlds.hlds_args.
:- import_module hlds.hlds_clauses.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_pred.
:- import_module hlds.make_goal.
:- import_module hlds.status.
:- import_module libs.
:- import_module libs.globals.
:- import_module mdbcomp.
:- import_module mdbcomp.goal_path.
:- import_module mdbcomp.prim_data.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_util.
:- import_module parse_tree.set_of_var.

:- import_module assoc_list.
:- import_module bool.
:- import_module list.
:- import_module map.
:- import_module pair.
:- import_module set_tree234.
:- import_module term.

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

prepare_for_typecheck_module(!ModuleInfo) :-
    module_info_get_valid_pred_id_set(!.ModuleInfo, OrigValidPredIdSet),
    module_info_get_pred_id_table(!.ModuleInfo, PredIdTable0),
    map.to_sorted_assoc_list(PredIdTable0, PredIdsInfos0),

    prepare_for_typecheck(!.ModuleInfo, OrigValidPredIdSet,
        PredIdsInfos0, PredIdsInfos),

    map.from_sorted_assoc_list(PredIdsInfos, PredIdTable),
    module_info_set_pred_id_table(PredIdTable, !ModuleInfo).

:- pred prepare_for_typecheck(module_info::in, set_tree234(pred_id)::in,
    assoc_list(pred_id, pred_info)::in, assoc_list(pred_id, pred_info)::out)
    is det.

prepare_for_typecheck(_, _, [], []).
prepare_for_typecheck(ModuleInfo, ValidPredIdSet,
        [PredIdInfo0 | PredIdsInfos0], [PredIdInfo | PredIdsInfos]) :-
    some [!PredInfo] (
        PredIdInfo0 = PredId - !:PredInfo,
        ( if set_tree234.contains(ValidPredIdSet, PredId) then
            % Goal ids are used to identify typeclass constraints.
            pred_info_get_clauses_info(!.PredInfo, GoalIdClausesInfo0),
            fill_goal_id_slots_in_clauses(ModuleInfo, _ContainingGoalMap,
                GoalIdClausesInfo0, GoalIdClausesInfo),
            pred_info_set_clauses_info(GoalIdClausesInfo, !PredInfo),

            maybe_add_field_access_function_clause(ModuleInfo, !PredInfo),

            module_info_get_globals(ModuleInfo, Globals),
            maybe_improve_headvar_names(Globals, !PredInfo),
            PredIdInfo = PredId - !.PredInfo
        else
            PredIdInfo = PredIdInfo0
        )
    ),
    prepare_for_typecheck(ModuleInfo, ValidPredIdSet,
        PredIdsInfos0, PredIdsInfos).

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

    % For a field access function for which the user has supplied
    % a declaration but no clauses, add a clause
    % 'foo :='(X, Y) = 'foo :='(X, Y).
    % As for the default clauses added for builtins, this is not a recursive
    % call -- post_typecheck.m will expand the body into unifications.
    %
:- pred maybe_add_field_access_function_clause(module_info::in,
    pred_info::in, pred_info::out) is det.

maybe_add_field_access_function_clause(ModuleInfo, !PredInfo) :-
    pred_info_get_status(!.PredInfo, PredStatus),
    pred_info_get_clauses_info(!.PredInfo, ClausesInfo0),
    clauses_info_get_clauses_rep(ClausesInfo0, ClausesRep0, _ItemNumbers0),
    ( if
        pred_info_is_field_access_function(ModuleInfo, !.PredInfo),
        clause_list_is_empty(ClausesRep0) = yes,
        pred_status_defined_in_this_module(PredStatus) = yes
    then
        clauses_info_get_headvars(ClausesInfo0, HeadVars),
        proc_arg_vector_to_func_args(HeadVars, FuncArgs, FuncRetVal),
        pred_info_get_context(!.PredInfo, Context),
        FuncModule = pred_info_module(!.PredInfo),
        FuncName = pred_info_name(!.PredInfo),
        pred_info_get_orig_arity(!.PredInfo, PredFormArity),
        user_arity_pred_form_arity(pf_function, UserArity, PredFormArity),
        UserArity = user_arity(UserArityInt),
        FuncSymName = qualified(FuncModule, FuncName),
        FuncDuCtor =
            du_ctor(FuncSymName, UserArityInt, cons_id_dummy_type_ctor),
        FuncConsId = du_data_ctor(FuncDuCtor),
        FuncRHS = rhs_functor(FuncConsId, is_not_exist_constr, FuncArgs),
        create_pure_atomic_complicated_unification(FuncRetVal,
            FuncRHS, Context, umc_explicit, [], Goal0),
        Goal0 = hlds_goal(GoalExpr, GoalInfo0),
        NonLocals = set_of_var.list_to_set(proc_arg_vector_to_list(HeadVars)),
        goal_info_set_nonlocals(NonLocals, GoalInfo0, GoalInfo),
        Goal = hlds_goal(GoalExpr, GoalInfo),
        Clause = clause(all_modes, Goal, impl_lang_mercury, Context, []),
        set_clause_list([Clause], ClausesRep),
        ItemNumbers = init_clause_item_numbers_comp_gen,
        clauses_info_set_clauses_rep(ClausesRep, ItemNumbers,
            ClausesInfo0, ClausesInfo),
        pred_info_update_goal_type(np_goal_type_clause, !PredInfo),
        pred_info_set_clauses_info(ClausesInfo, !PredInfo),
        pred_info_get_markers(!.PredInfo, Markers0),
        add_marker(marker_calls_are_fully_qualified, Markers0, Markers),
        pred_info_set_markers(Markers, !PredInfo)
    else
        true
    ).

%---------------------------------------------------------------------------%
:- end_module check_hlds.pre_typecheck.
%---------------------------------------------------------------------------%
