%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2003-2008, 2010-2012 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: proc_label.m.
% Main author: zs.
%
% This file defines backend-independent identifiers for procedures that a
% backend can use as the basis for the names of the labels or functions
% implementing those procedures. It also has functions for creating these
% identifiers.

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

:- module backend_libs.proc_label.
:- interface.

:- import_module hlds.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module hlds.hlds_rtti.
:- import_module mdbcomp.
:- import_module mdbcomp.prim_data.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.

    % Return the id of the procedure specified by the rtti_proc_label.
    %
:- func make_proc_label_from_rtti(rtti_proc_label) = proc_label.

    % Return the id of the specified procedure.
    %
:- func make_proc_label(module_info, pred_id, proc_id) = proc_label.

    % Return the id of the specified mode of the unification procedure
    % for the given type.

:- func make_uni_label(module_info, type_ctor, proc_id) = proc_label.

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

:- implementation.

:- import_module hlds.pred_name.
:- import_module mdbcomp.builtin_modules.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.prog_type_test.

:- import_module bool.
:- import_module require.
:- import_module string.

make_proc_label_from_rtti(RttiProcLabel) = ProcLabel :-
    RttiProcLabel = rtti_proc_label(PredOrFunc, ThisModule,
        PredModule, PredName, PredFormArity, _ArgTypes, _PredId, ProcId,
        _ProcHeadVarsWithNames, _ArgModes, _CodeModel,
        PredIsImported, _PredIsPseudoImported, Origin,
        _ProcIsExported, _ProcIsImported),
    ProcLabel = do_make_proc_label(PredOrFunc, ThisModule, PredModule,
        PredName, PredFormArity, ProcId, PredIsImported, Origin).

make_proc_label(ModuleInfo, PredId, ProcId) = ProcLabel :-
    module_info_get_name(ModuleInfo, ThisModule),
    module_info_pred_proc_info(ModuleInfo, PredId, ProcId, PredInfo,
        _ProcInfo),
    PredOrFunc = pred_info_is_pred_or_func(PredInfo),
    PredModule = pred_info_module(PredInfo),
    PredName = pred_info_name(PredInfo),
    PredFormArity = pred_info_pred_form_arity(PredInfo),
    PredIsImported = (if pred_info_is_imported(PredInfo) then yes else no),
    pred_info_get_origin(PredInfo, Origin),

    ProcLabel = do_make_proc_label(PredOrFunc, ThisModule, PredModule,
        PredName, PredFormArity, ProcId, PredIsImported, Origin).

:- func do_make_proc_label(pred_or_func, module_name, module_name,
    string, pred_form_arity, proc_id, bool, pred_origin) = proc_label.

do_make_proc_label(PredOrFunc, ThisModule, PredModule, PredName, PredFormArity,
        ProcId, PredIsImported, Origin) = ProcLabel :-
    ( if Origin = origin_compiler(made_for_uci(SpecialPred, TypeCtor)) then
        ( if
            % All type_ctors other than tuples here should be module qualified,
            % since builtin types are handled separately in polymorphism.m.
            TypeCtor = type_ctor(TypeCtorSymName, TypeArity),
            (
                TypeCtorSymName = unqualified(TypeName),
                type_ctor_is_tuple(TypeCtor),
                TypeModule = mercury_public_builtin_module
            ;
                TypeCtorSymName = qualified(TypeModule, TypeName)
            )
        then
            ( if
                ThisModule \= TypeModule,
                SpecialPred = spec_pred_unify,
                not hlds_pred.in_in_unification_proc_id(ProcId)
            then
                DefiningModule = ThisModule
            else
                DefiningModule = TypeModule
            ),
            proc_id_to_int(ProcId, ProcIdInt),
            ProcLabel = special_proc_label(DefiningModule, SpecialPred,
                TypeModule, TypeName, TypeArity, ProcIdInt)
        else
            unexpected($pred,
                "cannot make label for special pred `" ++ PredName ++ "'")
        )
    else
        ProcLabel = make_user_proc_label(ThisModule, PredIsImported,
            PredOrFunc, PredModule, PredName, PredFormArity, ProcId)
    ).

    % make_user_proc_label(ModuleName, PredIsImported,
    %   PredOrFunc, ModuleName, PredName, Arity, ProcId) = Label:
    %
    % Make a proc_label for a user-defined procedure.
    %
    % The PredIsImported argument should be the result of
    % calling pred_info_is_imported.
    %
:- func make_user_proc_label(module_name, bool, pred_or_func,
    module_name, string, pred_form_arity, proc_id) = proc_label.

make_user_proc_label(ThisModule, PredIsImported, PredOrFunc, PredModule,
        PredName, PredFormArity, ProcId) = ProcLabel :-
    ( if
        % Work out which module supplies the code for the predicate.
        ThisModule \= PredModule,
        PredIsImported = no
    then
        % This predicate is a specialized version of a pred from a `.opt' file.
        DefiningModule = ThisModule
    else
        DefiningModule = PredModule
    ),
    PredFormArity = pred_form_arity(PredFormArityInt),
    proc_id_to_int(ProcId, ProcIdInt),
    ProcLabel = ordinary_proc_label(DefiningModule, PredOrFunc,
        PredModule, PredName, PredFormArityInt, ProcIdInt).

make_uni_label(ModuleInfo, TypeCtor, UniModeNum) = ProcLabel :-
    module_info_get_name(ModuleInfo, ModuleName),
    ( if TypeCtor = type_ctor(qualified(TypeModule, TypeName), Arity) then
        ( if hlds_pred.in_in_unification_proc_id(UniModeNum) then
            Module = TypeModule
        else
            Module = ModuleName
        ),
        proc_id_to_int(UniModeNum, UniModeNumInt),
        ProcLabel = special_proc_label(Module, spec_pred_unify, TypeModule,
            TypeName, Arity, UniModeNumInt)
    else
        unexpected($pred, "unqualified type_ctor")
    ).

%-----------------------------------------------------------------------------%
:- end_module backend_libs.proc_label.
%-----------------------------------------------------------------------------%
