mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-19 07:45:09 +00:00
Estimated hours taken: 4 Branches: main Divide the old 4100, 2900 and 4600 line modules ml_code_gen.m, ml_code_util.m and modes.m into smaller, more cohesive modules. In the process, put related predicates next to each other. There are no algorithmic changes. compiler/ml_proc_gen.m: New module that looks after code generation tasks that affect a procedure as a whole. Its code is taken from the old ml_code_gen.m. Analogous to proc_gen.m, which does the same job for the LLDS backend. compiler/ml_foreign_proc_gen.m: New module that generates code for foreign_proc goals. Its code is taken from the old ml_code_gen.m. Analogous to pragma_c_gen.m in the LLDS backend. compiler/ml_commit_gen.m: New module that generates code for commit goals. Its code is taken from the old ml_code_gen.m. Analogous to commit_gen.m in the LLDS backend. compiler/ml_gen_info.m: New module that encapsulates the ml_gen_info structure. Its code is taken from the old ml_code_util.m. Analogous to code_info.m in the LLDS backend. compiler/ml_accurate_gc.m: New module that generates the data and goals needed for accurate gc. Its code is taken from the old ml_code_util.m. compiler/ml_call_gen.m: compiler/ml_closure_gen.m: Move some predicates that are used by other modules of the MLDS backend to ml_code_util, in order to avoid otherwise unneeded dependencies. compiler/mode_util.m: Move a predicate here from ml_code_util.m, since it is needed by several MLDS backend modules but is not MLDS specific. compiler/ml_code_gen.m: Remove the code moved to other modules. Delete an old note about a problem fixed long ago. compiler/ml_code_util.m: Remove the code moved to other modules. Add the code moved here from other modules. compiler/modecheck_conj.m: New module that handles mode analysis of conjunctions. Its code is taken from the old modes.m. compiler/modecheck_goal.m: New module that handles mode analysis of most types of goals, except conjunctions, unifications and calls. Its code is taken from the old modes.m. compiler/modecheck_util.m: New module containing utility predicates used more one of the modules do mode analysis. Its code is taken from the old modes.m. compiler/mode_util.m: Move a predicate here from modes.m, since this is where a related predicate already is. Give a predicate a more meaningful name. compiler/goal_util.m: Move a predicate here from modes.m, since this is where a related predicate already is. compiler/modes.m: Remove the code moved to other modules. compiler/ml_backend.m: compiler/check_hlds.m: Add the new modules. compiler/notes/compiler_design.html: Document the new modules. compiler/prog_data.m: Give some function symbols disambiguating prefixes. compiler/add_solver.m: compiler/deforest.m: compiler/make_hlds_passes.m: compiler/mercury_compile.m: compiler/ml_lookup_switch.m: compiler/ml_simplify_switch.m: compiler/ml_string.m: compiler/ml_switch_gen.m: compiler/ml_tag_switch.m: compiler/ml_unify_gen.m: compiler/modecheck_call.m: compiler/modecheck_unify.m: compiler/prog_io_type_defn.m: compiler/rtti_to_mlds.m: compiler/type_ctor_info.m: compiler/unify_proc.m: Conform to the changes above.
2114 lines
84 KiB
Mathematica
2114 lines
84 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2009 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: 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 check_hlds.unify_proc.
|
|
:- interface.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_clauses.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
:- import_module queue.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type proc_requests.
|
|
:- type req_queue == queue(pred_proc_id).
|
|
|
|
:- pred get_req_queue(proc_requests::in, req_queue::out) is det.
|
|
:- pred set_req_queue(req_queue::in,
|
|
proc_requests::in, proc_requests::out) is det.
|
|
|
|
:- type unify_proc_id
|
|
---> unify_proc_id(type_ctor, uni_mode).
|
|
|
|
% Initialize the proc_requests table.
|
|
%
|
|
:- pred init_requests(proc_requests::out) is det.
|
|
|
|
% Add a new request for a unification procedure to the proc_requests table.
|
|
%
|
|
:- pred 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 request_proc(pred_id::in, list(mer_mode)::in, inst_varset::in,
|
|
maybe(list(is_live))::in, maybe(determinism)::in, prog_context::in,
|
|
proc_id::out, module_info::in, module_info::out) is det.
|
|
|
|
% add_lazily_generated_unify_pred(TypeCtor, UnifyPredId_for_Type,
|
|
% !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 add_lazily_generated_unify_pred(type_ctor::in, pred_id::out,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
% add_lazily_generated_compare_pred_decl(TypeCtor, ComparePredId_for_Type,
|
|
% !ModuleInfo):
|
|
%
|
|
% Add declarations, but not clauses, for a compare or index predicate.
|
|
%
|
|
:- pred add_lazily_generated_compare_pred_decl(type_ctor::in, pred_id::out,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
% Given the type and mode of a unification, look up the mode number
|
|
% for the unification proc.
|
|
%
|
|
:- pred lookup_mode_num(module_info::in, type_ctor::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/2, etc.)
|
|
%
|
|
:- pred generate_clause_info(special_pred_id::in, mer_type::in,
|
|
hlds_type_body::in, prog_context::in, module_info::in, clauses_info::out)
|
|
is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.clause_to_proc.
|
|
:- import_module check_hlds.inst_match.
|
|
:- import_module check_hlds.mode_util.
|
|
:- import_module check_hlds.modes.
|
|
:- import_module check_hlds.polymorphism.
|
|
:- import_module check_hlds.post_typecheck.
|
|
:- import_module check_hlds.type_util.
|
|
:- import_module check_hlds.unique_modes.
|
|
:- import_module hlds.goal_util.
|
|
:- import_module hlds.hlds_args.
|
|
:- import_module hlds.hlds_out.
|
|
:- import_module hlds.hlds_rtti.
|
|
:- import_module hlds.instmap.
|
|
:- import_module hlds.make_hlds.
|
|
:- import_module hlds.pred_table.
|
|
:- import_module hlds.quantification.
|
|
:- import_module hlds.special_pred.
|
|
:- import_module libs.
|
|
:- import_module libs.compiler_util.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module parse_tree.builtin_lib_types.
|
|
:- import_module parse_tree.error_util.
|
|
:- import_module parse_tree.prog_mode.
|
|
:- import_module parse_tree.prog_type.
|
|
:- import_module recompilation.
|
|
|
|
:- import_module bool.
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module pair.
|
|
:- import_module set.
|
|
:- import_module string.
|
|
:- import_module svmap.
|
|
:- import_module term.
|
|
:- import_module varset.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% 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 proc_requests
|
|
---> proc_requests(
|
|
unify_req_map :: unify_req_map,
|
|
% The assignment of proc_id numbers to
|
|
% unify_proc_ids.
|
|
req_queue :: req_queue
|
|
% The queue of procs we still to generate code
|
|
% for.
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
init_requests(Requests) :-
|
|
map.init(UnifyReqMap),
|
|
queue.init(ReqQueue),
|
|
Requests = proc_requests(UnifyReqMap, ReqQueue).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Boring access predicates
|
|
|
|
:- pred get_unify_req_map(proc_requests::in, unify_req_map::out) is det.
|
|
:- pred set_unify_req_map(unify_req_map::in,
|
|
proc_requests::in, proc_requests::out) is det.
|
|
|
|
get_unify_req_map(PR, PR ^ unify_req_map).
|
|
get_req_queue(PR, PR ^ req_queue).
|
|
set_unify_req_map(UnifyReqMap, PR, PR ^ unify_req_map := UnifyReqMap).
|
|
set_req_queue(ReqQueue, PR, PR ^ req_queue := ReqQueue).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
lookup_mode_num(ModuleInfo, TypeCtor, UniMode, Det, Num) :-
|
|
( search_mode_num(ModuleInfo, TypeCtor, UniMode, Det, Num1) ->
|
|
Num = Num1
|
|
;
|
|
unexpected(this_file, "search_num failed")
|
|
).
|
|
|
|
:- pred search_mode_num(module_info::in, type_ctor::in, uni_mode::in,
|
|
determinism::in, proc_id::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.
|
|
%
|
|
search_mode_num(ModuleInfo, TypeCtor, UniMode, Determinism, ProcId) :-
|
|
UniMode = (XInitial - YInitial -> _Final),
|
|
(
|
|
Determinism = detism_semi,
|
|
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),
|
|
get_unify_req_map(Requests, UnifyReqMap),
|
|
map.search(UnifyReqMap, unify_proc_id(TypeCtor, UniMode), ProcId)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
request_unify(UnifyId, InstVarSet, Determinism, Context, !ModuleInfo) :-
|
|
UnifyId = unify_proc_id(TypeCtor, UnifyMode),
|
|
|
|
% Generating a unification procedure for a type uses its body.
|
|
module_info_get_maybe_recompilation_info(!.ModuleInfo, MaybeRecompInfo0),
|
|
(
|
|
MaybeRecompInfo0 = yes(RecompInfo0),
|
|
TypeCtorItem = type_ctor_to_item_name(TypeCtor),
|
|
recompilation.record_used_item(type_body_item,
|
|
TypeCtorItem, TypeCtorItem, RecompInfo0, RecompInfo),
|
|
module_info_set_maybe_recompilation_info(yes(RecompInfo), !ModuleInfo)
|
|
;
|
|
MaybeRecompInfo0 = no
|
|
),
|
|
|
|
% Check if this unification has already been requested, or
|
|
% if the proc is hand defined.
|
|
(
|
|
(
|
|
search_mode_num(!.ModuleInfo, TypeCtor, UnifyMode, Determinism, _)
|
|
;
|
|
module_info_get_type_table(!.ModuleInfo, TypeTable),
|
|
search_type_ctor_defn(TypeTable, TypeCtor, TypeDefn),
|
|
hlds_data.get_type_defn_body(TypeDefn, TypeBody),
|
|
(
|
|
TypeCtor = type_ctor(TypeName, _TypeArity),
|
|
TypeName = qualified(TypeModuleName, _),
|
|
module_info_get_name(!.ModuleInfo, ModuleName),
|
|
ModuleName = TypeModuleName,
|
|
TypeBody = hlds_abstract_type(_)
|
|
;
|
|
type_ctor_has_hand_defined_rtti(TypeCtor, TypeBody)
|
|
)
|
|
)
|
|
->
|
|
true
|
|
;
|
|
% Lookup the pred_id for the unification procedure that we are
|
|
% going to generate.
|
|
module_info_get_special_pred_map(!.ModuleInfo, SpecialPredMap),
|
|
( map.search(SpecialPredMap, spec_pred_unify - TypeCtor, PredId0) ->
|
|
PredId = PredId0
|
|
;
|
|
% We generate unification predicates for most imported types
|
|
% lazily, so add the declarations and clauses now.
|
|
add_lazily_generated_unify_pred(TypeCtor, PredId, !ModuleInfo)
|
|
),
|
|
|
|
% Convert from `uni_mode' to `list(mer_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),
|
|
TypeCtor = type_ctor(_, TypeArity),
|
|
list.duplicate(TypeArity, InMode, TypeInfoModes),
|
|
ArgModes = TypeInfoModes ++ ArgModes0,
|
|
|
|
ArgLives = no, % XXX ArgLives should be part of the UnifyId
|
|
|
|
request_proc(PredId, ArgModes, InstVarSet, ArgLives, yes(Determinism),
|
|
Context, ProcId, !ModuleInfo),
|
|
|
|
% Save the proc_id for this unify_proc_id.
|
|
module_info_get_proc_requests(!.ModuleInfo, Requests0),
|
|
get_unify_req_map(Requests0, UnifyReqMap0),
|
|
map.set(UnifyReqMap0, UnifyId, ProcId, UnifyReqMap),
|
|
set_unify_req_map(UnifyReqMap, Requests0, Requests),
|
|
module_info_set_proc_requests(Requests, !ModuleInfo)
|
|
).
|
|
|
|
request_proc(PredId, ArgModes, InstVarSet, ArgLives, MaybeDet, Context, ProcId,
|
|
!ModuleInfo) :-
|
|
some [!PredInfo, !ProcInfo, !PredMap, !ProcMap, !Goal] (
|
|
% Create a new proc_info for this procedure.
|
|
module_info_preds(!.ModuleInfo, !:PredMap),
|
|
map.lookup(!.PredMap, PredId, !:PredInfo),
|
|
list.length(ArgModes, Arity),
|
|
DeclaredArgModes = no,
|
|
add_new_proc(InstVarSet, Arity, ArgModes, DeclaredArgModes, ArgLives,
|
|
MaybeDet, Context, address_is_not_taken, !PredInfo, 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_get_procedures(!.PredInfo, !:ProcMap),
|
|
pred_info_get_clauses_info(!.PredInfo, ClausesInfo),
|
|
map.lookup(!.ProcMap, ProcId, !:ProcInfo),
|
|
proc_info_set_can_process(no, !ProcInfo),
|
|
|
|
copy_clauses_to_proc(ProcId, ClausesInfo, !ProcInfo),
|
|
|
|
proc_info_get_goal(!.ProcInfo, !:Goal),
|
|
set_goal_contexts(Context, !Goal),
|
|
|
|
% The X == Y pretest on unifications makes sense only for in-in
|
|
% unifications, and if the initial insts are incompatible, then
|
|
% casts in the pretest would prevent mode analysis from discovering
|
|
% this fact.
|
|
(
|
|
all [ArgMode] (
|
|
list.member(ArgMode, ArgModes)
|
|
=>
|
|
mode_is_fully_input(!.ModuleInfo, ArgMode)
|
|
),
|
|
\+ MaybeDet = yes(detism_failure)
|
|
->
|
|
true
|
|
;
|
|
!:Goal = maybe_strip_equality_pretest(!.Goal)
|
|
),
|
|
proc_info_set_goal(!.Goal, !ProcInfo),
|
|
|
|
svmap.det_update(ProcId, !.ProcInfo, !ProcMap),
|
|
pred_info_set_procedures(!.ProcMap, !PredInfo),
|
|
svmap.det_update(PredId, !.PredInfo, !PredMap),
|
|
module_info_set_preds(!.PredMap, !ModuleInfo),
|
|
|
|
% Insert the pred_proc_id into the request queue.
|
|
module_info_get_proc_requests(!.ModuleInfo, Requests0),
|
|
get_req_queue(Requests0, ReqQueue0),
|
|
queue.put(ReqQueue0, proc(PredId, ProcId), ReqQueue),
|
|
set_req_queue(ReqQueue, Requests0, Requests),
|
|
module_info_set_proc_requests(Requests, !ModuleInfo)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
add_lazily_generated_unify_pred(TypeCtor, PredId, !ModuleInfo) :-
|
|
( type_ctor_is_tuple(TypeCtor) ->
|
|
TypeCtor = type_ctor(_, TupleArity),
|
|
|
|
% Build a hlds_type_body for the tuple constructor, which will
|
|
% be used by generate_clause_info.
|
|
varset.init(TVarSet0),
|
|
varset.new_vars(TVarSet0, TupleArity, TupleArgTVars, TVarSet),
|
|
prog_type.var_list_to_type_list(map.init, TupleArgTVars,
|
|
TupleArgTypes),
|
|
|
|
% Tuple constructors can't be existentially quantified.
|
|
ExistQVars = [],
|
|
ClassConstraints = [],
|
|
|
|
MakeUnamedField = (func(ArgType) = ctor_arg(no, ArgType, Context)),
|
|
CtorArgs = list.map(MakeUnamedField, TupleArgTypes),
|
|
|
|
CtorSymName = unqualified("{}"),
|
|
Ctor = ctor(ExistQVars, ClassConstraints, CtorSymName, CtorArgs,
|
|
Context),
|
|
|
|
ConsId = tuple_cons(TupleArity),
|
|
map.from_assoc_list([ConsId - single_functor_tag], ConsTagValues),
|
|
UnifyPred = no,
|
|
DuTypeKind = du_type_kind_general,
|
|
ReservedTag = does_not_use_reserved_tag,
|
|
ReservedAddr = does_not_use_reserved_address,
|
|
IsForeign = no,
|
|
TypeBody = hlds_du_type([Ctor], ConsTagValues, no_cheaper_tag_test,
|
|
DuTypeKind, UnifyPred, ReservedTag, ReservedAddr, IsForeign),
|
|
construct_type(TypeCtor, TupleArgTypes, Type),
|
|
|
|
term.context_init(Context)
|
|
;
|
|
collect_type_defn(!.ModuleInfo, TypeCtor, Type, TVarSet, TypeBody,
|
|
Context)
|
|
),
|
|
(
|
|
can_generate_special_pred_clauses_for_type(!.ModuleInfo,
|
|
TypeCtor, TypeBody)
|
|
->
|
|
% If the unification predicate has another status it should
|
|
% already have been generated.
|
|
UnifyPredStatus = status_pseudo_imported,
|
|
Item = clauses
|
|
;
|
|
UnifyPredStatus = status_imported(import_locn_implementation),
|
|
Item = declaration
|
|
),
|
|
add_lazily_generated_special_pred(spec_pred_unify, Item, TVarSet, Type,
|
|
TypeCtor, TypeBody, Context, UnifyPredStatus, PredId, !ModuleInfo).
|
|
|
|
add_lazily_generated_compare_pred_decl(TypeCtor, PredId, !ModuleInfo) :-
|
|
collect_type_defn(!.ModuleInfo, TypeCtor, Type, TVarSet, TypeBody,
|
|
Context),
|
|
|
|
% If the compare predicate has another status, it should already have been
|
|
% generated.
|
|
ImportStatus = status_imported(import_locn_implementation),
|
|
add_lazily_generated_special_pred(spec_pred_compare, declaration, TVarSet,
|
|
Type, TypeCtor, TypeBody, Context, ImportStatus, PredId, !ModuleInfo).
|
|
|
|
:- pred add_lazily_generated_special_pred(special_pred_id::in,
|
|
unify_pred_item::in, tvarset::in, mer_type::in, type_ctor::in,
|
|
hlds_type_body::in, context::in, import_status::in, pred_id::out,
|
|
module_info::in, module_info::out) is det.
|
|
|
|
add_lazily_generated_special_pred(SpecialId, Item, TVarSet, Type, TypeCtor,
|
|
TypeBody, Context, PredStatus, PredId, !ModuleInfo) :-
|
|
% Add the declaration and maybe clauses.
|
|
(
|
|
Item = clauses,
|
|
add_special_pred_for_real(SpecialId, TVarSet, Type, TypeCtor,
|
|
TypeBody, Context, PredStatus, !ModuleInfo)
|
|
;
|
|
Item = declaration,
|
|
add_special_pred_decl_for_real(SpecialId, TVarSet, Type, TypeCtor,
|
|
Context, PredStatus, !ModuleInfo)
|
|
),
|
|
|
|
module_info_get_special_pred_map(!.ModuleInfo, SpecialPredMap),
|
|
map.lookup(SpecialPredMap, SpecialId - TypeCtor, PredId),
|
|
module_info_pred_info(!.ModuleInfo, 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(!.ModuleInfo,
|
|
ErrorProcs, PredInfo0, PredInfo)
|
|
;
|
|
Item = declaration,
|
|
post_typecheck_finish_imported_pred_no_io(!.ModuleInfo,
|
|
ErrorProcs, PredInfo0, PredInfo)
|
|
),
|
|
expect(unify(ErrorProcs, []), this_file,
|
|
"add_lazily_generated_special_pred: error in post_typecheck"),
|
|
|
|
% Call polymorphism to introduce type_info arguments
|
|
% for polymorphic types.
|
|
module_info_set_pred_info(PredId, PredInfo, !ModuleInfo),
|
|
|
|
% 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 generate_clause_info, but unification predicates which
|
|
% contain such calls are never generated lazily.
|
|
polymorphism_process_generated_pred(PredId, !ModuleInfo).
|
|
|
|
:- type unify_pred_item
|
|
---> declaration
|
|
; clauses.
|
|
|
|
:- pred collect_type_defn(module_info::in, type_ctor::in, mer_type::out,
|
|
tvarset::out, hlds_type_body::out, prog_context::out) is det.
|
|
|
|
collect_type_defn(ModuleInfo, TypeCtor, Type, TVarSet, TypeBody, Context) :-
|
|
module_info_get_type_table(ModuleInfo, TypeTable),
|
|
lookup_type_ctor_defn(TypeTable, TypeCtor, TypeDefn),
|
|
hlds_data.get_type_defn_tvarset(TypeDefn, TVarSet),
|
|
hlds_data.get_type_defn_tparams(TypeDefn, TypeParams),
|
|
hlds_data.get_type_defn_kind_map(TypeDefn, KindMap),
|
|
hlds_data.get_type_defn_body(TypeDefn, TypeBody),
|
|
hlds_data.get_type_defn_status(TypeDefn, TypeStatus),
|
|
hlds_data.get_type_defn_context(TypeDefn, Context),
|
|
|
|
expect(special_pred_is_generated_lazily(ModuleInfo, TypeCtor, TypeBody,
|
|
TypeStatus), this_file, "add_lazily_generated_unify_pred"),
|
|
prog_type.var_list_to_type_list(KindMap, TypeParams, TypeArgs),
|
|
construct_type(TypeCtor, TypeArgs, Type).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
generate_clause_info(SpecialPredId, Type, TypeBody, Context, ModuleInfo,
|
|
ClauseInfo) :-
|
|
special_pred_interface(SpecialPredId, Type, ArgTypes, _Modes, _Det),
|
|
some [!Info] (
|
|
info_init(ModuleInfo, !:Info),
|
|
make_fresh_named_vars_from_types(ArgTypes, "HeadVar__", 1, Args,
|
|
!Info),
|
|
(
|
|
SpecialPredId = spec_pred_unify,
|
|
( Args = [X, Y] ->
|
|
generate_unify_proc_body(Type, TypeBody, X, Y,
|
|
Context, Clause, !Info)
|
|
;
|
|
unexpected(this_file, "generate_clause_info: bad unify args")
|
|
)
|
|
;
|
|
SpecialPredId = spec_pred_index,
|
|
( Args = [X, Index] ->
|
|
generate_index_proc_body(Type, TypeBody, X, Index,
|
|
Context, Clause, !Info)
|
|
;
|
|
unexpected(this_file, "generate_clause_info: bad index args")
|
|
)
|
|
;
|
|
SpecialPredId = spec_pred_compare,
|
|
( Args = [Res, X, Y] ->
|
|
generate_compare_proc_body(Type, TypeBody, Res, X, Y,
|
|
Context, Clause, !Info)
|
|
;
|
|
unexpected(this_file, "generate_clause_info: bad compare args")
|
|
)
|
|
;
|
|
SpecialPredId = spec_pred_init,
|
|
( Args = [X] ->
|
|
generate_initialise_proc_body(Type, TypeBody, X,
|
|
Context, Clause, !Info)
|
|
;
|
|
unexpected(this_file, "generate_clause_info: bad init args")
|
|
)
|
|
),
|
|
info_extract(!.Info, VarSet, Types)
|
|
),
|
|
map.init(TVarNameMap),
|
|
ArgVec = proc_arg_vector_init(pf_predicate, Args),
|
|
set_clause_list([Clause], ClausesRep),
|
|
rtti_varmaps_init(RttiVarMaps),
|
|
HasForeignClauses = yes,
|
|
ClauseInfo = clauses_info(VarSet, Types, TVarNameMap, Types, ArgVec,
|
|
ClausesRep, init_clause_item_numbers_comp_gen,
|
|
RttiVarMaps, HasForeignClauses).
|
|
|
|
:- pred generate_initialise_proc_body(mer_type::in, hlds_type_body::in,
|
|
prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_initialise_proc_body(_Type, TypeBody, X, Context, Clause, !Info) :-
|
|
info_get_module_info(!.Info, ModuleInfo),
|
|
(
|
|
% If this is an equivalence type then we just generate a call
|
|
% to the initialisation pred of the type on the RHS of the equivalence
|
|
% and cast the result back to the type on the LHS of the equivalence.
|
|
TypeBody = hlds_eqv_type(EqvType),
|
|
goal_info_init(Context, GoalInfo),
|
|
make_fresh_named_var_from_type(EqvType, "PreCast_HeadVar", 1, X0,
|
|
!Info),
|
|
type_to_ctor_det(EqvType, TypeCtor),
|
|
PredName = special_pred_name(spec_pred_init, TypeCtor),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
TypeCtor = type_ctor(TypeSymName, _TypeArity),
|
|
sym_name_get_module_name_default(TypeSymName, ModuleName,
|
|
TypeModuleName),
|
|
InitPred = qualified(TypeModuleName, PredName),
|
|
PredId = invalid_pred_id,
|
|
ModeId = invalid_proc_id,
|
|
InitCall = plain_call(PredId, ModeId, [X0], not_builtin, no, InitPred),
|
|
InitGoal = hlds_goal(InitCall, GoalInfo),
|
|
|
|
Any = any(shared, none),
|
|
generate_cast_with_insts(equiv_type_cast, X0, X, Any, Any, Context,
|
|
CastGoal),
|
|
Goal = hlds_goal(conj(plain_conj, [InitGoal, CastGoal]), GoalInfo),
|
|
quantify_clause_body([X], Goal, Context, Clause, !Info)
|
|
;
|
|
TypeBody = hlds_solver_type(SolverTypeDetails, _),
|
|
|
|
% 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.)
|
|
|
|
HowToInit = SolverTypeDetails ^ std_init_pred,
|
|
(
|
|
HowToInit = solver_init_automatic(InitPred)
|
|
;
|
|
HowToInit = solver_init_explicit,
|
|
unexpected(this_file, "generating initialise pred. for " ++
|
|
"solver type that does not have automatic initialisation.")
|
|
),
|
|
PredId = invalid_pred_id,
|
|
ModeId = invalid_proc_id,
|
|
Call = plain_call(PredId, ModeId, [X], not_builtin, no, InitPred),
|
|
goal_info_init(Context, GoalInfo),
|
|
Goal = hlds_goal(Call, GoalInfo),
|
|
quantify_clause_body([X], Goal, Context, Clause, !Info)
|
|
;
|
|
( TypeBody = hlds_du_type(_, _, _, _, _, _, _, _)
|
|
; TypeBody = hlds_foreign_type(_)
|
|
; TypeBody = hlds_abstract_type(_)
|
|
),
|
|
unexpected(this_file, "generate_initialise_proc_body: " ++
|
|
"trying to create initialisation proc for type " ++
|
|
"that has no solver_type_details")
|
|
).
|
|
|
|
:- pred generate_unify_proc_body(mer_type::in, hlds_type_body::in,
|
|
prog_var::in, prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_unify_proc_body(Type, TypeBody, X, Y, Context, Clause, !Info) :-
|
|
info_get_module_info(!.Info, ModuleInfo),
|
|
type_to_ctor_det(Type, TypeCtor),
|
|
(
|
|
check_builtin_dummy_type_ctor(TypeCtor) = is_builtin_dummy_type_ctor
|
|
->
|
|
Goal = true_goal_with_context(Context),
|
|
quantify_clause_body([X, Y], Goal, Context, Clause, !Info)
|
|
;
|
|
type_body_has_user_defined_equality_pred(ModuleInfo,
|
|
TypeBody, UserEqComp)
|
|
->
|
|
generate_user_defined_unify_proc_body(UserEqComp, X, Y, Context,
|
|
Clause, !Info)
|
|
;
|
|
(
|
|
TypeBody = hlds_du_type(Ctors, _, _, DuTypeKind, _, _, _, _),
|
|
(
|
|
( DuTypeKind = du_type_kind_mercury_enum
|
|
; DuTypeKind = du_type_kind_foreign_enum(_)
|
|
),
|
|
make_simple_test(X, Y, umc_explicit, [], Goal),
|
|
quantify_clause_body([X, Y], Goal, Context, Clause, !Info)
|
|
;
|
|
DuTypeKind = du_type_kind_direct_dummy,
|
|
Goal = true_goal_with_context(Context),
|
|
quantify_clause_body([X, Y], Goal, Context, Clause, !Info)
|
|
;
|
|
DuTypeKind = du_type_kind_notag(_, ArgType, _),
|
|
IsDummyType = check_dummy_type(ModuleInfo, ArgType),
|
|
(
|
|
IsDummyType = is_dummy_type,
|
|
% Treat this type as if it were a dummy type itself.
|
|
Goal = true_goal_with_context(Context),
|
|
quantify_clause_body([X, Y], Goal, Context, Clause, !Info)
|
|
;
|
|
IsDummyType = is_not_dummy_type,
|
|
generate_du_unify_proc_body(TypeCtor, Ctors, X, Y, Context,
|
|
Clause, !Info)
|
|
)
|
|
;
|
|
DuTypeKind = du_type_kind_general,
|
|
generate_du_unify_proc_body(TypeCtor, Ctors, X, Y, Context,
|
|
Clause, !Info)
|
|
)
|
|
;
|
|
TypeBody = hlds_eqv_type(EqvType),
|
|
IsDummyType = check_dummy_type(ModuleInfo, EqvType),
|
|
(
|
|
IsDummyType = is_dummy_type,
|
|
% Treat this type as if it were a dummy type itself.
|
|
Goal = true_goal_with_context(Context),
|
|
quantify_clause_body([X, Y], Goal, Context, Clause, !Info)
|
|
;
|
|
IsDummyType = is_not_dummy_type,
|
|
generate_eqv_unify_proc_body(EqvType, X, Y, Context,
|
|
Clause, !Info)
|
|
)
|
|
;
|
|
TypeBody = hlds_solver_type(_, _),
|
|
generate_default_solver_type_unify_proc_body(X, Y, Context,
|
|
Clause, !Info)
|
|
;
|
|
TypeBody = hlds_foreign_type(_),
|
|
% If no user defined equality predicate is given,
|
|
% we treat foreign_type as if they were an equivalent
|
|
% to the builtin type c_pointer.
|
|
generate_eqv_unify_proc_body(c_pointer_type, X, Y, Context,
|
|
Clause, !Info)
|
|
;
|
|
TypeBody = hlds_abstract_type(_),
|
|
( compiler_generated_rtti_for_builtins(ModuleInfo) ->
|
|
TypeCategory = classify_type(ModuleInfo, Type),
|
|
generate_builtin_unify(TypeCategory, X, Y, Context, Clause,
|
|
!Info)
|
|
;
|
|
unexpected(this_file,
|
|
"trying to create unify proc for abstract type")
|
|
)
|
|
)
|
|
).
|
|
|
|
:- pred generate_builtin_unify(type_ctor_category::in,
|
|
prog_var::in, prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_builtin_unify(CtorCat, X, Y, Context, Clause, !Info) :-
|
|
ArgVars = [X, Y],
|
|
|
|
% can_generate_special_pred_clauses_for_type ensures the unexpected
|
|
% cases can never occur.
|
|
(
|
|
CtorCat = ctor_cat_builtin(cat_builtin_int),
|
|
Name = "builtin_unify_int"
|
|
;
|
|
CtorCat = ctor_cat_builtin(cat_builtin_char),
|
|
Name = "builtin_unify_character"
|
|
;
|
|
CtorCat = ctor_cat_builtin(cat_builtin_string),
|
|
Name = "builtin_unify_string"
|
|
;
|
|
CtorCat = ctor_cat_builtin(cat_builtin_float),
|
|
Name = "builtin_unify_float"
|
|
;
|
|
CtorCat = ctor_cat_higher_order,
|
|
Name = "builtin_unify_pred"
|
|
;
|
|
( CtorCat = ctor_cat_tuple
|
|
; CtorCat = ctor_cat_enum(_)
|
|
; CtorCat = ctor_cat_builtin_dummy
|
|
; CtorCat = ctor_cat_variable
|
|
; CtorCat = ctor_cat_void
|
|
; CtorCat = ctor_cat_system(_)
|
|
; CtorCat = ctor_cat_user(_)
|
|
),
|
|
unexpected(this_file, "generate_builtin_unify: bad ctor category")
|
|
),
|
|
build_call(Name, ArgVars, Context, UnifyGoal, !Info),
|
|
quantify_clause_body(ArgVars, UnifyGoal, Context, Clause, !Info).
|
|
|
|
:- pred generate_user_defined_unify_proc_body(unify_compare::in,
|
|
prog_var::in, prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_user_defined_unify_proc_body(UserEqCompare, _, _, _, _, !Info) :-
|
|
UserEqCompare = abstract_noncanonical_type(_IsSolverType),
|
|
unexpected(this_file,
|
|
"trying to create unify proc for abstract noncanonical type").
|
|
generate_user_defined_unify_proc_body(UserEqCompare, X, Y, Context, Clause,
|
|
!Info) :-
|
|
UserEqCompare = unify_compare(MaybeUnify, MaybeCompare),
|
|
(
|
|
MaybeUnify = yes(UnifyPredName),
|
|
% 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.)
|
|
|
|
PredId = invalid_pred_id,
|
|
ModeId = invalid_proc_id,
|
|
Call = plain_call(PredId, ModeId, [X, Y], not_builtin, no,
|
|
UnifyPredName),
|
|
goal_info_init(Context, GoalInfo),
|
|
Goal0 = hlds_goal(Call, GoalInfo)
|
|
;
|
|
MaybeUnify = no,
|
|
(
|
|
MaybeCompare = yes(ComparePredName),
|
|
% Just generate a call to the specified predicate, which is the
|
|
% user-defined comparison pred for this type, and unify the result
|
|
% with `='. (The pred_id and proc_id will be figured out by type
|
|
% checking and mode analysis.)
|
|
|
|
info_new_var(comparison_result_type, ResultVar, !Info),
|
|
PredId = invalid_pred_id,
|
|
ModeId = invalid_proc_id,
|
|
Call = plain_call(PredId, ModeId, [ResultVar, X, Y], not_builtin,
|
|
no, ComparePredName),
|
|
goal_info_init(Context, GoalInfo),
|
|
CallGoal = hlds_goal(Call, GoalInfo),
|
|
|
|
create_pure_atomic_complicated_unification(ResultVar,
|
|
compare_functor("="), Context, umc_explicit, [], UnifyGoal),
|
|
Goal0 = hlds_goal(conj(plain_conj, [CallGoal, UnifyGoal]), GoalInfo)
|
|
;
|
|
MaybeCompare = no,
|
|
unexpected(this_file, "generate_user_defined_unify_proc_body")
|
|
)
|
|
),
|
|
maybe_wrap_with_pretest_equality(Context, X, Y, no, Goal0, Goal, !Info),
|
|
quantify_clause_body([X, Y], Goal, Context, Clause, !Info).
|
|
|
|
:- pred generate_eqv_unify_proc_body(mer_type::in, prog_var::in,
|
|
prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_eqv_unify_proc_body(EqvType, X, Y, Context, Clause, !Info) :-
|
|
% We should check whether EqvType 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.
|
|
make_fresh_named_var_from_type(EqvType, "Cast_HeadVar", 1, CastX, !Info),
|
|
make_fresh_named_var_from_type(EqvType, "Cast_HeadVar", 2, CastY, !Info),
|
|
generate_cast(equiv_type_cast, X, CastX, Context, CastXGoal),
|
|
generate_cast(equiv_type_cast, Y, CastY, Context, CastYGoal),
|
|
create_pure_atomic_complicated_unification(CastX, rhs_var(CastY),
|
|
Context, umc_explicit, [], UnifyGoal),
|
|
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
conj_list_to_goal([CastXGoal, CastYGoal, UnifyGoal], GoalInfo, Goal),
|
|
quantify_clause_body([X, Y], Goal, Context, Clause, !Info).
|
|
|
|
% 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 generate_index_proc_body(mer_type::in, hlds_type_body::in,
|
|
prog_var::in, prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_index_proc_body(Type, TypeBody, X, Index, Context, Clause, !Info) :-
|
|
info_get_module_info(!.Info, ModuleInfo),
|
|
( type_body_has_user_defined_equality_pred(ModuleInfo, TypeBody, _) ->
|
|
% For non-canonical types, the generated comparison predicate either
|
|
% calls a user-specified comparison predicate or returns an error,
|
|
% and does not call the type's index predicate, so do not generate
|
|
% an index predicate for such types.
|
|
unexpected(this_file,
|
|
"trying to create index proc for non-canonical type")
|
|
;
|
|
(
|
|
TypeBody = hlds_du_type(Ctors, _, _, DuTypeKind, _, _, _, _),
|
|
(
|
|
% 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.
|
|
DuTypeKind = du_type_kind_mercury_enum,
|
|
unexpected(this_file,
|
|
"trying to create index proc for enum type")
|
|
;
|
|
DuTypeKind = du_type_kind_foreign_enum(_),
|
|
unexpected(this_file,
|
|
"trying to create index proc for foreign enum type")
|
|
;
|
|
DuTypeKind = du_type_kind_direct_dummy,
|
|
unexpected(this_file,
|
|
"trying to create index proc for dummy type")
|
|
;
|
|
DuTypeKind = du_type_kind_notag(_, _, _),
|
|
unexpected(this_file,
|
|
"trying to create index proc for notag type")
|
|
;
|
|
DuTypeKind = du_type_kind_general,
|
|
type_to_ctor_det(Type, TypeCtor),
|
|
generate_du_index_proc_body(TypeCtor, Ctors, X, Index, Context,
|
|
Clause, !Info)
|
|
)
|
|
;
|
|
TypeBody = hlds_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.
|
|
unexpected(this_file, "trying to create index proc for eqv type")
|
|
;
|
|
TypeBody = hlds_foreign_type(_),
|
|
unexpected(this_file,
|
|
"trying to create index proc for a foreign type")
|
|
;
|
|
TypeBody = hlds_solver_type(_, _),
|
|
unexpected(this_file,
|
|
"trying to create index proc for a solver type")
|
|
;
|
|
TypeBody = hlds_abstract_type(_),
|
|
unexpected(this_file,
|
|
"trying to create index proc for abstract type")
|
|
)
|
|
).
|
|
|
|
:- pred generate_compare_proc_body(mer_type::in, hlds_type_body::in,
|
|
prog_var::in, prog_var::in, prog_var::in, prog_context::in,
|
|
clause::out, unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_compare_proc_body(Type, TypeBody, Res, X, Y, Context, Clause,
|
|
!Info) :-
|
|
info_get_module_info(!.Info, ModuleInfo),
|
|
(
|
|
type_to_ctor_det(Type, TypeCtor),
|
|
check_builtin_dummy_type_ctor(TypeCtor) = is_builtin_dummy_type_ctor
|
|
->
|
|
generate_dummy_compare_proc_body(Res, X, Y, Context, Clause, !Info)
|
|
;
|
|
type_body_has_user_defined_equality_pred(ModuleInfo, TypeBody,
|
|
UserEqComp)
|
|
->
|
|
generate_user_defined_compare_proc_body(UserEqComp,
|
|
Res, X, Y, Context, Clause, !Info)
|
|
;
|
|
(
|
|
TypeBody = hlds_du_type(Ctors, _, _, DuTypeKind, _, _, _, _),
|
|
(
|
|
( DuTypeKind = du_type_kind_mercury_enum
|
|
; DuTypeKind = du_type_kind_foreign_enum(_)
|
|
),
|
|
generate_enum_compare_proc_body(Res, X, Y, Context, Clause,
|
|
!Info)
|
|
;
|
|
DuTypeKind = du_type_kind_direct_dummy,
|
|
generate_dummy_compare_proc_body(Res, X, Y, Context, Clause,
|
|
!Info)
|
|
|
|
;
|
|
DuTypeKind = du_type_kind_notag(_, ArgType, _),
|
|
IsDummyType = check_dummy_type(ModuleInfo, ArgType),
|
|
(
|
|
IsDummyType = is_dummy_type,
|
|
% Treat this type as if it were a dummy type itself.
|
|
generate_dummy_compare_proc_body(Res, X, Y, Context,
|
|
Clause, !Info)
|
|
;
|
|
IsDummyType = is_not_dummy_type,
|
|
generate_du_compare_proc_body(Type, Ctors, Res, X, Y,
|
|
Context, Clause, !Info)
|
|
)
|
|
;
|
|
DuTypeKind = du_type_kind_general,
|
|
generate_du_compare_proc_body(Type, Ctors, Res, X, Y,
|
|
Context, Clause, !Info)
|
|
)
|
|
;
|
|
TypeBody = hlds_eqv_type(EqvType),
|
|
IsDummyType = check_dummy_type(ModuleInfo, EqvType),
|
|
(
|
|
IsDummyType = is_dummy_type,
|
|
% Treat this type as if it were a dummy type itself.
|
|
generate_dummy_compare_proc_body(Res, X, Y, Context, Clause,
|
|
!Info)
|
|
;
|
|
IsDummyType = is_not_dummy_type,
|
|
generate_eqv_compare_proc_body(EqvType, Res, X, Y,
|
|
Context, Clause, !Info)
|
|
)
|
|
;
|
|
TypeBody = hlds_foreign_type(_),
|
|
generate_eqv_compare_proc_body(c_pointer_type, Res, X, Y,
|
|
Context, Clause, !Info)
|
|
;
|
|
TypeBody = hlds_solver_type(_, _),
|
|
generate_default_solver_type_compare_proc_body(Res, X, Y,
|
|
Context, Clause, !Info)
|
|
;
|
|
TypeBody = hlds_abstract_type(_),
|
|
( compiler_generated_rtti_for_builtins(ModuleInfo) ->
|
|
TypeCategory = classify_type(ModuleInfo, Type),
|
|
generate_builtin_compare(TypeCategory, Res, X, Y,
|
|
Context, Clause, !Info)
|
|
;
|
|
unexpected(this_file,
|
|
"trying to create compare proc for abstract type")
|
|
)
|
|
)
|
|
).
|
|
|
|
% This should only used for the Erlang backend right now. We follow the
|
|
% Erlang order that tuples of smaller arity always precede tuples of larger
|
|
% arity.
|
|
%
|
|
:- pred compare_ctors_lexically(constructor::in, constructor::in,
|
|
comparison_result::out) is det.
|
|
|
|
compare_ctors_lexically(A, B, Res) :-
|
|
list.length(A ^ cons_args, ArityA),
|
|
list.length(B ^ cons_args, ArityB),
|
|
compare(ArityRes, ArityA, ArityB),
|
|
(
|
|
ArityRes = (=),
|
|
% XXX this assumes the string ordering used by the Mercury compiler is
|
|
% the same as that of the target language compiler
|
|
NameA = unqualify_name(A ^ cons_name),
|
|
NameB = unqualify_name(B ^ cons_name),
|
|
compare(Res, NameA, NameB)
|
|
;
|
|
( ArityRes = (<)
|
|
; ArityRes = (>)
|
|
),
|
|
Res = ArityRes
|
|
).
|
|
|
|
:- pred generate_enum_compare_proc_body(prog_var::in,
|
|
prog_var::in, prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_enum_compare_proc_body(Res, X, Y, Context, Clause, !Info) :-
|
|
IntType = int_type,
|
|
make_fresh_named_var_from_type(IntType, "Cast_HeadVar", 1, CastX, !Info),
|
|
make_fresh_named_var_from_type(IntType, "Cast_HeadVar", 2, CastY, !Info),
|
|
generate_cast(unsafe_type_cast, X, CastX, Context, CastXGoal),
|
|
generate_cast(unsafe_type_cast, Y, CastY, Context, CastYGoal),
|
|
build_call("builtin_compare_int", [Res, CastX, CastY], Context,
|
|
CompareGoal, !Info),
|
|
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
conj_list_to_goal([CastXGoal, CastYGoal, CompareGoal], GoalInfo, Goal),
|
|
quantify_clause_body([Res, X, Y], Goal, Context, Clause, !Info).
|
|
|
|
:- pred generate_dummy_compare_proc_body(prog_var::in, prog_var::in,
|
|
prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_dummy_compare_proc_body(Res, X, Y, Context, Clause, !Info) :-
|
|
generate_return_equal(Res, Context, Goal),
|
|
% XXX check me
|
|
quantify_clause_body([Res, X, Y], Goal, Context, Clause, !Info).
|
|
|
|
:- pred generate_builtin_compare(type_ctor_category::in,
|
|
prog_var::in, prog_var::in, prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_builtin_compare(CtorCat, Res, X, Y, Context, Clause, !Info) :-
|
|
ArgVars = [Res, X, Y],
|
|
|
|
% can_generate_special_pred_clauses_for_type ensures the unexpected
|
|
% cases can never occur.
|
|
(
|
|
CtorCat = ctor_cat_builtin(cat_builtin_int),
|
|
Name = "builtin_compare_int"
|
|
;
|
|
CtorCat = ctor_cat_builtin(cat_builtin_char),
|
|
Name = "builtin_compare_character"
|
|
;
|
|
CtorCat = ctor_cat_builtin(cat_builtin_string),
|
|
Name = "builtin_compare_string"
|
|
;
|
|
CtorCat = ctor_cat_builtin(cat_builtin_float),
|
|
Name = "builtin_compare_float"
|
|
;
|
|
CtorCat = ctor_cat_higher_order,
|
|
Name = "builtin_compare_pred"
|
|
;
|
|
( CtorCat = ctor_cat_tuple
|
|
; CtorCat = ctor_cat_enum(_)
|
|
; CtorCat = ctor_cat_builtin_dummy
|
|
; CtorCat = ctor_cat_variable
|
|
; CtorCat = ctor_cat_void
|
|
; CtorCat = ctor_cat_system(_)
|
|
; CtorCat = ctor_cat_user(_)
|
|
),
|
|
unexpected(this_file, "generate_builtin_compare: bad ctor category")
|
|
),
|
|
build_call(Name, ArgVars, Context, CompareGoal, !Info),
|
|
quantify_clause_body(ArgVars, CompareGoal, Context, Clause, !Info).
|
|
|
|
:- pred generate_default_solver_type_unify_proc_body(prog_var::in,
|
|
prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_default_solver_type_unify_proc_body(X, Y, Context, Clause, !Info) :-
|
|
ArgVars = [X, Y],
|
|
build_call("builtin_unify_solver_type", ArgVars, Context, Goal, !Info),
|
|
quantify_clause_body(ArgVars, Goal, Context, Clause, !Info).
|
|
|
|
:- pred generate_default_solver_type_compare_proc_body(prog_var::in,
|
|
prog_var::in, prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_default_solver_type_compare_proc_body(Res, X, Y, Context, Clause,
|
|
!Info) :-
|
|
ArgVars = [Res, X, Y],
|
|
build_call("builtin_compare_solver_type", ArgVars, Context, Goal, !Info),
|
|
quantify_clause_body(ArgVars, Goal, Context, Clause, !Info).
|
|
|
|
:- pred generate_user_defined_compare_proc_body(unify_compare::in,
|
|
prog_var::in, prog_var::in, prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_user_defined_compare_proc_body(abstract_noncanonical_type(_),
|
|
_, _, _, _, _, !Info) :-
|
|
unexpected(this_file,
|
|
"trying to create compare proc for abstract noncanonical type").
|
|
generate_user_defined_compare_proc_body(unify_compare(_, MaybeCompare),
|
|
Res, X, Y, Context, Clause, !Info) :-
|
|
ArgVars = [Res, X, Y],
|
|
(
|
|
MaybeCompare = yes(ComparePredName),
|
|
|
|
% Just generate a call to the specified predicate, which is the
|
|
% user-defined comparison pred for this type. (The pred_id and proc_id
|
|
% will be figured out by type checking and mode analysis.)
|
|
|
|
PredId = invalid_pred_id,
|
|
ModeId = invalid_proc_id,
|
|
Call = plain_call(PredId, ModeId, ArgVars, not_builtin, no,
|
|
ComparePredName),
|
|
goal_info_init(Context, GoalInfo),
|
|
Goal0 = hlds_goal(Call, GoalInfo),
|
|
maybe_wrap_with_pretest_equality(Context, X, Y, yes(Res), Goal0, Goal,
|
|
!Info)
|
|
;
|
|
MaybeCompare = no,
|
|
% Just generate code that will call error/1.
|
|
build_call("builtin_compare_non_canonical_type", ArgVars, Context,
|
|
Goal, !Info)
|
|
),
|
|
quantify_clause_body(ArgVars, Goal, Context, Clause, !Info).
|
|
|
|
:- pred generate_eqv_compare_proc_body(mer_type::in,
|
|
prog_var::in, prog_var::in, prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_eqv_compare_proc_body(EqvType, Res, X, Y, Context, Clause, !Info) :-
|
|
% We should check whether EqvType 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 comparison procedure directly; if it is a concrete type, we should
|
|
% generate the body of its comparison procedure inline here.
|
|
make_fresh_named_var_from_type(EqvType, "Cast_HeadVar", 1, CastX,
|
|
!Info),
|
|
make_fresh_named_var_from_type(EqvType, "Cast_HeadVar", 2, CastY,
|
|
!Info),
|
|
generate_cast(equiv_type_cast, X, CastX, Context, CastXGoal),
|
|
generate_cast(equiv_type_cast, Y, CastY, Context, CastYGoal),
|
|
build_call("compare", [Res, CastX, CastY], Context, CompareGoal,
|
|
!Info),
|
|
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
conj_list_to_goal([CastXGoal, CastYGoal, CompareGoal], GoalInfo, Goal),
|
|
quantify_clause_body([Res, X, Y], Goal, Context, Clause, !Info).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% For a type such as
|
|
%
|
|
% type t ---> a1 ; a2 ; b(int) ; c(float); d(int, string, t).
|
|
%
|
|
% we want to generate the code
|
|
%
|
|
% __Unify__(X, Y) :-
|
|
% (
|
|
% X = a1,
|
|
% Y = X
|
|
% % Actually, to avoid infinite recursion,
|
|
% % the above unification is done as type int:
|
|
% % CastX = unsafe_cast(X) `with_type` int,
|
|
% % CastY = unsafe_cast(Y) `with_type` int,
|
|
% % CastX = CastY
|
|
% ;
|
|
% X = a2,
|
|
% Y = X % Likewise, done as type int
|
|
% ;
|
|
% X = b(X1),
|
|
% Y = b(Y2),
|
|
% X1 = Y2,
|
|
% ;
|
|
% X = c(X1),
|
|
% Y = c(Y1),
|
|
% X1 = X2,
|
|
% ;
|
|
% X = d(X1, X2, X3),
|
|
% Y = c(Y1, Y2, Y3),
|
|
% X1 = y1,
|
|
% X2 = Y2,
|
|
% X3 = Y3
|
|
% ).
|
|
%
|
|
% Note that in the disjuncts handling constants, we want to unify Y with
|
|
% X, not with the constant. Doing this allows dupelim to take the code
|
|
% fragments implementing the switch arms for constants and eliminate all
|
|
% but one of them. This can be a significant code size saving for types
|
|
% with lots of constants which can lead to significant reductions in C
|
|
% compilation time. The keep_constant_binding feature on the cast goals is
|
|
% there to ask mode analysis to copy any known bound inst on the cast-from
|
|
% variable to the cast-to variable. This is necessary to keep determinism
|
|
% analysis working for modes in which the inputs of the unify predicate
|
|
% are known to be bound to the same constant, modes whose determinism
|
|
% should therefore be inferred to be det.
|
|
% (tests/general/det_complicated_unify2.m tests this case.)
|
|
%
|
|
:- pred generate_du_unify_proc_body(type_ctor::in, list(constructor)::in,
|
|
prog_var::in, prog_var::in, prog_context::in,
|
|
clause::out, unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_du_unify_proc_body(TypeCtor, Ctors, X, Y, Context, Clause, !Info) :-
|
|
CanCompareAsInt = can_compare_constants_as_ints(!.Info),
|
|
list.map_foldl(generate_du_unify_case(TypeCtor, X, Y, Context,
|
|
CanCompareAsInt), Ctors, Disjuncts, !Info),
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
Goal0 = hlds_goal(disj(Disjuncts), GoalInfo),
|
|
maybe_wrap_with_pretest_equality(Context, X, Y, no, Goal0, Goal, !Info),
|
|
quantify_clause_body([X, Y], Goal, Context, Clause, !Info).
|
|
|
|
:- pred generate_du_unify_case(type_ctor::in, prog_var::in, prog_var::in,
|
|
prog_context::in, bool::in, constructor::in, hlds_goal::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_du_unify_case(TypeCtor, X, Y, Context, CanCompareAsInt, Ctor, Goal,
|
|
!Info) :-
|
|
Ctor = ctor(ExistQTVars, _Constraints, FunctorName, ArgTypes, _Ctxt),
|
|
list.length(ArgTypes, FunctorArity),
|
|
( TypeCtor = type_ctor(unqualified("{}"), _) ->
|
|
FunctorConsId = tuple_cons(FunctorArity)
|
|
;
|
|
FunctorConsId = cons(FunctorName, FunctorArity, TypeCtor)
|
|
),
|
|
(
|
|
ArgTypes = [],
|
|
CanCompareAsInt = yes
|
|
->
|
|
RHS = rhs_functor(FunctorConsId, no, []),
|
|
create_pure_atomic_complicated_unification(X, RHS, Context,
|
|
umc_explicit, [], UnifyX_Goal),
|
|
info_new_named_var(int_type, "CastX", CastX, !Info),
|
|
info_new_named_var(int_type, "CastY", CastY, !Info),
|
|
generate_cast(unsafe_type_cast, X, CastX, Context, CastXGoal0),
|
|
generate_cast(unsafe_type_cast, Y, CastY, Context, CastYGoal0),
|
|
goal_add_feature(feature_keep_constant_binding, CastXGoal0, CastXGoal),
|
|
goal_add_feature(feature_keep_constant_binding, CastYGoal0, CastYGoal),
|
|
create_pure_atomic_complicated_unification(CastY, rhs_var(CastX),
|
|
Context, umc_explicit, [], UnifyY_Goal),
|
|
GoalList = [UnifyX_Goal, CastXGoal, CastYGoal, UnifyY_Goal]
|
|
;
|
|
make_fresh_vars(ArgTypes, ExistQTVars, Vars1, !Info),
|
|
make_fresh_vars(ArgTypes, ExistQTVars, Vars2, !Info),
|
|
RHS1 = rhs_functor(FunctorConsId, no, Vars1),
|
|
RHS2 = rhs_functor(FunctorConsId, no, Vars2),
|
|
create_pure_atomic_complicated_unification(X, RHS1, Context,
|
|
umc_explicit, [], UnifyX_Goal),
|
|
create_pure_atomic_complicated_unification(Y, RHS2, Context,
|
|
umc_explicit, [], UnifyY_Goal),
|
|
unify_var_lists(ArgTypes, ExistQTVars, Vars1, Vars2, UnifyArgs_Goals,
|
|
!Info),
|
|
GoalList = [UnifyX_Goal, UnifyY_Goal | UnifyArgs_Goals]
|
|
),
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
conj_list_to_goal(GoalList, GoalInfo, Goal).
|
|
|
|
% Succeed iff the target back end guarantees that comparing two constants
|
|
% for equality can be done by casting them both to integers and comparing
|
|
% the integers for equality.
|
|
%
|
|
:- func can_compare_constants_as_ints(unify_proc_info) = bool.
|
|
|
|
can_compare_constants_as_ints(Info) = CanCompareAsInt :-
|
|
ModuleInfo = Info ^ upi_module_info,
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
lookup_bool_option(Globals, can_compare_constants_as_ints,
|
|
CanCompareAsInt).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% For a type such as
|
|
%
|
|
% :- type foo ---> f ; g(a, b, c) ; h(foo).
|
|
%
|
|
% we want to generate the code
|
|
%
|
|
% index(X, Index) :-
|
|
% (
|
|
% X = f,
|
|
% Index = 0
|
|
% ;
|
|
% X = g(_, _, _),
|
|
% Index = 1
|
|
% ;
|
|
% X = h(_),
|
|
% Index = 2
|
|
% ).
|
|
:- pred generate_du_index_proc_body(type_ctor::in, list(constructor)::in,
|
|
prog_var::in, prog_var::in, prog_context::in, clause::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_du_index_proc_body(TypeCtor, Ctors, X, Index, Context, Clause,
|
|
!Info) :-
|
|
list.map_foldl2(generate_du_index_case(TypeCtor, X, Index, Context),
|
|
Ctors, Disjuncts, 0, _, !Info),
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(disj(Disjuncts), GoalInfo),
|
|
quantify_clause_body([X, Index], Goal, Context, Clause, !Info).
|
|
|
|
:- pred generate_du_index_case(type_ctor::in, prog_var::in, prog_var::in,
|
|
prog_context::in, constructor::in, hlds_goal::out, int::in, int::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_du_index_case(TypeCtor, X, Index, Context, Ctor, Goal, !N, !Info) :-
|
|
Ctor = ctor(ExistQTVars, _Constraints, FunctorName, ArgTypes, _Ctxt),
|
|
list.length(ArgTypes, FunctorArity),
|
|
FunctorConsId = cons(FunctorName, FunctorArity, TypeCtor),
|
|
make_fresh_vars(ArgTypes, ExistQTVars, ArgVars, !Info),
|
|
create_pure_atomic_complicated_unification(X,
|
|
rhs_functor(FunctorConsId, no, ArgVars),
|
|
Context, umc_explicit, [], UnifyX_Goal),
|
|
make_int_const_construction(Index, !.N, UnifyIndex_Goal),
|
|
!:N = !.N + 1,
|
|
GoalList = [UnifyX_Goal, UnifyIndex_Goal],
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
conj_list_to_goal(GoalList, GoalInfo, Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred generate_du_compare_proc_body(mer_type::in, list(constructor)::in,
|
|
prog_var::in, prog_var::in, prog_var::in, prog_context::in,
|
|
clause::out, unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_du_compare_proc_body(Type, Ctors0, Res, X, Y, Context, Clause,
|
|
!Info) :-
|
|
info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.lookup_bool_option(Globals, lexically_order_constructors,
|
|
LexicalOrder),
|
|
(
|
|
LexicalOrder = yes,
|
|
list.sort(compare_ctors_lexically, Ctors0, Ctors)
|
|
;
|
|
LexicalOrder = no,
|
|
Ctors = Ctors0
|
|
),
|
|
(
|
|
Ctors = [],
|
|
unexpected(this_file, "compare for type with no functors")
|
|
;
|
|
Ctors = [_ | _],
|
|
globals.lookup_int_option(Globals, compare_specialization,
|
|
CompareSpec),
|
|
list.length(Ctors, NumCtors),
|
|
( NumCtors =< CompareSpec ->
|
|
type_to_ctor_det(Type, TypeCtor),
|
|
generate_du_quad_compare_proc_body(TypeCtor, Ctors, Res, X, Y,
|
|
Context, Goal0, !Info)
|
|
;
|
|
generate_du_linear_compare_proc_body(Type, Ctors, Res, X, Y,
|
|
Context, Goal0, !Info)
|
|
),
|
|
maybe_wrap_with_pretest_equality(Context, X, Y, yes(Res), Goal0, Goal,
|
|
!Info),
|
|
HeadVars = [Res, X, Y],
|
|
quantify_clause_body(HeadVars, Goal, Context, Clause, !Info)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% For a du type, such as
|
|
%
|
|
% :- type foo ---> f(a) ; g(a, b, c) ; h.
|
|
%
|
|
% the quadratic code we want to generate is
|
|
%
|
|
% compare(Res, X, Y) :-
|
|
% (
|
|
% X = f(X1),
|
|
% Y = f(Y1),
|
|
% compare(R, X1, Y1)
|
|
% ;
|
|
% X = f(_),
|
|
% Y = g(_, _, _),
|
|
% R = (<)
|
|
% ;
|
|
% X = f(_),
|
|
% Y = h,
|
|
% 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)
|
|
% )
|
|
% ;
|
|
% X = g(_, _, _),
|
|
% Y = h,
|
|
% R = (<)
|
|
% ;
|
|
% X = f(_),
|
|
% Y = h,
|
|
% R = (<)
|
|
% ;
|
|
% X = g(_, _, _),
|
|
% Y = h,
|
|
% R = (<)
|
|
% ;
|
|
% X = h,
|
|
% Y = h,
|
|
% R = (<)
|
|
% ).
|
|
%
|
|
% Note that in the clauses handling two copies of the same constant,
|
|
% we unify Y with the constant, not with X. This is required to get
|
|
% switch_detection and det_analysis to recognize the determinism of the
|
|
% predicate.
|
|
%
|
|
:- pred generate_du_quad_compare_proc_body(type_ctor::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.
|
|
|
|
generate_du_quad_compare_proc_body(TypeCtor, Ctors, R, X, Y, Context, Goal,
|
|
!Info) :-
|
|
generate_du_quad_compare_switch_on_x(TypeCtor, Ctors, Ctors, R, X, Y,
|
|
Context, [], Cases, !Info),
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
disj_list_to_goal(Cases, GoalInfo, Goal).
|
|
|
|
:- pred generate_du_quad_compare_switch_on_x(type_ctor::in,
|
|
list(constructor)::in, list(constructor)::in,
|
|
prog_var::in, prog_var::in, prog_var::in,
|
|
prog_context::in, list(hlds_goal)::in, list(hlds_goal)::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_du_quad_compare_switch_on_x(_TypeCtor, [], _RightCtors, _R, _X, _Y,
|
|
_Context, !Cases, !Info).
|
|
generate_du_quad_compare_switch_on_x(TypeCtor, [LeftCtor | LeftCtors],
|
|
RightCtors, R, X, Y, Context, !Cases, !Info) :-
|
|
generate_du_quad_compare_switch_on_y(TypeCtor, LeftCtor, RightCtors,
|
|
">", R, X, Y, Context, !Cases, !Info),
|
|
generate_du_quad_compare_switch_on_x(TypeCtor, LeftCtors, RightCtors,
|
|
R, X, Y, Context, !Cases, !Info).
|
|
|
|
:- pred generate_du_quad_compare_switch_on_y(type_ctor::in,
|
|
constructor::in, list(constructor)::in, string::in,
|
|
prog_var::in, prog_var::in, prog_var::in, prog_context::in,
|
|
list(hlds_goal)::in, list(hlds_goal)::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_du_quad_compare_switch_on_y(_TypeCtor, _LeftCtor, [],
|
|
_Cmp, _R, _X, _Y, _Context, !Cases, !Info).
|
|
generate_du_quad_compare_switch_on_y(TypeCtor, LeftCtor,
|
|
[RightCtor | RightCtors], Cmp0, R, X, Y, Context, !Cases, !Info) :-
|
|
( LeftCtor = RightCtor ->
|
|
generate_compare_case(TypeCtor, LeftCtor, R, X, Y, Context, quad, Case,
|
|
!Info),
|
|
Cmp1 = "<"
|
|
;
|
|
generate_asymmetric_compare_case(TypeCtor, LeftCtor, RightCtor,
|
|
Cmp0, R, X, Y, Context, Case, !Info),
|
|
Cmp1 = Cmp0
|
|
),
|
|
generate_du_quad_compare_switch_on_y(TypeCtor, LeftCtor, RightCtors,
|
|
Cmp1, R, X, Y, Context, [Case | !.Cases], !:Cases, !Info).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% For a du type, such as
|
|
%
|
|
% :- type foo ---> f ; g(a) ; h(b, foo).
|
|
%
|
|
% the linear code we want to generate is
|
|
%
|
|
% 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 generate_compare_cases,
|
|
% % below.
|
|
% (
|
|
% X = 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
|
|
% ).
|
|
%
|
|
% Note that disjuncts covering constants do not test Y, since for constants
|
|
% X_Index = Y_Index implies X = Y.
|
|
%
|
|
:- pred generate_du_linear_compare_proc_body(mer_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.
|
|
|
|
generate_du_linear_compare_proc_body(Type, Ctors, Res, X, Y, Context, Goal,
|
|
!Info) :-
|
|
IntType = int_type,
|
|
info_new_var(IntType, X_Index, !Info),
|
|
info_new_var(IntType, Y_Index, !Info),
|
|
info_new_var(comparison_result_type, R, !Info),
|
|
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
|
|
X_InstmapDelta = instmap_delta_bind_var(X_Index),
|
|
build_specific_call(Type, spec_pred_index, [X, X_Index],
|
|
X_InstmapDelta, detism_det, Context, Call_X_Index, !Info),
|
|
Y_InstmapDelta = instmap_delta_bind_var(Y_Index),
|
|
build_specific_call(Type, spec_pred_index, [Y, Y_Index],
|
|
Y_InstmapDelta, detism_det, Context, Call_Y_Index, !Info),
|
|
|
|
build_call("builtin_int_lt", [X_Index, Y_Index], Context,
|
|
Call_Less_Than, !Info),
|
|
build_call("builtin_int_gt", [X_Index, Y_Index], Context,
|
|
Call_Greater_Than, !Info),
|
|
|
|
make_const_construction(Res, compare_cons_id("<"), Return_Less_Than),
|
|
make_const_construction(Res, compare_cons_id(">"), Return_Greater_Than),
|
|
|
|
create_pure_atomic_complicated_unification(Res, rhs_var(R), Context,
|
|
umc_explicit, [], Return_R),
|
|
|
|
type_to_ctor_det(Type, TypeCtor),
|
|
generate_compare_cases(TypeCtor, Ctors, R, X, Y, Context, Cases, !Info),
|
|
CasesGoal = hlds_goal(disj(Cases), GoalInfo),
|
|
|
|
build_call("compare_error", [], Context, Abort, !Info),
|
|
|
|
HandleEqualGoal =
|
|
hlds_goal(
|
|
if_then_else([], CasesGoal, Return_R, Abort),
|
|
GoalInfo),
|
|
HandleGreaterEqualGoal =
|
|
hlds_goal(
|
|
if_then_else([], Call_Greater_Than, Return_Greater_Than,
|
|
HandleEqualGoal),
|
|
GoalInfo),
|
|
HandleLessGreaterEqualGoal =
|
|
hlds_goal(
|
|
if_then_else([], Call_Less_Than, Return_Less_Than,
|
|
HandleGreaterEqualGoal),
|
|
GoalInfo),
|
|
Goal =
|
|
hlds_goal(
|
|
conj(plain_conj,
|
|
[Call_X_Index, Call_Y_Index, HandleLessGreaterEqualGoal]),
|
|
GoalInfo).
|
|
|
|
% 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 = X, % 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)
|
|
% )
|
|
% )
|
|
%
|
|
% Note that in the clauses for constants, we unify Y with X, not with
|
|
% the constant. This is to allow dupelim to eliminate all but one of
|
|
% the code fragments implementing such switch arms.
|
|
%
|
|
:- pred generate_compare_cases(type_ctor::in, list(constructor)::in,
|
|
prog_var::in, prog_var::in, prog_var::in, prog_context::in,
|
|
list(hlds_goal)::out, unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_compare_cases(_TypeCtor, [], _R, _X, _Y, _Context, [], !Info).
|
|
generate_compare_cases(TypeCtor, [Ctor | Ctors], R, X, Y, Context,
|
|
[Case | Cases], !Info) :-
|
|
generate_compare_case(TypeCtor, Ctor, R, X, Y, Context, linear, Case,
|
|
!Info),
|
|
generate_compare_cases(TypeCtor, Ctors, R, X, Y, Context, Cases, !Info).
|
|
|
|
:- type linear_or_quad
|
|
---> linear
|
|
; quad.
|
|
|
|
:- pred generate_compare_case(type_ctor::in, constructor::in, prog_var::in,
|
|
prog_var::in, prog_var::in, prog_context::in, linear_or_quad::in,
|
|
hlds_goal::out, unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
generate_compare_case(TypeCtor, Ctor, R, X, Y, Context, Kind, Case, !Info) :-
|
|
Ctor = ctor(ExistQTVars, _Constraints, FunctorName, ArgTypes, _Ctxt),
|
|
list.length(ArgTypes, FunctorArity),
|
|
FunctorConsId = cons(FunctorName, FunctorArity, TypeCtor),
|
|
(
|
|
ArgTypes = [],
|
|
RHS = rhs_functor(FunctorConsId, no, []),
|
|
create_pure_atomic_complicated_unification(X, RHS, Context,
|
|
umc_explicit, [], UnifyX_Goal),
|
|
generate_return_equal(R, Context, EqualGoal),
|
|
(
|
|
Kind = linear,
|
|
% The disjunct we are generating is executed only if the index
|
|
% values of X and Y are the same, so if X is bound to a constant,
|
|
% Y must also be bound to that same constant.
|
|
GoalList = [UnifyX_Goal, EqualGoal]
|
|
;
|
|
Kind = quad,
|
|
create_pure_atomic_complicated_unification(Y, RHS, Context,
|
|
umc_explicit, [], UnifyY_Goal),
|
|
GoalList = [UnifyX_Goal, UnifyY_Goal, EqualGoal]
|
|
)
|
|
;
|
|
ArgTypes = [_ | _],
|
|
make_fresh_vars(ArgTypes, ExistQTVars, Vars1, !Info),
|
|
make_fresh_vars(ArgTypes, ExistQTVars, Vars2, !Info),
|
|
RHS1 = rhs_functor(FunctorConsId, no, Vars1),
|
|
RHS2 = rhs_functor(FunctorConsId, no, Vars2),
|
|
create_pure_atomic_complicated_unification(X, RHS1, Context,
|
|
umc_explicit, [], UnifyX_Goal),
|
|
create_pure_atomic_complicated_unification(Y, RHS2, Context,
|
|
umc_explicit, [], UnifyY_Goal),
|
|
compare_args(ArgTypes, ExistQTVars, Vars1, Vars2, R,
|
|
Context, CompareArgs_Goal, !Info),
|
|
GoalList = [UnifyX_Goal, UnifyY_Goal, CompareArgs_Goal]
|
|
),
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
conj_list_to_goal(GoalList, GoalInfo, Case).
|
|
|
|
:- pred generate_asymmetric_compare_case(type_ctor::in,
|
|
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.
|
|
|
|
generate_asymmetric_compare_case(TypeCtor, Ctor1, Ctor2, CompareOp, R, X, Y,
|
|
Context, Case, !Info) :-
|
|
Ctor1 = ctor(ExistQTVars1, _Constraints1, FunctorName1, ArgTypes1, _Ctxt1),
|
|
Ctor2 = ctor(ExistQTVars2, _Constraints2, FunctorName2, ArgTypes2, _Ctxt2),
|
|
list.length(ArgTypes1, FunctorArity1),
|
|
list.length(ArgTypes2, FunctorArity2),
|
|
FunctorConsId1 = cons(FunctorName1, FunctorArity1, TypeCtor),
|
|
FunctorConsId2 = cons(FunctorName2, FunctorArity2, TypeCtor),
|
|
make_fresh_vars(ArgTypes1, ExistQTVars1, Vars1, !Info),
|
|
make_fresh_vars(ArgTypes2, ExistQTVars2, Vars2, !Info),
|
|
RHS1 = rhs_functor(FunctorConsId1, no, Vars1),
|
|
RHS2 = rhs_functor(FunctorConsId2, no, Vars2),
|
|
create_pure_atomic_complicated_unification(X, RHS1, Context,
|
|
umc_explicit, [], UnifyX_Goal),
|
|
create_pure_atomic_complicated_unification(Y, RHS2, Context,
|
|
umc_explicit, [], UnifyY_Goal),
|
|
make_const_construction(R, compare_cons_id(CompareOp), ReturnResult),
|
|
GoalList = [UnifyX_Goal, UnifyY_Goal, ReturnResult],
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
conj_list_to_goal(GoalList, GoalInfo, Case).
|
|
|
|
% 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 compare_args(list(constructor_arg)::in, existq_tvars::in,
|
|
list(prog_var)::in, list(prog_var)::in, prog_var::in, prog_context::in,
|
|
hlds_goal::out, unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
compare_args(ArgTypes, ExistQTVars, Xs, Ys, R, Context, Goal, !Info) :-
|
|
(
|
|
compare_args_2(ArgTypes, ExistQTVars, Xs, Ys, R, Context, Goal0, !Info)
|
|
->
|
|
Goal = Goal0
|
|
;
|
|
unexpected(this_file, "compare_args: length mismatch")
|
|
).
|
|
|
|
:- pred compare_args_2(list(constructor_arg)::in, existq_tvars::in,
|
|
list(prog_var)::in, list(prog_var)::in, prog_var::in, prog_context::in,
|
|
hlds_goal::out, unify_proc_info::in, unify_proc_info::out) is semidet.
|
|
|
|
compare_args_2([], _, [], [], R, Context, Return_Equal, !Info) :-
|
|
generate_return_equal(R, Context, Return_Equal).
|
|
compare_args_2([Arg | ArgTypes], ExistQTVars, [X | Xs], [Y | Ys], R,
|
|
Context, Goal, !Info) :-
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
|
|
Type = Arg ^ arg_type,
|
|
|
|
% 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.
|
|
(
|
|
some [ExistQTVar] (
|
|
list.member(ExistQTVar, ExistQTVars),
|
|
type_contains_var(Type, ExistQTVar)
|
|
)
|
|
->
|
|
ComparePred = "typed_compare"
|
|
;
|
|
ComparePred = "compare"
|
|
),
|
|
info_get_module_info(!.Info, ModuleInfo),
|
|
IsDummy = check_dummy_type(ModuleInfo, Type),
|
|
(
|
|
IsDummy = is_dummy_type,
|
|
% X and Y contain dummy values, so there is nothing to compare.
|
|
compare_args_2(ArgTypes, ExistQTVars, Xs, Ys, R, Context, Goal, !Info)
|
|
;
|
|
IsDummy = is_not_dummy_type,
|
|
(
|
|
Xs = [],
|
|
Ys = []
|
|
->
|
|
build_call(ComparePred, [R, X, Y], Context, Goal, !Info)
|
|
;
|
|
info_new_var(comparison_result_type, R1, !Info),
|
|
build_call(ComparePred, [R1, X, Y], Context, Do_Comparison, !Info),
|
|
|
|
make_const_construction(R1, compare_cons_id("="), Check_Equal),
|
|
Check_Not_Equal = hlds_goal(negation(Check_Equal), GoalInfo),
|
|
|
|
create_pure_atomic_complicated_unification(R, rhs_var(R1),
|
|
Context, umc_explicit, [], Return_R1),
|
|
Condition = hlds_goal(
|
|
conj(plain_conj, [Do_Comparison, Check_Not_Equal]),
|
|
GoalInfo),
|
|
compare_args_2(ArgTypes, ExistQTVars, Xs, Ys, R, Context, ElseCase,
|
|
!Info),
|
|
Goal = hlds_goal(
|
|
if_then_else([], Condition, Return_R1, ElseCase),
|
|
GoalInfo)
|
|
)
|
|
).
|
|
|
|
:- pred generate_return_equal(prog_var::in, prog_context::in,
|
|
hlds_goal::out) is det.
|
|
|
|
generate_return_equal(ResultVar, Context, Goal) :-
|
|
make_const_construction(ResultVar, compare_cons_id("="), Goal0),
|
|
goal_set_context(Context, Goal0, Goal).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred build_call(string::in, list(prog_var)::in, prog_context::in,
|
|
hlds_goal::out, unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
build_call(Name, ArgVars, Context, Goal, !Info) :-
|
|
info_get_module_info(!.Info, ModuleInfo),
|
|
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) ->
|
|
MercuryBuiltin = mercury_public_builtin_module
|
|
;
|
|
MercuryBuiltin = mercury_private_builtin_module
|
|
),
|
|
goal_util.generate_simple_call(MercuryBuiltin, Name, pf_predicate,
|
|
mode_no(0), detism_erroneous, purity_pure, ArgVars, [],
|
|
instmap_delta_bind_no_var, ModuleInfo, Context, Goal).
|
|
|
|
:- pred build_specific_call(mer_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.
|
|
|
|
build_specific_call(Type, SpecialPredId, ArgVars, InstmapDelta, Detism,
|
|
Context, Goal, !Info) :-
|
|
info_get_module_info(!.Info, ModuleInfo),
|
|
(
|
|
polymorphism.get_special_proc(Type, SpecialPredId, ModuleInfo,
|
|
PredName, PredId, ProcId)
|
|
->
|
|
GoalExpr = plain_call(PredId, ProcId, ArgVars, not_builtin, no,
|
|
PredName),
|
|
set.list_to_set(ArgVars, NonLocals),
|
|
goal_info_init(NonLocals, InstmapDelta, Detism, purity_pure,
|
|
GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, GoalInfo),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo)
|
|
;
|
|
% 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.
|
|
unexpected(this_file, "build_specific_call: lookup failed")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred make_fresh_named_var_from_type(mer_type::in, string::in, int::in,
|
|
prog_var::out, unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
make_fresh_named_var_from_type(Type, BaseName, Num, Var, !Info) :-
|
|
string.int_to_string(Num, NumStr),
|
|
string.append(BaseName, NumStr, Name),
|
|
info_new_named_var(Type, Name, Var, !Info).
|
|
|
|
:- pred make_fresh_named_vars_from_types(list(mer_type)::in, string::in,
|
|
int::in, list(prog_var)::out, unify_proc_info::in, unify_proc_info::out)
|
|
is det.
|
|
|
|
make_fresh_named_vars_from_types([], _, _, [], !Info).
|
|
make_fresh_named_vars_from_types([Type | Types], BaseName, Num,
|
|
[Var | Vars], !Info) :-
|
|
make_fresh_named_var_from_type(Type, BaseName, Num, Var, !Info),
|
|
make_fresh_named_vars_from_types(Types, BaseName, Num + 1, Vars, !Info).
|
|
|
|
:- pred make_fresh_vars_from_types(list(mer_type)::in, list(prog_var)::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
make_fresh_vars_from_types([], [], !Info).
|
|
make_fresh_vars_from_types([Type | Types], [Var | Vars], !Info) :-
|
|
info_new_var(Type, Var, !Info),
|
|
make_fresh_vars_from_types(Types, Vars, !Info).
|
|
|
|
:- pred make_fresh_vars(list(constructor_arg)::in, existq_tvars::in,
|
|
list(prog_var)::out, unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
make_fresh_vars(CtorArgs, ExistQTVars, Vars, !Info) :-
|
|
(
|
|
ExistQTVars = [],
|
|
ArgTypes = list.map(func(C) = C ^ arg_type, CtorArgs),
|
|
make_fresh_vars_from_types(ArgTypes, Vars, !Info)
|
|
;
|
|
ExistQTVars = [_ | _],
|
|
% 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.
|
|
info_get_varset(!.Info, VarSet0),
|
|
list.length(CtorArgs, NumVars),
|
|
varset.new_vars(VarSet0, NumVars, Vars, VarSet),
|
|
info_set_varset(VarSet, !Info)
|
|
).
|
|
|
|
:- pred unify_var_lists(list(constructor_arg)::in, existq_tvars::in,
|
|
list(prog_var)::in, list(prog_var)::in, list(hlds_goal)::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
unify_var_lists(ArgTypes, ExistQVars, Vars1, Vars2, Goal, !Info) :-
|
|
( unify_var_lists_2(ArgTypes, ExistQVars, Vars1, Vars2, Goal0, !Info) ->
|
|
Goal = Goal0
|
|
;
|
|
unexpected(this_file, "unify_var_lists: length mismatch")
|
|
).
|
|
|
|
:- pred unify_var_lists_2(list(constructor_arg)::in, existq_tvars::in,
|
|
list(prog_var)::in, list(prog_var)::in, list(hlds_goal)::out,
|
|
unify_proc_info::in, unify_proc_info::out) is semidet.
|
|
|
|
unify_var_lists_2([], _, [], [], [], !Info).
|
|
unify_var_lists_2([Arg | ArgTypes], ExistQTVars, [X | Xs], [Y | Ys],
|
|
[Goal | Goals], !Info) :-
|
|
Type = Arg ^ arg_type,
|
|
term.context_init(Context),
|
|
(
|
|
info_get_module_info(!.Info, ModuleInfo),
|
|
check_dummy_type(ModuleInfo, Type) = is_dummy_type
|
|
->
|
|
Goal = true_goal
|
|
;
|
|
% 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),
|
|
type_contains_var(Type, ExistQTVar)
|
|
->
|
|
build_call("typed_unify", [X, Y], Context, Goal, !Info)
|
|
;
|
|
create_pure_atomic_complicated_unification(X, rhs_var(Y),
|
|
Context, umc_explicit, [], Goal)
|
|
),
|
|
unify_var_lists_2(ArgTypes, ExistQTVars, Xs, Ys, Goals, !Info).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred maybe_wrap_with_pretest_equality(prog_context::in,
|
|
prog_var::in, prog_var::in, maybe(prog_var)::in,
|
|
hlds_goal::in, hlds_goal::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
maybe_wrap_with_pretest_equality(Context, X, Y, MaybeCompareRes, Goal0, Goal,
|
|
!Info) :-
|
|
ShouldPretestEq = should_pretest_equality(!.Info),
|
|
(
|
|
ShouldPretestEq = no,
|
|
Goal = Goal0
|
|
;
|
|
ShouldPretestEq = yes,
|
|
CastType = get_pretest_equality_cast_type(!.Info),
|
|
info_new_named_var(CastType, "CastX", CastX, !Info),
|
|
info_new_named_var(CastType, "CastY", CastY, !Info),
|
|
generate_cast(unsafe_type_cast, X, CastX, Context, CastXGoal0),
|
|
generate_cast(unsafe_type_cast, Y, CastY, Context, CastYGoal0),
|
|
goal_add_feature(feature_keep_constant_binding, CastXGoal0, CastXGoal),
|
|
goal_add_feature(feature_keep_constant_binding, CastYGoal0, CastYGoal),
|
|
create_pure_atomic_complicated_unification(CastX, rhs_var(CastY),
|
|
Context, umc_explicit, [], EqualityGoal0),
|
|
goal_add_feature(feature_pretest_equality_condition,
|
|
EqualityGoal0, EqualityGoal),
|
|
CondGoalExpr = conj(plain_conj, [CastXGoal, CastYGoal, EqualityGoal]),
|
|
goal_info_init(GoalInfo0),
|
|
goal_info_set_context(Context, GoalInfo0, ContextGoalInfo),
|
|
CondGoal = hlds_goal(CondGoalExpr, ContextGoalInfo),
|
|
(
|
|
MaybeCompareRes = no,
|
|
EqualGoal = true_goal_with_context(Context),
|
|
GoalInfo = ContextGoalInfo
|
|
;
|
|
MaybeCompareRes = yes(Res),
|
|
make_const_construction(Res, compare_cons_id("="), EqualGoal),
|
|
EqualGoal = hlds_goal(_, EqualGoalInfo),
|
|
InstmapDelta = goal_info_get_instmap_delta(EqualGoalInfo),
|
|
goal_info_set_instmap_delta(InstmapDelta,
|
|
ContextGoalInfo, GoalInfo)
|
|
),
|
|
GoalExpr = if_then_else([], CondGoal, EqualGoal, Goal0),
|
|
goal_info_add_feature(feature_pretest_equality, GoalInfo,
|
|
FeaturedGoalInfo),
|
|
Goal = hlds_goal(GoalExpr, FeaturedGoalInfo)
|
|
).
|
|
|
|
% We can start unify and compare predicates that may call other predicates
|
|
% with an equality test, since it often succeeds, and when it does, it is
|
|
% faster than executing the rest of the predicate body.
|
|
%
|
|
:- func should_pretest_equality(unify_proc_info) = bool.
|
|
|
|
should_pretest_equality(Info) = ShouldPretestEq :-
|
|
ModuleInfo = Info ^ upi_module_info,
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
lookup_bool_option(Globals, should_pretest_equality, ShouldPretestEq).
|
|
|
|
:- func get_pretest_equality_cast_type(unify_proc_info) = mer_type.
|
|
|
|
get_pretest_equality_cast_type(Info) = CastType :-
|
|
ModuleInfo = Info ^ upi_module_info,
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
lookup_bool_option(Globals, pretest_equality_cast_pointers, CastPointers),
|
|
(
|
|
CastPointers = yes,
|
|
CastType = c_pointer_type
|
|
;
|
|
CastPointers = no,
|
|
CastType = int_type
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred 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.
|
|
|
|
quantify_clause_body(HeadVars, Goal0, Context, Clause, !Info) :-
|
|
info_get_varset(!.Info, Varset0),
|
|
info_get_types(!.Info, Types0),
|
|
info_get_rtti_varmaps(!.Info, RttiVarMaps0),
|
|
implicitly_quantify_clause_body_general(ordinary_nonlocals_maybe_lambda,
|
|
HeadVars, _Warnings, Goal0, Goal,
|
|
Varset0, Varset, Types0, Types, RttiVarMaps0, RttiVarMaps),
|
|
info_set_varset(Varset, !Info),
|
|
info_set_types(Types, !Info),
|
|
info_set_rtti_varmaps(RttiVarMaps, !Info),
|
|
Clause = clause(all_modes, Goal, impl_lang_mercury, Context).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func compare_type_ctor = type_ctor.
|
|
|
|
compare_type_ctor = TypeCtor :-
|
|
Builtin = mercury_public_builtin_module,
|
|
TypeCtor = type_ctor(qualified(Builtin, "comparison_result"), 0).
|
|
|
|
:- func compare_cons_id(string) = cons_id.
|
|
|
|
compare_cons_id(Name) = cons(SymName, 0, compare_type_ctor) :-
|
|
SymName = qualified(mercury_public_builtin_module, Name).
|
|
|
|
:- func compare_functor(string) = unify_rhs.
|
|
|
|
compare_functor(Name) = rhs_functor(compare_cons_id(Name), no, []).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type unify_proc_info.
|
|
|
|
:- pred info_init(module_info::in, unify_proc_info::out) is det.
|
|
:- pred info_new_var(mer_type::in, prog_var::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
:- pred info_new_named_var(mer_type::in, string::in, prog_var::out,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
:- pred info_extract(unify_proc_info::in,
|
|
prog_varset::out, vartypes::out) is det.
|
|
|
|
:- pred info_get_varset(unify_proc_info::in, prog_varset::out) is det.
|
|
:- pred info_get_types(unify_proc_info::in, vartypes::out) is det.
|
|
:- pred info_get_rtti_varmaps(unify_proc_info::in, rtti_varmaps::out) is det.
|
|
:- pred info_get_module_info(unify_proc_info::in, module_info::out) is det.
|
|
|
|
:- pred info_set_varset(prog_varset::in,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
:- pred info_set_types(vartypes::in,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
:- pred info_set_rtti_varmaps(rtti_varmaps::in,
|
|
unify_proc_info::in, unify_proc_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type unify_proc_info
|
|
---> unify_proc_info(
|
|
upi_varset :: prog_varset,
|
|
upi_vartypes :: vartypes,
|
|
upi_rtti_varmaps :: rtti_varmaps,
|
|
upi_module_info :: module_info
|
|
).
|
|
|
|
info_init(ModuleInfo, UPI) :-
|
|
varset.init(VarSet),
|
|
map.init(Types),
|
|
rtti_varmaps_init(RttiVarMaps),
|
|
UPI = unify_proc_info(VarSet, Types, RttiVarMaps, ModuleInfo).
|
|
|
|
info_new_var(Type, Var, !UPI) :-
|
|
varset.new_var(!.UPI ^ upi_varset, Var, VarSet),
|
|
map.det_insert(!.UPI ^ upi_vartypes, Var, Type, VarTypes),
|
|
!:UPI = !.UPI ^ upi_varset := VarSet,
|
|
!:UPI = !.UPI ^ upi_vartypes := VarTypes.
|
|
|
|
info_new_named_var(Type, Name, Var, !UPI) :-
|
|
varset.new_named_var(!.UPI ^ upi_varset, Name, Var, VarSet),
|
|
map.det_insert(!.UPI ^ upi_vartypes, Var, Type, VarTypes),
|
|
!:UPI = !.UPI ^ upi_varset := VarSet,
|
|
!:UPI = !.UPI ^ upi_vartypes := VarTypes.
|
|
|
|
info_extract(UPI, UPI ^ upi_varset, UPI ^ upi_vartypes).
|
|
|
|
info_get_varset(UPI, UPI ^ upi_varset).
|
|
info_get_types(UPI, UPI ^ upi_vartypes).
|
|
info_get_rtti_varmaps(UPI, UPI ^ upi_rtti_varmaps).
|
|
info_get_module_info(UPI, UPI ^ upi_module_info).
|
|
|
|
info_set_varset(VarSet, UPI, UPI ^ upi_varset := VarSet).
|
|
info_set_types(Types, UPI, UPI ^ upi_vartypes := Types).
|
|
info_set_rtti_varmaps(RttiVarMaps, UPI, UPI ^ upi_rtti_varmaps := RttiVarMaps).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func this_file = string.
|
|
|
|
this_file = "unify_proc.m".
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module unify_proc.
|
|
%-----------------------------------------------------------------------------%
|