Files
mercury/compiler/unify_proc.m
Peter Ross 77a1261d3b Merge the foreign_type pragma changes from the dotnet branch to the main
Estimated hours taken: 10
Branches: main

Merge the foreign_type pragma changes from the dotnet branch to the main
branch, plus do some more development work to generalise the change.

compiler/prog_data.m:
    Add a type to hold the data from parsing a pragma foreign_type decl.

compiler/prog_io_pragma.m:
    Parse the pragma foreign_type.  This code is currently commented
    out, while we decide on the syntax.

compiler/hlds_data.m:
    Add a new alternative to hlds_type_body where the body of the type
    is a foreign type.

compiler/make_hlds.m:
    Place the foreign_type pragmas into the HLDS.

compiler/foreign.m:
    Implement to_type_string which replaces export__type_to_type_string,
    unlike export__type_to_type_string foreign__to_type_string takes an
    argument specifying which language the representation is meant to be
    in.  to_type_string also needs to take a module_info to handle
    foreign_types correctly.  To avoid the need for the module_info to
    be passed around the MLDS backend we provide a new type
    exported_type which provides enough information for an alternate
    version of to_type_string to be called.

compiler/export.m:
    Delete export__type_to_type_string.

compiler/llds.m:
    Since foreign__to_type_string needs a module_info, we add a new
    field to pragma_c_arg_decl which is the result of calling
    foreign__to_type_string.  This avoids threading the module_info
    around various llds passes.

compiler/mlds.m:
    Record with in the mercury_type the exported_type, this avoids
    passing the module_info around the MLDS backend.
    Also add the foreign_type alternative to mlds__type.
    Update mercury_type_to_mlds_type so that it handles types which are
    foreign types.

compiler/mlds_to_il.m:
    Convert a mlds__foreign_type into an ilds__type.

compiler/ilds.m:
    The CLR spec requires that System.Object and System.String be
    treated specially in the IL assembly so add them as simple types.

compiler/ilasm.m:
    Before outputting a class name into the IL assembly check whether it
    it can be simplified to a builtin type, and if so output that name
    instead as required by the ECMA spec.
    Changes for the addition of string and object as simple types.

doc/reference_manual.texi:
    Document the new pragma, this is currently commented out because it
    refers to syntax that has not yet been finalised.

compiler/fact_table.m:
compiler/llds_out.m:
compiler/ml_code_gen.m:
compiler/ml_code_util.m:
compiler/ml_simplify_switch.m:
compiler/ml_switch_gen.m:
compiler/ml_unify_gen.m:
compiler/mlds_to_c.m:
compiler/mlds_to_csharp.m:
compiler/mlds_to_gcc.m:
compiler/mlds_to_java.m:
compiler/mlds_to_mcpp.m:
compiler/pragma_c_gen.m:
compiler/rtti_to_mlds.m:
    Changes to handle using foreign__to_type_string.

compiler/hlds_out.m:
compiler/intermod.m:
compiler/magic_util.m:
compiler/ml_type_gen.m:
compiler/recompilation_usage.m:
compiler/recompilation_version.m:
compiler/term_util.m:
compiler/type_ctor_info.m:
compiler/unify_proc.m:
    Changes to handle the new hlds_type_body.

compiler/mercury_to_mercury.m:
    Output the pragma foreign_type declaration.

compiler/module_qual.m:
    Qualify the pragma foreign_type declarations.

compiler/modules.m:
    Pragma foreign_type is allowed in the interface.
2001-10-24 13:34:41 +00:00

1742 lines
60 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1994-2001 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.
%-----------------------------------------------------------------------------%
%
% unify_proc.m:
%
% This module encapsulates access to the proc_requests table,
% and constructs the clauses for out-of-line complicated
% unification procedures.
% It also generates the code for other compiler-generated type-specific
% predicates such as compare/3.
%
% During mode analysis, we notice each different complicated unification
% that occurs. For each one we add a new mode to the out-of-line
% unification predicate for that type, and we record in the `proc_requests'
% table that we need to eventually modecheck that mode of the unification
% procedure.
%
% After we've done mode analysis for all the ordinary predicates, we then
% do mode analysis for the out-of-line unification procedures. Note that
% unification procedures may call other unification procedures which have
% not yet been encountered, causing new entries to be added to the
% proc_requests table. We store the entries in a queue and continue the
% process until the queue is empty.
%
% The same queuing mechanism is also used for procedures created by
% mode inference during mode analysis and unique mode analysis.
%
% Currently if the same complicated unification procedure is called by
% different modules, each module will end up with a copy of the code for
% that procedure. In the long run it would be desireable to either delay
% generation of complicated unification procedures until link time (like
% Cfront does with C++ templates) or to have a smart linker which could
% merge duplicate definitions (like Borland C++). However the amount of
% code duplication involved is probably very small, so it's definitely not
% worth worrying about right now.
% XXX What about complicated unification of an abstract type in a partially
% instantiated mode? Currently we don't implement it correctly. Probably
% it should be disallowed, but we should issue a proper error message.
%-----------------------------------------------------------------------------%
:- module unify_proc.
:- interface.
:- import_module hlds_module, hlds_pred, hlds_goal, hlds_data.
:- import_module mode_info, prog_data, special_pred.
:- import_module bool, std_util, io, list.
:- type proc_requests.
:- type unify_proc_id == pair(type_id, uni_mode).
% Initialize the proc_requests table.
:- pred unify_proc__init_requests(proc_requests::out) is det.
% Add a new request for a unification procedure to the
% proc_requests table.
:- pred unify_proc__request_unify(unify_proc_id::in, inst_varset::in,
determinism::in, prog_context::in, module_info::in, module_info::out)
is det.
% Add a new request for a procedure (not necessarily a unification)
% to the request queue. Return the procedure's newly allocated
% proc_id. (This is used by unique_modes.m.)
:- pred unify_proc__request_proc(pred_id::in, list(mode)::in, inst_varset::in,
maybe(list(is_live))::in, maybe(determinism)::in, prog_context::in,
module_info::in, proc_id::out, module_info::out) is det.
% unify_proc__add_lazily_generated_unify_pred(TypeId,
% UnifyPredId_for_Type, ModuleInfo0, ModuleInfo).
%
% For most imported unification procedures, we delay
% generating declarations and clauses until we know
% whether they are actually needed because there
% is a complicated unification involving the type.
% This predicate is exported for use by higher_order.m
% when it is specializing calls to unify/2.
:- pred unify_proc__add_lazily_generated_unify_pred(type_id::in,
pred_id::out, module_info::in, module_info::out) is det.
% unify_proc__add_lazily_generated_compare_pred_decl(TypeId,
% ComparePredId_for_Type, ModuleInfo0, ModuleInfo).
%
% Add declarations, but not clauses, for a compare or index predicate.
:- pred unify_proc__add_lazily_generated_compare_pred_decl(type_id::in,
pred_id::out, module_info::in, module_info::out) is det.
% Do mode analysis of the queued procedures.
% If the first argument is `unique_mode_check',
% then also go on and do full determinism analysis and unique mode
% analysis on them as well.
% The pred_table arguments are used to store copies of the
% procedure bodies before unique mode analysis, so that
% we can restore them before doing the next analysis pass.
:- pred modecheck_queued_procs(how_to_check_goal::in,
pred_table::in, module_info::in, pred_table::out, module_info::out,
bool::out, io__state::di, io__state::uo) is det.
% Given the type and mode of a unification, look up the
% mode number for the unification proc.
:- pred unify_proc__lookup_mode_num(module_info::in, type_id::in, uni_mode::in,
determinism::in, proc_id::out) is det.
% Generate the clauses for one of the compiler-generated
% special predicates (compare/3, index/3, unify, etc.)
:- pred unify_proc__generate_clause_info(special_pred_id::in, (type)::in,
hlds_type_body::in, prog_context::in, module_info::in,
clauses_info::out) is det.
% This number gives the maximum number of constructors in a type
% whose compare procedure can be specialized, and whose compare
% procedure therefore does need an index procedure on that type.
:- func unify_proc__max_exploited_compare_spec_value = int.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module globals, options.
:- import_module code_util, code_info, type_util.
:- import_module mercury_to_mercury, hlds_out.
:- import_module make_hlds, polymorphism, post_typecheck, prog_util, prog_out.
:- import_module quantification, clause_to_proc, term, varset.
:- import_module modes, mode_util, inst_match, instmap, (inst).
:- import_module switch_detection, cse_detection, det_analysis, unique_modes.
:- import_module recompilation.
:- import_module tree, map, set, queue, int, string, require, assoc_list.
% We keep track of all the complicated unification procs we need
% by storing them in the proc_requests structure.
% For each unify_proc_id (i.e. type & mode), we store the proc_id
% (mode number) of the unification procedure which corresponds to
% that mode.
:- type unify_req_map == map(unify_proc_id, proc_id).
:- type req_queue == queue(pred_proc_id).
:- type proc_requests --->
proc_requests(
unify_req_map, % the assignment of proc_id
% numbers to unify_proc_ids
req_queue % queue of procs we still need
% to generate code for
).
%-----------------------------------------------------------------------------%
unify_proc__init_requests(Requests) :-
map__init(UnifyReqMap),
queue__init(ReqQueue),
Requests = proc_requests(UnifyReqMap, ReqQueue).
%-----------------------------------------------------------------------------%
% Boring access predicates
:- pred unify_proc__get_unify_req_map(proc_requests, unify_req_map).
:- mode unify_proc__get_unify_req_map(in, out) is det.
:- pred unify_proc__get_req_queue(proc_requests, req_queue).
:- mode unify_proc__get_req_queue(in, out) is det.
:- pred unify_proc__set_unify_req_map(proc_requests, unify_req_map,
proc_requests).
:- mode unify_proc__set_unify_req_map(in, in, out) is det.
:- pred unify_proc__set_req_queue(proc_requests, req_queue, proc_requests).
:- mode unify_proc__set_req_queue(in, in, out) is det.
unify_proc__get_unify_req_map(proc_requests(UnifyReqMap, _), UnifyReqMap).
unify_proc__get_req_queue(proc_requests(_, ReqQueue), ReqQueue).
unify_proc__set_unify_req_map(proc_requests(_, B), UnifyReqMap,
proc_requests(UnifyReqMap, B)).
unify_proc__set_req_queue(proc_requests(A, _), ReqQueue,
proc_requests(A, ReqQueue)).
%-----------------------------------------------------------------------------%
unify_proc__lookup_mode_num(ModuleInfo, TypeId, UniMode, Det, Num) :-
( unify_proc__search_mode_num(ModuleInfo, TypeId, UniMode, Det, Num1) ->
Num = Num1
;
error("unify_proc.m: unify_proc__search_num failed")
).
:- pred unify_proc__search_mode_num(module_info, type_id, uni_mode, determinism,
proc_id).
:- mode unify_proc__search_mode_num(in, in, in, in, out) is semidet.
% Given the type, mode, and determinism of a unification, look up the
% mode number for the unification proc.
% We handle semidet unifications with mode (in, in) specially - they
% are always mode zero. Similarly for unifications of `any' insts.
% (It should be safe to use the `in, in' mode for any insts, since
% we assume that `ground' and `any' have the same representation.)
% For unreachable unifications, we also use mode zero.
unify_proc__search_mode_num(ModuleInfo, TypeId, UniMode, Determinism, ProcId) :-
UniMode = (XInitial - YInitial -> _Final),
(
Determinism = semidet,
inst_is_ground_or_any(ModuleInfo, XInitial),
inst_is_ground_or_any(ModuleInfo, YInitial)
->
hlds_pred__in_in_unification_proc_id(ProcId)
;
XInitial = not_reached
->
hlds_pred__in_in_unification_proc_id(ProcId)
;
YInitial = not_reached
->
hlds_pred__in_in_unification_proc_id(ProcId)
;
module_info_get_proc_requests(ModuleInfo, Requests),
unify_proc__get_unify_req_map(Requests, UnifyReqMap),
map__search(UnifyReqMap, TypeId - UniMode, ProcId)
).
%-----------------------------------------------------------------------------%
unify_proc__request_unify(UnifyId, InstVarSet, Determinism, Context,
ModuleInfo0, ModuleInfo) :-
UnifyId = TypeId - UnifyMode,
%
% Generating a unification procedure for a type uses its body.
%
module_info_get_maybe_recompilation_info(ModuleInfo0, MaybeRecompInfo0),
( MaybeRecompInfo0 = yes(RecompInfo0) ->
recompilation__record_used_item(type_body,
TypeId, TypeId, RecompInfo0, RecompInfo),
module_info_set_maybe_recompilation_info(ModuleInfo0,
yes(RecompInfo), ModuleInfo1)
;
ModuleInfo1 = ModuleInfo0
),
%
% check if this unification has already been requested, or
% if the proc is hand defined.
%
(
(
unify_proc__search_mode_num(ModuleInfo1, TypeId,
UnifyMode, Determinism, _)
;
TypeId = TypeName - _TypeArity,
TypeName = qualified(TypeModuleName, _),
module_info_name(ModuleInfo1, ModuleName),
ModuleName = TypeModuleName,
module_info_types(ModuleInfo1, TypeTable),
map__search(TypeTable, TypeId, TypeDefn),
hlds_data__get_type_defn_body(TypeDefn, TypeBody),
TypeBody = abstract_type
;
type_id_has_hand_defined_rtti(TypeId)
)
->
ModuleInfo = ModuleInfo1
;
%
% lookup the pred_id for the unification procedure
% that we are going to generate
%
module_info_get_special_pred_map(ModuleInfo1, SpecialPredMap),
( map__search(SpecialPredMap, unify - TypeId, PredId0) ->
PredId = PredId0,
ModuleInfo2 = ModuleInfo1
;
% We generate unification predicates for most
% imported types lazily, so add the declarations
% and clauses now.
unify_proc__add_lazily_generated_unify_pred(TypeId,
PredId, ModuleInfo1, ModuleInfo2)
),
% convert from `uni_mode' to `list(mode)'
UnifyMode = ((X_Initial - Y_Initial) -> (X_Final - Y_Final)),
ArgModes0 = [(X_Initial -> X_Final), (Y_Initial -> Y_Final)],
% for polymorphic types, add extra modes for the type_infos
in_mode(InMode),
TypeId = _ - TypeArity,
list__duplicate(TypeArity, InMode, TypeInfoModes),
list__append(TypeInfoModes, ArgModes0, ArgModes),
ArgLives = no, % XXX ArgLives should be part of the UnifyId
unify_proc__request_proc(PredId, ArgModes, InstVarSet, ArgLives,
yes(Determinism), Context, ModuleInfo2,
ProcId, ModuleInfo3),
%
% save the proc_id for this unify_proc_id
%
module_info_get_proc_requests(ModuleInfo3, Requests0),
unify_proc__get_unify_req_map(Requests0, UnifyReqMap0),
map__set(UnifyReqMap0, UnifyId, ProcId, UnifyReqMap),
unify_proc__set_unify_req_map(Requests0, UnifyReqMap, Requests),
module_info_set_proc_requests(ModuleInfo3, Requests,
ModuleInfo)
).
unify_proc__request_proc(PredId, ArgModes, InstVarSet, ArgLives, MaybeDet,
Context, ModuleInfo0, ProcId, ModuleInfo) :-
%
% create a new proc_info for this procedure
%
module_info_preds(ModuleInfo0, Preds0),
map__lookup(Preds0, PredId, PredInfo0),
list__length(ArgModes, Arity),
DeclaredArgModes = no,
add_new_proc(PredInfo0, InstVarSet, Arity, ArgModes, DeclaredArgModes,
ArgLives, MaybeDet, Context, address_is_not_taken,
PredInfo1, ProcId),
%
% copy the clauses for the procedure from the pred_info to the
% proc_info, and mark the procedure as one that cannot
% be processed yet
%
pred_info_procedures(PredInfo1, Procs1),
pred_info_clauses_info(PredInfo1, ClausesInfo),
map__lookup(Procs1, ProcId, ProcInfo0),
proc_info_set_can_process(ProcInfo0, no, ProcInfo1),
copy_clauses_to_proc(ProcId, ClausesInfo, ProcInfo1, ProcInfo2),
proc_info_goal(ProcInfo2, Goal0),
set_goal_contexts(Context, Goal0, Goal),
proc_info_set_goal(ProcInfo2, Goal, ProcInfo),
map__det_update(Procs1, ProcId, ProcInfo, Procs2),
pred_info_set_procedures(PredInfo1, Procs2, PredInfo2),
map__det_update(Preds0, PredId, PredInfo2, Preds2),
module_info_set_preds(ModuleInfo0, Preds2, ModuleInfo2),
%
% insert the pred_proc_id into the request queue
%
module_info_get_proc_requests(ModuleInfo2, Requests0),
unify_proc__get_req_queue(Requests0, ReqQueue0),
queue__put(ReqQueue0, proc(PredId, ProcId), ReqQueue),
unify_proc__set_req_queue(Requests0, ReqQueue, Requests),
module_info_set_proc_requests(ModuleInfo2, Requests, ModuleInfo).
%-----------------------------------------------------------------------------%
% XXX these belong in modes.m
modecheck_queued_procs(HowToCheckGoal, OldPredTable0, ModuleInfo0,
OldPredTable, ModuleInfo, Changed) -->
{ module_info_get_proc_requests(ModuleInfo0, Requests0) },
{ unify_proc__get_req_queue(Requests0, RequestQueue0) },
(
{ queue__get(RequestQueue0, PredProcId, RequestQueue1) }
->
{ unify_proc__set_req_queue(Requests0, RequestQueue1,
Requests1) },
{ module_info_set_proc_requests(ModuleInfo0, Requests1,
ModuleInfo1) },
%
% Check that the procedure is valid (i.e. type-correct),
% before we attempt to do mode analysis on it.
% This check is necessary to avoid internal errors
% caused by doing mode analysis on type-incorrect code.
% XXX inefficient! This is O(N*M).
%
{ PredProcId = proc(PredId, _ProcId) },
{ module_info_predids(ModuleInfo1, ValidPredIds) },
( { list__member(PredId, ValidPredIds) } ->
queued_proc_progress_message(PredProcId,
HowToCheckGoal, ModuleInfo1),
modecheck_queued_proc(HowToCheckGoal, PredProcId,
OldPredTable0, ModuleInfo1,
OldPredTable2, ModuleInfo2, Changed1)
;
{ OldPredTable2 = OldPredTable0 },
{ ModuleInfo2 = ModuleInfo1 },
{ Changed1 = no }
),
modecheck_queued_procs(HowToCheckGoal, OldPredTable2,
ModuleInfo2, OldPredTable, ModuleInfo, Changed2),
{ bool__or(Changed1, Changed2, Changed) }
;
{ OldPredTable = OldPredTable0 },
{ ModuleInfo = ModuleInfo0 },
{ Changed = no }
).
:- pred queued_proc_progress_message(pred_proc_id, how_to_check_goal,
module_info, io__state, io__state).
:- mode queued_proc_progress_message(in, in, in, di, uo) is det.
queued_proc_progress_message(PredProcId, HowToCheckGoal, ModuleInfo) -->
globals__io_lookup_bool_option(very_verbose, VeryVerbose),
( { VeryVerbose = yes } ->
%
% print progress message
%
( { HowToCheckGoal = check_unique_modes } ->
io__write_string(
"% Analyzing modes, determinism, and unique-modes for\n% ")
;
io__write_string("% Mode-analyzing ")
),
{ PredProcId = proc(PredId, ProcId) },
hlds_out__write_pred_proc_id(ModuleInfo, PredId, ProcId),
io__write_string("\n")
/*****
{ mode_list_get_initial_insts(Modes, ModuleInfo1,
InitialInsts) },
io__write_string("% Initial insts: `"),
{ varset__init(InstVarSet) },
mercury_output_inst_list(InitialInsts, InstVarSet),
io__write_string("'\n")
*****/
;
[]
).
:- pred modecheck_queued_proc(how_to_check_goal, pred_proc_id, pred_table,
module_info, pred_table, module_info, bool,
io__state, io__state).
:- mode modecheck_queued_proc(in, in, in, in, out, out, out, di, uo) is det.
modecheck_queued_proc(HowToCheckGoal, PredProcId, OldPredTable0, ModuleInfo0,
OldPredTable, ModuleInfo, Changed) -->
{
%
% mark the procedure as ready to be processed
%
PredProcId = proc(PredId, ProcId),
module_info_preds(ModuleInfo0, Preds0),
map__lookup(Preds0, PredId, PredInfo0),
pred_info_procedures(PredInfo0, Procs0),
map__lookup(Procs0, ProcId, ProcInfo0),
proc_info_set_can_process(ProcInfo0, yes, ProcInfo1),
map__det_update(Procs0, ProcId, ProcInfo1, Procs1),
pred_info_set_procedures(PredInfo0, Procs1, PredInfo1),
map__det_update(Preds0, PredId, PredInfo1, Preds1),
module_info_set_preds(ModuleInfo0, Preds1, ModuleInfo1)
},
%
% modecheck the procedure
%
modecheck_proc(ProcId, PredId, ModuleInfo1, ModuleInfo2, NumErrors,
Changed1),
(
{ NumErrors \= 0 }
->
io__set_exit_status(1),
{ OldPredTable = OldPredTable0 },
{ module_info_remove_predid(ModuleInfo2, PredId, ModuleInfo) },
{ Changed = Changed1 }
;
( { HowToCheckGoal = check_unique_modes } ->
{ detect_switches_in_proc(ProcId, PredId,
ModuleInfo2, ModuleInfo3) },
detect_cse_in_proc(ProcId, PredId,
ModuleInfo3, ModuleInfo4),
determinism_check_proc(ProcId, PredId,
ModuleInfo4, ModuleInfo5),
{ save_proc_info(ProcId, PredId, ModuleInfo5,
OldPredTable0, OldPredTable) },
unique_modes__check_proc(ProcId, PredId,
ModuleInfo5, ModuleInfo,
Changed2),
{ bool__or(Changed1, Changed2, Changed) }
;
{ OldPredTable = OldPredTable0 },
{ ModuleInfo = ModuleInfo2 },
{ Changed = Changed1 }
)
).
%
% save a copy of the proc info for the specified procedure in OldProcTable0,
% giving OldProcTable.
%
:- pred save_proc_info(proc_id, pred_id, module_info, pred_table, pred_table).
:- mode save_proc_info(in, in, in, in, out) is det.
save_proc_info(ProcId, PredId, ModuleInfo, OldPredTable0, OldPredTable) :-
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
_PredInfo, ProcInfo),
map__lookup(OldPredTable0, PredId, OldPredInfo0),
pred_info_procedures(OldPredInfo0, OldProcTable0),
map__set(OldProcTable0, ProcId, ProcInfo, OldProcTable),
pred_info_set_procedures(OldPredInfo0, OldProcTable, OldPredInfo),
map__det_update(OldPredTable0, PredId, OldPredInfo, OldPredTable).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
unify_proc__add_lazily_generated_unify_pred(TypeId,
PredId, ModuleInfo0, ModuleInfo) :-
(
type_id_is_tuple(TypeId)
->
TypeId = _ - TupleArity,
%
% Build a hlds_type_body for the tuple constructor, which will
% be used by unify_proc__generate_clause_info.
%
varset__init(TVarSet0),
varset__new_vars(TVarSet0, TupleArity, TupleArgTVars, TVarSet),
term__var_list_to_term_list(TupleArgTVars, TupleArgTypes),
% Tuple constructors can't be existentially quantified.
ExistQVars = [],
ClassConstraints = [],
MakeUnamedField = (func(ArgType) = no - ArgType),
CtorArgs = list__map(MakeUnamedField, TupleArgTypes),
Ctor = ctor(ExistQVars, ClassConstraints,
CtorSymName, CtorArgs),
CtorSymName = unqualified("{}"),
ConsId = cons(CtorSymName, TupleArity),
map__from_assoc_list([ConsId - unshared_tag(0)],
ConsTagValues),
UnifyPred = no,
IsEnum = no,
TypeBody = du_type([Ctor], ConsTagValues, IsEnum, UnifyPred),
construct_type(TypeId, TupleArgTypes, Type),
term__context_init(Context)
;
unify_proc__collect_type_defn(ModuleInfo0, TypeId,
Type, TVarSet, TypeBody, Context)
),
% Call make_hlds.m to construct the unification predicate.
( can_generate_special_pred_clauses_for_type(TypeId, TypeBody) ->
% If the unification predicate has another status it should
% already have been generated.
UnifyPredStatus = pseudo_imported,
Item = clauses
;
UnifyPredStatus = imported(implementation),
Item = declaration
),
unify_proc__add_lazily_generated_special_pred(unify, Item,
TVarSet, Type, TypeId, TypeBody, Context, UnifyPredStatus,
PredId, ModuleInfo0, ModuleInfo).
unify_proc__add_lazily_generated_compare_pred_decl(TypeId,
PredId, ModuleInfo0, ModuleInfo) :-
unify_proc__collect_type_defn(ModuleInfo0, TypeId, Type,
TVarSet, TypeBody, Context),
% If the compare predicate has another status it should
% already have been generated.
ImportStatus = imported(implementation),
unify_proc__add_lazily_generated_special_pred(compare, declaration,
TVarSet, Type, TypeId, TypeBody, Context, ImportStatus,
PredId, ModuleInfo0, ModuleInfo).
:- pred unify_proc__add_lazily_generated_special_pred(special_pred_id,
unify_pred_item, tvarset, type, type_id, hlds_type_body,
context, import_status, pred_id, module_info, module_info).
:- mode unify_proc__add_lazily_generated_special_pred(in, in, in, in, in, in,
in, in, out, in, out) is det.
unify_proc__add_lazily_generated_special_pred(SpecialId, Item,
TVarSet, Type, TypeId, TypeBody, Context, PredStatus,
PredId, ModuleInfo0, ModuleInfo) :-
%
% Add the declaration and maybe clauses.
%
(
Item = clauses,
make_hlds__add_special_pred_for_real(SpecialId, ModuleInfo0,
TVarSet, Type, TypeId, TypeBody, Context,
PredStatus, ModuleInfo1)
;
Item = declaration,
make_hlds__add_special_pred_decl_for_real(SpecialId,
ModuleInfo0, TVarSet, Type, TypeId,
Context, PredStatus, ModuleInfo1)
),
module_info_get_special_pred_map(ModuleInfo1, SpecialPredMap),
map__lookup(SpecialPredMap, SpecialId - TypeId, PredId),
module_info_pred_info(ModuleInfo1, PredId, PredInfo0),
%
% The clauses are generated with all type information computed,
% so just go on to post_typecheck.
%
(
Item = clauses,
post_typecheck__finish_pred_no_io(ModuleInfo1,
ErrorProcs, PredInfo0, PredInfo)
;
Item = declaration,
post_typecheck__finish_imported_pred_no_io(ModuleInfo1,
ErrorProcs, PredInfo0, PredInfo)
),
require(unify(ErrorProcs, []),
"unify_proc__add_lazily_generated_special_pred: error in post_typecheck"),
%
% Call polymorphism to introduce type_info arguments
% for polymorphic types.
%
module_info_set_pred_info(ModuleInfo1, PredId, PredInfo, ModuleInfo2),
%
% Note that this will not work if the generated clauses call
% a polymorphic predicate which requires type_infos to be added.
% Such calls can be generated by unify_proc__generate_clause_info,
% but unification predicates which contain such calls are never
% generated lazily.
%
polymorphism__process_generated_pred(PredId, ModuleInfo2, ModuleInfo).
:- type unify_pred_item
---> declaration
; clauses
.
:- pred unify_proc__collect_type_defn(module_info,
type_id, type, tvarset, hlds_type_body, prog_context).
:- mode unify_proc__collect_type_defn(in, in, out, out, out, out) is det.
unify_proc__collect_type_defn(ModuleInfo0, TypeId, Type,
TVarSet, TypeBody, Context) :-
module_info_types(ModuleInfo0, Types),
map__lookup(Types, TypeId, TypeDefn),
hlds_data__get_type_defn_tvarset(TypeDefn, TVarSet),
hlds_data__get_type_defn_tparams(TypeDefn, TypeParams),
hlds_data__get_type_defn_body(TypeDefn, TypeBody),
hlds_data__get_type_defn_status(TypeDefn, TypeStatus),
hlds_data__get_type_defn_context(TypeDefn, Context),
require(special_pred_is_generated_lazily(ModuleInfo0,
TypeId, TypeBody, TypeStatus),
"unify_proc__add_lazily_generated_unify_pred"),
construct_type(TypeId, TypeParams, Type).
%-----------------------------------------------------------------------------%
unify_proc__generate_clause_info(SpecialPredId, Type, TypeBody, Context,
ModuleInfo, ClauseInfo) :-
( TypeBody = eqv_type(EqvType) ->
HeadVarType = EqvType
;
HeadVarType = Type
),
special_pred_info(SpecialPredId, HeadVarType,
_PredName, ArgTypes, _Modes, _Det),
unify_proc__info_init(ModuleInfo, VarTypeInfo0),
unify_proc__make_fresh_named_vars_from_types(ArgTypes, "HeadVar__", 1,
Args, VarTypeInfo0, VarTypeInfo1),
( SpecialPredId = unify, Args = [H1, H2] ->
unify_proc__generate_unify_clauses(TypeBody, H1, H2,
Context, Clauses, VarTypeInfo1, VarTypeInfo)
; SpecialPredId = index, Args = [X, Index] ->
unify_proc__generate_index_clauses(TypeBody,
X, Index, Context, Clauses, VarTypeInfo1, VarTypeInfo)
; SpecialPredId = compare, Args = [Res, X, Y] ->
unify_proc__generate_compare_clauses(Type, TypeBody,
Res, X, Y, Context, Clauses, VarTypeInfo1, VarTypeInfo)
;
error("unknown special pred")
),
unify_proc__info_extract(VarTypeInfo, VarSet, Types),
map__init(TVarNameMap),
map__init(TI_VarMap),
map__init(TCI_VarMap),
HasForeignClauses = yes,
ClauseInfo = clauses_info(VarSet, Types, TVarNameMap,
Types, Args, Clauses, TI_VarMap, TCI_VarMap,
HasForeignClauses).
:- pred unify_proc__generate_unify_clauses(hlds_type_body, prog_var, prog_var,
prog_context, list(clause), unify_proc_info, unify_proc_info).
:- mode unify_proc__generate_unify_clauses(in, in, in, in, out, in, out)
is det.
unify_proc__generate_unify_clauses(TypeBody, H1, H2, Context, Clauses) -->
(
{ TypeBody = du_type(Ctors, _, IsEnum, MaybeEqPred) },
( { MaybeEqPred = yes(PredName) } ->
%
% Just generate a call to the specified predicate,
% which is the user-defined equality pred for this
% type.
% (The pred_id and proc_id will be figured
% out by type checking and mode analysis.)
%
{ invalid_pred_id(PredId) },
{ invalid_proc_id(ModeId) },
{ Call = call(PredId, ModeId, [H1, H2], not_builtin,
no, PredName) },
{ goal_info_init(GoalInfo0) },
{ goal_info_set_context(GoalInfo0, Context,
GoalInfo) },
{ Goal = Call - GoalInfo },
unify_proc__quantify_clauses_body([H1, H2], Goal,
Context, Clauses)
; { IsEnum = yes } ->
%
% Enumerations are atomic types, so modecheck_unify.m
% will treat this unification as a simple_test, not
% a complicated_unify.
%
{ create_atomic_unification(H1, var(H2),
Context, explicit, [], Goal) },
unify_proc__quantify_clauses_body([H1, H2], Goal,
Context, Clauses)
;
unify_proc__generate_du_unify_clauses(Ctors, H1, H2,
Context, Clauses)
)
;
{ TypeBody = eqv_type(_Type) },
% We should check whether _Type is a type variable,
% an abstract type or a concrete type.
% If it is type variable, then we should generate the same code
% we generate now. If it is an abstract type, we should call
% its unification procedure directly; if it is a concrete type,
% we should generate the body of its unification procedure
% inline here.
%
% XXX Somebody should document here what the later stages
% of the compiler do to prevent an infinite recursion here.
{ create_atomic_unification(H1, var(H2), Context, explicit, [],
Goal) },
unify_proc__quantify_clauses_body([H1, H2], Goal, Context,
Clauses)
;
{ TypeBody = foreign_type(_, _) },
% XXX Is this the correct thing to do?
% I assume at code gen time I could examine the types
% of the unification and output different code because
% they are foreign types.
{ create_atomic_unification(H1, var(H2), Context, explicit, [],
Goal) },
unify_proc__quantify_clauses_body([H1, H2], Goal, Context,
Clauses)
;
{ TypeBody = uu_type(_) },
{ error("trying to create unify proc for uu type") }
;
{ TypeBody = abstract_type },
{ error("trying to create unify proc for abstract type") }
).
% This predicate generates the bodies of index predicates for the
% types that need index predicates.
%
% add_special_preds in make_hlds.m should include index in the list
% of special preds to define only for the kinds of types which do not
% lead this predicate to abort.
:- pred unify_proc__generate_index_clauses(hlds_type_body, prog_var, prog_var,
prog_context, list(clause), unify_proc_info, unify_proc_info).
:- mode unify_proc__generate_index_clauses(in, in, in, in, out, in, out)
is det.
unify_proc__generate_index_clauses(TypeBody, X, Index, Context, Clauses) -->
(
{ TypeBody = du_type(Ctors, _, IsEnum, MaybeEqPred) },
( { MaybeEqPred = yes(_) } ->
%
% For non-canonical types, the generated comparison
% predicate returns an error, and does not call the
% type's index predicate, so do not generate an index
% predicate for such types.
%
{ error("trying to create index proc for non-canonical type") }
; { IsEnum = yes } ->
%
% For enum types, the generated comparison predicate
% performs an integer comparison, and does not call the
% type's index predicate, so do not generate an index
% predicate for such types.
%
{ error("trying to create index proc for enum type") }
;
unify_proc__generate_du_index_clauses(Ctors, X, Index,
Context, 0, Clauses)
)
;
{ TypeBody = eqv_type(_Type) },
% The only place that the index predicate for a type can ever
% be called from is the compare predicate for that type.
% However, the compare predicate for an equivalence type
% never calls the index predicate for that type; it calls
% the compare predicate of the expanded type instead.
%
% Therefore the clause body we are generating should never be
% invoked.
{ error("trying to create index proc for eqv type") }
;
{ TypeBody = foreign_type(_, _) },
{ error("trying to create index proc for a foreign type") }
;
{ TypeBody = uu_type(_) },
{ error("trying to create index proc for uu type") }
;
{ TypeBody = abstract_type },
{ error("trying to create index proc for abstract type") }
).
:- pred unify_proc__generate_compare_clauses((type)::in, hlds_type_body::in,
prog_var::in, prog_var::in, prog_var::in, prog_context::in,
list(clause)::out, unify_proc_info::in, unify_proc_info::out) is det.
unify_proc__generate_compare_clauses(Type, TypeBody, Res, H1, H2, Context,
Clauses) -->
(
{ TypeBody = du_type(Ctors, _, IsEnum, MaybeEqPred) },
( { MaybeEqPred = yes(_) } ->
%
% just generate code that will call error/1
%
{ ArgVars = [Res, H1, H2] },
unify_proc__build_call(
"builtin_compare_non_canonical_type",
ArgVars, Context, Goal),
unify_proc__quantify_clauses_body(ArgVars, Goal,
Context, Clauses)
; { IsEnum = yes } ->
{ IntType = int_type },
unify_proc__info_new_var(IntType, TC1),
unify_proc__info_new_var(IntType, TC2),
{ TC1ArgVars = [H1, TC1] },
unify_proc__build_call("unsafe_type_cast",
TC1ArgVars, Context, TC1Goal),
{ TC2ArgVars = [H2, TC2] },
unify_proc__build_call("unsafe_type_cast",
TC2ArgVars, Context, TC2Goal),
{ CompareArgVars = [Res, TC1, TC2] },
unify_proc__build_call("builtin_compare_int",
CompareArgVars, Context, CompareGoal),
{ goal_info_init(GoalInfo0) },
{ goal_info_set_context(GoalInfo0, Context,
GoalInfo) },
{ conj_list_to_goal([TC1Goal, TC2Goal, CompareGoal],
GoalInfo, Goal) },
{ ArgVars = [Res, H1, H2] },
unify_proc__quantify_clauses_body(ArgVars, Goal,
Context, Clauses)
;
unify_proc__generate_du_compare_clauses(Type, Ctors,
Res, H1, H2, Context, Clauses)
)
;
{ TypeBody = eqv_type(_) },
% We should check whether _Type is a type variable,
% an abstract type or a concrete type.
% If it is type variable, then we should generate the same code
% we generate now. If it is an abstract type, we should call
% its compare procedure directly; if it is a concrete type,
% we should generate the body of its compare procedure
% inline here.
%
% XXX Somebody should document here what the later stages
% of the compiler do to prevent an infinite recursion here.
{ ArgVars = [Res, H1, H2] },
unify_proc__build_call("compare", ArgVars, Context, Goal),
unify_proc__quantify_clauses_body(ArgVars, Goal, Context,
Clauses)
;
{ TypeBody = foreign_type(_, _) },
% XXX
% I think we should delay handling this for foreign types until
% code gen time.
{ ArgVars = [Res, H1, H2] },
unify_proc__build_call("compare", ArgVars, Context, Goal),
unify_proc__quantify_clauses_body(ArgVars, Goal, Context,
Clauses)
;
{ TypeBody = uu_type(_) },
{ error("trying to create compare proc for uu type") }
;
{ TypeBody = abstract_type },
{ error("trying to create compare proc for abstract type") }
).
:- pred unify_proc__quantify_clauses_body(list(prog_var)::in, hlds_goal::in,
prog_context::in, list(clause)::out,
unify_proc_info::in, unify_proc_info::out) is det.
unify_proc__quantify_clauses_body(HeadVars, Goal, Context, Clauses) -->
unify_proc__quantify_clause_body(HeadVars, Goal, Context, Clause),
{ Clauses = [Clause] }.
:- pred unify_proc__quantify_clause_body(list(prog_var)::in, hlds_goal::in,
prog_context::in, clause::out,
unify_proc_info::in, unify_proc_info::out) is det.
unify_proc__quantify_clause_body(HeadVars, Goal, Context, Clause) -->
unify_proc__info_get_varset(Varset0),
unify_proc__info_get_types(Types0),
{ implicitly_quantify_clause_body(HeadVars, Goal, Varset0, Types0,
Body, Varset, Types, _Warnings) },
unify_proc__info_set_varset(Varset),
unify_proc__info_set_types(Types),
{ Clause = clause([], Body, mercury, Context) }.
%-----------------------------------------------------------------------------%
% For a type such as
%
% type t(X) ---> a ; b(int) ; c(X); d(int, X, t)
%
% we want to generate code
%
% eq(H1, H2) :-
% (
% H1 = a,
% H2 = a
% ;
% H1 = b(X1),
% H2 = b(X2),
% X1 = X2,
% ;
% H1 = c(Y1),
% H2 = c(Y2),
% Y1 = Y2,
% ;
% H1 = d(A1, B1, C1),
% H2 = c(A2, B2, C2),
% A1 = A2,
% B1 = B2,
% C1 = C2
% ).
:- pred unify_proc__generate_du_unify_clauses(list(constructor), prog_var,
prog_var, prog_context, list(clause),
unify_proc_info, unify_proc_info).
:- mode unify_proc__generate_du_unify_clauses(in, in, in, in, out, in, out)
is det.
unify_proc__generate_du_unify_clauses([], _H1, _H2, _Context, []) --> [].
unify_proc__generate_du_unify_clauses([Ctor | Ctors], H1, H2, Context,
[Clause | Clauses]) -->
{ Ctor = ctor(ExistQTVars, _Constraints, FunctorName, ArgTypes) },
{ list__length(ArgTypes, FunctorArity) },
{ FunctorConsId = cons(FunctorName, FunctorArity) },
unify_proc__make_fresh_vars(ArgTypes, ExistQTVars, Vars1),
unify_proc__make_fresh_vars(ArgTypes, ExistQTVars, Vars2),
{ create_atomic_unification(
H1, functor(FunctorConsId, Vars1), Context, explicit, [],
UnifyH1_Goal) },
{ create_atomic_unification(
H2, functor(FunctorConsId, Vars2), Context, explicit, [],
UnifyH2_Goal) },
unify_proc__unify_var_lists(ArgTypes, ExistQTVars, Vars1, Vars2,
UnifyArgs_Goal),
{ GoalList = [UnifyH1_Goal, UnifyH2_Goal | UnifyArgs_Goal] },
{ goal_info_init(GoalInfo0) },
{ goal_info_set_context(GoalInfo0, Context,
GoalInfo) },
{ conj_list_to_goal(GoalList, GoalInfo, Goal) },
unify_proc__quantify_clause_body([H1, H2], Goal, Context, Clause),
unify_proc__generate_du_unify_clauses(Ctors, H1, H2, Context, Clauses).
%-----------------------------------------------------------------------------%
% For a type such as
%
% :- type foo ---> f ; g(a, b, c) ; h(foo).
%
% we want to generate code
%
% index(X, Index) :-
% (
% X = f,
% Index = 0
% ;
% X = g(_, _, _),
% Index = 1
% ;
% X = h(_),
% Index = 2
% ).
:- pred unify_proc__generate_du_index_clauses(list(constructor), prog_var,
prog_var, prog_context, int, list(clause),
unify_proc_info, unify_proc_info).
:- mode unify_proc__generate_du_index_clauses(in, in, in, in, in, out, in, out)
is det.
unify_proc__generate_du_index_clauses([], _X, _Index, _Context, _N, []) --> [].
unify_proc__generate_du_index_clauses([Ctor | Ctors], X, Index, Context, N,
[Clause | Clauses]) -->
{ Ctor = ctor(ExistQTVars, _Constraints, FunctorName, ArgTypes) },
{ list__length(ArgTypes, FunctorArity) },
{ FunctorConsId = cons(FunctorName, FunctorArity) },
unify_proc__make_fresh_vars(ArgTypes, ExistQTVars, ArgVars),
{ create_atomic_unification(
X, functor(FunctorConsId, ArgVars), Context, explicit, [],
UnifyX_Goal) },
{ create_atomic_unification(
Index, functor(int_const(N), []), Context, explicit, [],
UnifyIndex_Goal) },
{ GoalList = [UnifyX_Goal, UnifyIndex_Goal] },
{ goal_info_init(GoalInfo0) },
{ goal_info_set_context(GoalInfo0, Context, GoalInfo) },
{ conj_list_to_goal(GoalList, GoalInfo, Goal) },
unify_proc__quantify_clause_body([X, Index], Goal, Context, Clause),
{ N1 is N + 1 },
unify_proc__generate_du_index_clauses(Ctors, X, Index, Context, N1,
Clauses).
%-----------------------------------------------------------------------------%
:- pred unify_proc__generate_du_compare_clauses((type)::in,
list(constructor)::in, prog_var::in, prog_var::in, prog_var::in,
prog_context::in, list(clause)::out,
unify_proc_info::in, unify_proc_info::out) is det.
unify_proc__generate_du_compare_clauses(Type, Ctors, Res, H1, H2,
Context, Clauses) -->
(
{ Ctors = [] },
{ error("compare for type with no functors") }
;
{ Ctors = [Ctor] },
unify_proc__info_get_module_info(ModuleInfo),
{ module_info_globals(ModuleInfo, Globals) },
{ globals__lookup_int_option(Globals, compare_specialization,
CompareSpec) },
( { CompareSpec >= 1 } ->
unify_proc__generate_du_one_compare_clause(
Ctor, Res, H1, H2,
Context, Clauses)
;
unify_proc__generate_du_general_compare_clauses(Type,
Ctors, Res, H1, H2, Context, Clauses)
)
;
{ Ctors = [Ctor1, Ctor2] },
unify_proc__info_get_module_info(ModuleInfo),
{ module_info_globals(ModuleInfo, Globals) },
{ globals__lookup_int_option(Globals, compare_specialization,
CompareSpec) },
( { CompareSpec >= 2 } ->
unify_proc__generate_du_two_compare_clauses(
Ctor1, Ctor2, Res, H1, H2,
Context, Clauses)
;
unify_proc__generate_du_general_compare_clauses(Type,
Ctors, Res, H1, H2, Context, Clauses)
)
;
{ Ctors = [Ctor1, Ctor2, Ctor3] },
unify_proc__info_get_module_info(ModuleInfo),
{ module_info_globals(ModuleInfo, Globals) },
{ globals__lookup_int_option(Globals, compare_specialization,
CompareSpec) },
( { CompareSpec >= 3 } ->
unify_proc__generate_du_three_compare_clauses(
Ctor1, Ctor2, Ctor3, Res, H1, H2,
Context, Clauses)
;
unify_proc__generate_du_general_compare_clauses(Type,
Ctors, Res, H1, H2, Context, Clauses)
)
;
{ Ctors = [_, _, _, _ | _] },
unify_proc__generate_du_general_compare_clauses(Type,
Ctors, Res, H1, H2, Context, Clauses)
).
unify_proc__max_exploited_compare_spec_value = 3.
%-----------------------------------------------------------------------------%
% For a du type with one function symbol, such as
%
% :- type foo ---> f(a, b, c)
%
% we want to generate code
%
% compare(Res, X, Y) :-
% X = f(X1, X2, X3), Y = f(Y1, Y2, Y3),
% ( compare(R1, X1, Y1), R1 \= (=) ->
% R = R1
% ; compare(R2, X2, Y2), R2 \= (=) ->
% R = R2
% ;
% compare(R, X3, Y3)
% ).
:- pred unify_proc__generate_du_one_compare_clause(constructor::in,
prog_var::in, prog_var::in, prog_var::in,
prog_context::in, list(clause)::out,
unify_proc_info::in, unify_proc_info::out) is det.
unify_proc__generate_du_one_compare_clause(Ctor, R, X, Y, Context, Clauses) -->
unify_proc__generate_compare_case(Ctor, R, X, Y, Context, Goal),
{ HeadVars = [R, X, Y] },
unify_proc__quantify_clauses_body(HeadVars, Goal, Context, Clauses).
%-----------------------------------------------------------------------------%
% For a du type with two or three function symbols, such as
%
% :- type foo ---> f(a) ; g(a, b, c)
%
% we want to generate code such as
%
% compare(Res, X, Y) :-
% (
% X = f(X1),
% Y = f(Y1),
% compare(R, X1, Y1)
% ;
% X = f(_),
% Y = g(_, _, _),
% R = (<)
% ;
% X = g(_, _, _),
% Y = f(_),
% R = (>)
% ;
% X = g(X1, X2, X3),
% Y = g(Y1, Y2, Y3),
% ( compare(R1, X1, Y1), R1 \= (=) ->
% R = R1
% ; compare(R2, X2, Y2), R2 \= (=) ->
% R = R2
% ;
% compare(R, X3, Y3)
% )
% ).
:- pred unify_proc__generate_du_two_compare_clauses(
constructor::in, constructor::in, prog_var::in, prog_var::in,
prog_var::in, prog_context::in, list(clause)::out,
unify_proc_info::in, unify_proc_info::out) is det.
unify_proc__generate_du_two_compare_clauses(Ctor1, Ctor2, R, X, Y,
Context, Clauses) -->
unify_proc__generate_compare_case(Ctor1, R, X, Y, Context, Case11),
unify_proc__generate_compare_case(Ctor2, R, X, Y, Context, Case22),
unify_proc__generate_asymmetric_compare_case(Ctor1, Ctor2, "<",
R, X, Y, Context, Case12),
unify_proc__generate_asymmetric_compare_case(Ctor2, Ctor1, ">",
R, X, Y, Context, Case21),
{ goal_info_init(GoalInfo0) },
{ goal_info_set_context(GoalInfo0, Context, GoalInfo) },
{ map__init(Empty) },
{ Goal = disj([Case11, Case12, Case21, Case22], Empty) - GoalInfo },
{ HeadVars = [R, X, Y] },
unify_proc__quantify_clauses_body(HeadVars, Goal, Context, Clauses).
:- pred unify_proc__generate_du_three_compare_clauses(
constructor::in, constructor::in, constructor::in,
prog_var::in, prog_var::in, prog_var::in, prog_context::in,
list(clause)::out, unify_proc_info::in, unify_proc_info::out) is det.
unify_proc__generate_du_three_compare_clauses(Ctor1, Ctor2, Ctor3, R, X, Y,
Context, Clauses) -->
unify_proc__generate_compare_case(Ctor1, R, X, Y, Context, Case11),
unify_proc__generate_compare_case(Ctor2, R, X, Y, Context, Case22),
unify_proc__generate_compare_case(Ctor3, R, X, Y, Context, Case33),
unify_proc__generate_asymmetric_compare_case(Ctor1, Ctor2, "<",
R, X, Y, Context, Case12),
unify_proc__generate_asymmetric_compare_case(Ctor1, Ctor3, "<",
R, X, Y, Context, Case13),
unify_proc__generate_asymmetric_compare_case(Ctor2, Ctor3, "<",
R, X, Y, Context, Case23),
unify_proc__generate_asymmetric_compare_case(Ctor2, Ctor1, ">",
R, X, Y, Context, Case21),
unify_proc__generate_asymmetric_compare_case(Ctor3, Ctor1, ">",
R, X, Y, Context, Case31),
unify_proc__generate_asymmetric_compare_case(Ctor3, Ctor2, ">",
R, X, Y, Context, Case32),
{ goal_info_init(GoalInfo0) },
{ goal_info_set_context(GoalInfo0, Context, GoalInfo) },
{ map__init(Empty) },
{ Goal = disj([Case11, Case12, Case13, Case21, Case22, Case23,
Case31, Case32, Case33], Empty) - GoalInfo },
{ HeadVars = [R, X, Y] },
unify_proc__quantify_clauses_body(HeadVars, Goal, Context, Clauses).
%-----------------------------------------------------------------------------%
% For a du type with four or more function symbols, such as
%
% :- type foo ---> f ; g(a) ; h(b, foo).
%
% we want to generate code
%
% compare(Res, X, Y) :-
% __Index__(X, X_Index), % Call_X_Index
% __Index__(Y, Y_Index), % Call_Y_Index
% ( X_Index < Y_Index -> % Call_Less_Than
% Res = (<) % Return_Less_Than
% ; X_Index > Y_Index -> % Call_Greater_Than
% Res = (>) % Return_Greater_Than
% ;
% % This disjunction is generated by
% % unify_proc__generate_compare_cases, below.
% (
% X = f, Y = f,
% R = (=)
% ;
% X = g(X1), Y = g(Y1),
% compare(R, X1, Y1)
% ;
% X = h(X1, X2), Y = h(Y1, Y2),
% ( compare(R1, X1, Y1), R1 \= (=) ->
% R = R1
% ;
% compare(R, X2, Y2)
% )
% )
% ->
% Res = R % Return_R
% ;
% compare_error % Abort
% ).
:- pred unify_proc__generate_du_general_compare_clauses((type)::in,
list(constructor)::in, prog_var::in, prog_var::in, prog_var::in,
prog_context::in, list(clause)::out,
unify_proc_info::in, unify_proc_info::out) is det.
unify_proc__generate_du_general_compare_clauses(Type, Ctors, Res, X, Y,
Context, [Clause]) -->
unify_proc__generate_du_compare_clauses_2(Type, Ctors, Res,
X, Y, Context, Goal),
{ HeadVars = [Res, X, Y] },
unify_proc__quantify_clause_body(HeadVars, Goal, Context, Clause).
:- pred unify_proc__generate_du_compare_clauses_2((type)::in,
list(constructor)::in, prog_var::in, prog_var::in, prog_var::in,
prog_context::in, hlds_goal::out,
unify_proc_info::in, unify_proc_info::out) is det.
unify_proc__generate_du_compare_clauses_2(Type, Ctors, Res, X, Y, Context,
Goal) -->
{ IntType = int_type },
{ mercury_public_builtin_module(MercuryBuiltin) },
{ construct_type(qualified(MercuryBuiltin, "comparison_result") - 0,
[], ResType) },
unify_proc__info_new_var(IntType, X_Index),
unify_proc__info_new_var(IntType, Y_Index),
unify_proc__info_new_var(ResType, R),
{ goal_info_init(GoalInfo0) },
{ goal_info_set_context(GoalInfo0, Context, GoalInfo) },
{ instmap_delta_from_assoc_list([X_Index - ground(shared, none)],
X_InstmapDelta) },
unify_proc__build_specific_call(Type, index, [X, X_Index],
X_InstmapDelta, det, Context, Call_X_Index),
{ instmap_delta_from_assoc_list([Y_Index - ground(shared, none)],
Y_InstmapDelta) },
unify_proc__build_specific_call(Type, index, [Y, Y_Index],
Y_InstmapDelta, det, Context, Call_Y_Index),
unify_proc__build_call("builtin_int_lt", [X_Index, Y_Index], Context,
Call_Less_Than),
unify_proc__build_call("builtin_int_gt", [X_Index, Y_Index], Context,
Call_Greater_Than),
{ create_atomic_unification(
Res, functor(cons(unqualified("<"), 0), []),
Context, explicit, [],
Return_Less_Than) },
{ create_atomic_unification(
Res, functor(cons(unqualified(">"), 0), []),
Context, explicit, [],
Return_Greater_Than) },
{ create_atomic_unification(Res, var(R), Context, explicit, [],
Return_R) },
unify_proc__generate_compare_cases(Ctors, R, X, Y, Context, Cases),
{ map__init(Empty) },
{ CasesGoal = disj(Cases, Empty) - GoalInfo },
unify_proc__build_call("compare_error", [], Context, Abort),
{ Goal = conj([
Call_X_Index,
Call_Y_Index,
if_then_else([], Call_Less_Than, Return_Less_Than,
if_then_else([], Call_Greater_Than, Return_Greater_Than,
if_then_else([], CasesGoal, Return_R, Abort, Empty
) - GoalInfo, Empty
) - GoalInfo, Empty
) - GoalInfo
]) - GoalInfo }.
% unify_proc__generate_compare_cases: for a type such as
%
% :- type foo ---> f ; g(a) ; h(b, foo).
%
% we want to generate code
% (
% X = f, % UnifyX_Goal
% Y = f, % UnifyY_Goal
% R = (=) % CompareArgs_Goal
% ;
% X = g(X1),
% Y = g(Y1),
% compare(R, X1, Y1)
% ;
% X = h(X1, X2),
% Y = h(Y1, Y2),
% ( compare(R1, X1, Y1), R1 \= (=) ->
% R = R1
% ;
% compare(R, X2, Y2)
% )
% )
:- pred unify_proc__generate_compare_cases(list(constructor), prog_var,
prog_var, prog_var, prog_context, list(hlds_goal),
unify_proc_info, unify_proc_info).
:- mode unify_proc__generate_compare_cases(in, in, in, in, in, out, in, out)
is det.
unify_proc__generate_compare_cases([], _R, _X, _Y, _Context, []) --> [].
unify_proc__generate_compare_cases([Ctor | Ctors], R, X, Y, Context,
[Case | Cases]) -->
unify_proc__generate_compare_case(Ctor, R, X, Y, Context, Case),
unify_proc__generate_compare_cases(Ctors, R, X, Y, Context, Cases).
:- pred unify_proc__generate_compare_case(constructor, prog_var, prog_var,
prog_var, prog_context, hlds_goal,
unify_proc_info, unify_proc_info).
:- mode unify_proc__generate_compare_case(in, in, in, in, in, out, in, out)
is det.
unify_proc__generate_compare_case(Ctor, R, X, Y, Context, Case) -->
{ Ctor = ctor(ExistQTVars, _Constraints, FunctorName, ArgTypes) },
{ list__length(ArgTypes, FunctorArity) },
{ FunctorConsId = cons(FunctorName, FunctorArity) },
unify_proc__make_fresh_vars(ArgTypes, ExistQTVars, Vars1),
unify_proc__make_fresh_vars(ArgTypes, ExistQTVars, Vars2),
{ create_atomic_unification(
X, functor(FunctorConsId, Vars1), Context, explicit, [],
UnifyX_Goal) },
{ create_atomic_unification(
Y, functor(FunctorConsId, Vars2), Context, explicit, [],
UnifyY_Goal) },
unify_proc__compare_args(ArgTypes, ExistQTVars, Vars1, Vars2,
R, Context, CompareArgs_Goal),
{ GoalList = [UnifyX_Goal, UnifyY_Goal, CompareArgs_Goal] },
{ goal_info_init(GoalInfo0) },
{ goal_info_set_context(GoalInfo0, Context, GoalInfo) },
{ conj_list_to_goal(GoalList, GoalInfo, Case) }.
:- pred unify_proc__generate_asymmetric_compare_case(constructor::in,
constructor::in, string::in, prog_var::in, prog_var::in, prog_var::in,
prog_context::in, hlds_goal::out,
unify_proc_info::in, unify_proc_info::out) is det.
unify_proc__generate_asymmetric_compare_case(Ctor1, Ctor2, CompareOp, R, X, Y,
Context, Case) -->
{ Ctor1 = ctor(ExistQTVars1, _Constraints1, FunctorName1, ArgTypes1) },
{ Ctor2 = ctor(ExistQTVars2, _Constraints2, FunctorName2, ArgTypes2) },
{ list__length(ArgTypes1, FunctorArity1) },
{ list__length(ArgTypes2, FunctorArity2) },
{ FunctorConsId1 = cons(FunctorName1, FunctorArity1) },
{ FunctorConsId2 = cons(FunctorName2, FunctorArity2) },
unify_proc__make_fresh_vars(ArgTypes1, ExistQTVars1, Vars1),
unify_proc__make_fresh_vars(ArgTypes2, ExistQTVars2, Vars2),
{ create_atomic_unification(
X, functor(FunctorConsId1, Vars1), Context, explicit, [],
UnifyX_Goal) },
{ create_atomic_unification(
Y, functor(FunctorConsId2, Vars2), Context, explicit, [],
UnifyY_Goal) },
{ create_atomic_unification(
R, functor(cons(unqualified(CompareOp), 0), []),
Context, explicit, [],
ReturnResult) },
{ GoalList = [UnifyX_Goal, UnifyY_Goal, ReturnResult] },
{ goal_info_init(GoalInfo0) },
{ goal_info_set_context(GoalInfo0, Context, GoalInfo) },
{ conj_list_to_goal(GoalList, GoalInfo, Case) }.
% unify_proc__compare_args: for a constructor such as
%
% h(list(int), foo, string)
%
% we want to generate code
%
% (
% compare(R1, X1, Y1), % Do_Comparison
% R1 \= (=) % Check_Not_Equal
% ->
% R = R1 % Return_R1
% ;
% compare(R2, X2, Y2),
% R2 \= (=)
% ->
% R = R2
% ;
% compare(R, X3, Y3) % Return_Comparison
% )
%
% For a constructor with no arguments, we want to generate code
%
% R = (=) % Return_Equal
:- pred unify_proc__compare_args(list(constructor_arg), existq_tvars,
list(prog_var), list(prog_var), prog_var, prog_context,
hlds_goal, unify_proc_info, unify_proc_info).
:- mode unify_proc__compare_args(in, in, in, in, in, in, out, in, out) is det.
unify_proc__compare_args(ArgTypes, ExistQTVars, Xs, Ys, R, Context, Goal) -->
(
unify_proc__compare_args_2(ArgTypes, ExistQTVars, Xs, Ys, R,
Context, Goal0)
->
{ Goal = Goal0 }
;
{ error("unify_proc__compare_args: length mismatch") }
).
:- pred unify_proc__compare_args_2(list(constructor_arg), existq_tvars,
list(prog_var), list(prog_var), prog_var, prog_context,
hlds_goal, unify_proc_info, unify_proc_info).
:- mode unify_proc__compare_args_2(in, in, in, in, in, in, out, in, out)
is semidet.
unify_proc__compare_args_2([], _, [], [], R, Context, Return_Equal) -->
{ create_atomic_unification(
R, functor(cons(unqualified("="), 0), []),
Context, explicit, [],
Return_Equal) }.
unify_proc__compare_args_2([_Name - Type|ArgTypes], ExistQTVars, [X|Xs], [Y|Ys],
R, Context, Goal) -->
{ goal_info_init(GoalInfo0) },
{ goal_info_set_context(GoalInfo0, Context, GoalInfo) },
%
% When comparing existentially typed arguments, the arguments may
% have different types; in that case, rather than just comparing them,
% which would be a type error, we call `typed_compare', which is a
% builtin that first compares their types and then compares
% their values.
%
{
list__member(ExistQTVar, ExistQTVars),
term__contains_var(Type, ExistQTVar)
->
ComparePred = "typed_compare"
;
ComparePred = "compare"
},
( { Xs = [], Ys = [] } ->
unify_proc__build_call(ComparePred, [R, X, Y], Context, Goal)
;
{ mercury_public_builtin_module(MercuryBuiltin) },
{ construct_type(
qualified(MercuryBuiltin, "comparison_result") - 0,
[], ResType) },
unify_proc__info_new_var(ResType, R1),
unify_proc__build_call(ComparePred, [R1, X, Y], Context,
Do_Comparison),
{ create_atomic_unification(
R1, functor(cons(unqualified("="), 0), []),
Context, explicit, [],
Check_Equal) },
{ Check_Not_Equal = not(Check_Equal) - GoalInfo },
{ create_atomic_unification(
R, var(R1), Context, explicit, [], Return_R1) },
{ Condition = conj([Do_Comparison, Check_Not_Equal])
- GoalInfo },
{ map__init(Empty) },
{ Goal = if_then_else([], Condition, Return_R1, ElseCase, Empty)
- GoalInfo},
unify_proc__compare_args_2(ArgTypes, ExistQTVars, Xs, Ys, R,
Context, ElseCase)
).
%-----------------------------------------------------------------------------%
:- pred unify_proc__build_call(string, list(prog_var), prog_context, hlds_goal,
unify_proc_info, unify_proc_info).
:- mode unify_proc__build_call(in, in, in, out, in, out) is det.
unify_proc__build_call(Name, ArgVars, Context, Goal) -->
unify_proc__info_get_module_info(ModuleInfo),
{ module_info_get_predicate_table(ModuleInfo, PredicateTable) },
{ list__length(ArgVars, Arity) },
%
% We assume that the special preds compare/3, index/2, and unify/2
% are the only public builtins called by code generated
% by this module.
%
{ special_pred_name_arity(_, Name, _, Arity) ->
mercury_public_builtin_module(MercuryBuiltin)
;
mercury_private_builtin_module(MercuryBuiltin)
},
{
predicate_table_search_pred_m_n_a(PredicateTable,
MercuryBuiltin, Name, Arity, [PredIdPrime])
->
PredId = PredIdPrime
;
prog_out__sym_name_to_string(qualified(MercuryBuiltin, Name),
QualName),
string__int_to_string(Arity, ArityString),
string__append_list(["unify_proc__build_call: ",
"invalid/ambiguous pred `",
QualName, "/", ArityString, "'"],
ErrorMessage),
error(ErrorMessage)
},
{ hlds_pred__initial_proc_id(ProcId) },
{ Call = call(PredId, ProcId, ArgVars, not_builtin,
no, qualified(MercuryBuiltin, Name)) },
{ goal_info_init(GoalInfo0) },
{ goal_info_set_context(GoalInfo0, Context, GoalInfo) },
{ Goal = Call - GoalInfo }.
:- pred unify_proc__build_specific_call((type)::in, special_pred_id::in,
list(prog_var)::in, instmap_delta::in, determinism::in,
prog_context::in, hlds_goal::out,
unify_proc_info::in, unify_proc_info::out) is det.
unify_proc__build_specific_call(Type, SpecialPredId, ArgVars, InstmapDelta,
Detism, Context, Goal) -->
unify_proc__info_get_module_info(ModuleInfo),
{
polymorphism__get_special_proc(Type, SpecialPredId, ModuleInfo,
PredName, PredId, ProcId)
->
GoalExpr = call(PredId, ProcId, ArgVars, not_builtin, no,
PredName),
set__list_to_set(ArgVars, NonLocals),
goal_info_init(NonLocals, InstmapDelta, Detism, GoalInfo0),
goal_info_set_context(GoalInfo0, Context, GoalInfo),
Goal = GoalExpr - GoalInfo
;
% unify_proc__build_specific_call is only ever used
% to build calls to special preds for a type in the
% bodies of other special preds for that same type.
% If the special preds for a type are built in the
% right order (index before compare), the lookup
% should never fail.
error("unify_proc__build_specific_call: lookup failed")
}.
%-----------------------------------------------------------------------------%
:- pred unify_proc__make_fresh_named_vars_from_types(list(type), string, int,
list(prog_var), unify_proc_info, unify_proc_info).
:- mode unify_proc__make_fresh_named_vars_from_types(in, in, in, out, in, out)
is det.
unify_proc__make_fresh_named_vars_from_types([], _, _, []) --> [].
unify_proc__make_fresh_named_vars_from_types([Type | Types], BaseName, Num,
[Var | Vars]) -->
{ string__int_to_string(Num, NumStr) },
{ string__append(BaseName, NumStr, Name) },
unify_proc__info_new_named_var(Type, Name, Var),
unify_proc__make_fresh_named_vars_from_types(Types, BaseName, Num + 1,
Vars).
:- pred unify_proc__make_fresh_vars_from_types(list(type), list(prog_var),
unify_proc_info, unify_proc_info).
:- mode unify_proc__make_fresh_vars_from_types(in, out, in, out) is det.
unify_proc__make_fresh_vars_from_types([], []) --> [].
unify_proc__make_fresh_vars_from_types([Type | Types], [Var | Vars]) -->
unify_proc__info_new_var(Type, Var),
unify_proc__make_fresh_vars_from_types(Types, Vars).
:- pred unify_proc__make_fresh_vars(list(constructor_arg), existq_tvars,
list(prog_var), unify_proc_info, unify_proc_info).
:- mode unify_proc__make_fresh_vars(in, in, out, in, out) is det.
unify_proc__make_fresh_vars(CtorArgs, ExistQTVars, Vars) -->
( { ExistQTVars = [] } ->
{ assoc_list__values(CtorArgs, ArgTypes) },
unify_proc__make_fresh_vars_from_types(ArgTypes, Vars)
;
%
% If there are existential types involved, then it's too
% hard to get the types right here (it would require
% allocating new type variables) -- instead, typecheck.m
% will typecheck the clause to figure out the correct types.
% So we just allocate the variables and leave it up to
% typecheck.m to infer their types.
%
unify_proc__info_get_varset(VarSet0),
{ list__length(CtorArgs, NumVars) },
{ varset__new_vars(VarSet0, NumVars, Vars, VarSet) },
unify_proc__info_set_varset(VarSet)
).
:- pred unify_proc__unify_var_lists(list(constructor_arg), existq_tvars,
list(prog_var), list(prog_var), list(hlds_goal),
unify_proc_info, unify_proc_info).
:- mode unify_proc__unify_var_lists(in, in, in, in, out, in, out) is det.
unify_proc__unify_var_lists(ArgTypes, ExistQVars, Vars1, Vars2, Goal) -->
(
unify_proc__unify_var_lists_2(ArgTypes, ExistQVars,
Vars1, Vars2, Goal0)
->
{ Goal = Goal0 }
;
{ error("unify_proc__unify_var_lists: length mismatch") }
).
:- pred unify_proc__unify_var_lists_2(list(constructor_arg), existq_tvars,
list(prog_var), list(prog_var), list(hlds_goal),
unify_proc_info, unify_proc_info).
:- mode unify_proc__unify_var_lists_2(in, in, in, in, out, in, out) is semidet.
unify_proc__unify_var_lists_2([], _, [], [], []) --> [].
unify_proc__unify_var_lists_2([_Name - Type | ArgTypes], ExistQTVars,
[Var1 | Vars1], [Var2 | Vars2], [Goal | Goals]) -->
{ term__context_init(Context) },
%
% When unifying existentially typed arguments, the arguments may
% have different types; in that case, rather than just unifying them,
% which would be a type error, we call `typed_unify', which is a
% builtin that first checks that their types are equal and then
% unifies the values.
%
(
{ list__member(ExistQTVar, ExistQTVars) },
{ term__contains_var(Type, ExistQTVar) }
->
unify_proc__build_call("typed_unify", [Var1, Var2], Context,
Goal)
;
{ create_atomic_unification(Var1, var(Var2), Context, explicit,
[], Goal) }
),
unify_proc__unify_var_lists_2(ArgTypes, ExistQTVars, Vars1, Vars2,
Goals).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% It's a pity that we don't have nested modules.
% :- begin_module unify_proc_info.
% :- interface.
:- type unify_proc_info.
:- pred unify_proc__info_init(module_info::in, unify_proc_info::out) is det.
:- pred unify_proc__info_new_var((type)::in, prog_var::out,
unify_proc_info::in, unify_proc_info::out) is det.
:- pred unify_proc__info_new_named_var((type)::in, string::in, prog_var::out,
unify_proc_info::in, unify_proc_info::out) is det.
:- pred unify_proc__info_extract(unify_proc_info::in,
prog_varset::out, vartypes::out) is det.
:- pred unify_proc__info_get_varset(prog_varset::out,
unify_proc_info::in, unify_proc_info::out) is det.
:- pred unify_proc__info_set_varset(prog_varset::in,
unify_proc_info::in, unify_proc_info::out) is det.
:- pred unify_proc__info_get_types(vartypes::out,
unify_proc_info::in, unify_proc_info::out) is det.
:- pred unify_proc__info_set_types(vartypes::in,
unify_proc_info::in, unify_proc_info::out) is det.
:- pred unify_proc__info_get_type_info_varmap(type_info_varmap::out,
unify_proc_info::in, unify_proc_info::out) is det.
:- pred unify_proc__info_get_module_info(module_info::out,
unify_proc_info::in, unify_proc_info::out) is det.
%-----------------------------------------------------------------------------%
% :- implementation
:- type unify_proc_info
---> unify_proc_info(
varset :: prog_varset,
vartypes :: vartypes,
type_info_varmap :: type_info_varmap,
module_info :: module_info
).
unify_proc__info_init(ModuleInfo, UPI) :-
varset__init(VarSet),
map__init(Types),
map__init(TVarMap),
UPI = unify_proc_info(VarSet, Types, TVarMap, ModuleInfo).
unify_proc__info_new_var(Type, Var, UPI,
(UPI^varset := VarSet) ^vartypes := Types) :-
varset__new_var(UPI^varset, Var, VarSet),
map__det_insert(UPI^vartypes, Var, Type, Types).
unify_proc__info_new_named_var(Type, Name, Var, UPI,
(UPI^varset := VarSet) ^vartypes := Types) :-
varset__new_named_var(UPI^varset, Name, Var, VarSet),
map__det_insert(UPI^vartypes, Var, Type, Types).
unify_proc__info_extract(UPI, UPI^varset, UPI^vartypes).
unify_proc__info_get_varset(UPI^varset, UPI, UPI).
unify_proc__info_get_types(UPI^vartypes, UPI, UPI).
unify_proc__info_get_type_info_varmap(UPI^type_info_varmap, UPI, UPI).
unify_proc__info_get_module_info(UPI^module_info, UPI, UPI).
unify_proc__info_set_varset(VarSet, UPI, UPI^varset := VarSet).
unify_proc__info_set_types(Types, UPI, UPI^vartypes := Types).
%-----------------------------------------------------------------------------%