%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2003-2008 The University of Melbourne.
% Copyright (C) 2014-2015, 2019-2020, 2022, 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: name_mangle.m.
%
% This module defines routines for generating and/or outputting identifiers
% for modules, predicates/functions, and procedures in forms that are
% syntactically acceptable in all our target languages, meaning C, Java
% and MSIL.
%
% NOTE: some parts of the name mangling routines are defined in
% prog_foreign.m because they are required by the frontend of the compiler,
% for generating makefile fragments etc.
%
% Warning: any changes to the name mangling algorithms implemented in this
% module may also require changes to profiler/demangle.m, util/mdemangle.c and
% compiler/prog_foreign.m.
%
%-----------------------------------------------------------------------------%

:- module backend_libs.name_mangle.
:- interface.

:- import_module backend_libs.rtti.
:- import_module mdbcomp.
:- import_module mdbcomp.prim_data.
:- import_module mdbcomp.sym_name.

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

:- type maybe_add_label_prefix
    --->    do_not_add_label_prefix
    ;       add_label_prefix.

    % Get a proc label string (used by procs which are exported to C).
    % The boolean controls whether label_prefix is added to the string.
    %
:- func proc_label_to_c_string(maybe_add_label_prefix, proc_label) = string.

    % Succeed iff the given name or sym_name doesn't need mangling.
    %
:- pred name_doesnt_need_mangling(string::in) is semidet.
:- pred sym_name_doesnt_need_mangling(sym_name::in) is semidet.

    % Create a name for base_typeclass_info.
    %
:- func make_base_typeclass_info_name(tc_name, string) = string.

:- func make_base_typeclass_info_name_with_data_prefix(tc_name, string)
    = string.

    % To ensure that Mercury labels don't clash with C symbols, we
    % prefix them with the string returned by this function.
    %
:- func mercury_label_prefix = string.

    % To ensure that the names of global variables generated by the Mercury
    % compiler don't clash with C symbols, we prefix them with the string
    % returned by this function.
    %
:- func mercury_var_prefix = string.

    % All the C data structures we generate which are either fully static
    % or static after initialization should have one of these prefixes,
    % to ensure that Mercury global variables don't clash with C symbols.
    %
:- func mercury_data_prefix = string.
:- func mercury_scalar_common_array_prefix = string.
:- func mercury_vector_common_array_prefix = string.

    % All the C types we generate should have this prefix to ensure
    % that they don't clash with C symbols.
    %
:- func mercury_common_type_prefix = string.

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

:- implementation.

:- import_module hlds.
:- import_module hlds.pred_name.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_foreign.

:- import_module int.
:- import_module list.
:- import_module string.

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

proc_label_to_c_string(AddPrefix, ProcLabel) = ProcLabelString :-
    (
        ProcLabel = ordinary_proc_label(DefiningModule, PredOrFunc, PredModule,
            PredName, Arity, ModeNum),
        LabelName = make_pred_or_func_name(DefiningModule, PredOrFunc,
            PredModule, PredName, Arity, AddPrefix),
        (
            PredOrFunc = pf_function,
            OrigArity = Arity - 1
        ;
            PredOrFunc = pf_predicate,
            OrigArity = Arity
        ),
        ProcLabelString = string.format("%s_%d_%d",
            [s(LabelName), i(OrigArity), i(ModeNum)])
    ;
        ProcLabel = special_proc_label(Module, SpecialPredId, TypeModule,
            TypeName, TypeArity, ModeNum),
        % For a special proc, output a label of the form:
        % mercury____<PredName>___<TypeModule>__<TypeName>_<TypeArity>_<Mode>

        % Figure out the LabelName.
        DummyArity = -1,    % not used by make_pred_or_func_name.
        TypeCtor = type_ctor(qualified(TypeModule, TypeName), TypeArity),
        PredName = uci_pred_name(SpecialPredId, TypeCtor),
        LabelName = make_pred_or_func_name(unqualified(""), pf_predicate,
            unqualified(""), PredName, DummyArity, AddPrefix),

        % Mangle all the relevant names.
        MangledModule = sym_name_mangle(Module),
        MangledTypeModule = sym_name_mangle(TypeModule),
        MangledTypeName = name_mangle(TypeName),

        % Module-qualify the type name.
        % To handle locally produced unification preds for imported types,
        % we need to qualify it with both the module name of the
        % type, and also (if it is different) the module name of the
        % current module.
        QualifiedMangledTypeName = qualify_name(MangledTypeModule,
            MangledTypeName),
        FullyQualifiedMangledTypeName = maybe_qualify_name(MangledModule,
            QualifiedMangledTypeName),

        % Join it all together.
        ProcLabelString = string.format("%s_%s_%d_%d",
            [s(LabelName), s(FullyQualifiedMangledTypeName),
            i(TypeArity), i(ModeNum)])
    ).

    % Make a name identifying a predicate or a function, given the
    % defining module, predicate or function indicator, declaring module,
    % predicate name, arity, and whether or not to add the
    % mercury_label_prefix.
    %
:- func make_pred_or_func_name(module_name, pred_or_func, module_name, string,
    arity, maybe_add_label_prefix) = string.

make_pred_or_func_name(DefiningModule, PredOrFunc, DeclaringModule,
        Name0, Arity, AddPrefix) = LabelName :-
    % WARNING: any changes to the name mangling algorithm here may also
    % require changes to profiler/demangle.m, util/mdemangle.c and
    % compiler/prog_foreign.m.

    DeclaringModuleName = sym_name_mangle(DeclaringModule),
    DefiningModuleName = sym_name_mangle(DefiningModule),
    ( if should_not_module_qualify_name(Name0, Arity) then
        LabelName0 = Name0
    else
        LabelName0 = qualify_name(DeclaringModuleName, Name0)
    ),
    % If this is a specialized version of a predicate defined
    % in some other module, then it needs both module prefixes.
    ( if DefiningModule \= DeclaringModule then
        LabelName1 = DefiningModuleName ++ "__" ++ LabelName0
    else
        LabelName1 = LabelName0
    ),
    LabelName2 = name_mangle(LabelName1),
    (
        PredOrFunc = pf_function,
        LabelName3 = "fn__" ++ LabelName2
    ;
        PredOrFunc = pf_predicate,
        LabelName3 = LabelName2
    ),
    (
        AddPrefix = add_label_prefix,
        LabelName = mercury_label_prefix ++ LabelName3
    ;
        AddPrefix = do_not_add_label_prefix,
        LabelName = LabelName3
    ).

    % Define the conditions for which labels are printed
    % without module qualification.
    %
:- pred should_not_module_qualify_name(string::in, arity::in) is semidet.

should_not_module_qualify_name(Name, Arity) :-
    (
        Name = "main",
        Arity = 2
    ;
        string.prefix(Name, "__")
    ).

name_doesnt_need_mangling(Name) :-
    string.is_all_alnum_or_underscore(Name),
    not string.append("f_", _Suffix, Name).

sym_name_doesnt_need_mangling(unqualified(Name)) :-
    name_doesnt_need_mangling(Name).
sym_name_doesnt_need_mangling(qualified(ModuleName, PlainName)) :-
    sym_name_doesnt_need_mangling(ModuleName),
    name_doesnt_need_mangling(PlainName).

    % Produces a string of the form Module__Name, unless Module__
    % is already a prefix of Name.
    %
:- func maybe_qualify_name(string, string) = string.

maybe_qualify_name(Module0, Name0) = Name :-
    string.append(Module0, "__", UnderscoresModule),
    ( if string.append(UnderscoresModule, _, Name0) then
        Name = Name0
    else
        string.append(UnderscoresModule, Name0, Name)
    ).

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

make_base_typeclass_info_name(TCName, TypeNames) = Str :-
    TCName = tc_name(ModuleName, ClassName, ClassArity),
    ClassSym = qualified(ModuleName, ClassName),
    MangledClassString = sym_name_mangle(ClassSym),
    MangledTypeNames = name_mangle(TypeNames),
    Str = string.format("base_typeclass_info_%s__arity%d__%s",
        [s(MangledClassString), i(ClassArity), s(MangledTypeNames)]).

make_base_typeclass_info_name_with_data_prefix(TCName, TypeNames) = Str :-
    Str = mercury_data_prefix ++
        make_base_typeclass_info_name(TCName, TypeNames).

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

mercury_label_prefix = "mercury__".

mercury_var_prefix = "mercury_var_".

mercury_data_prefix = "mercury_data_".
mercury_scalar_common_array_prefix = "mercury_common_".
mercury_vector_common_array_prefix = "mercury_vector_common_".

mercury_common_type_prefix = "mercury_type_".

%-----------------------------------------------------------------------------%
:- end_module backend_libs.name_mangle.
%-----------------------------------------------------------------------------%
