Files
mercury/compiler/untupling.m
Zoltan Somogyi 656493dfdf Use separate types for the status of different entity kinds.
We used the old import_status type to represent the status of six different
kinds of entities:

- types
- insts
- modes
- typeclasses
- instances
- predicates

even though some statuses that made sense for one kind of entity didn't for
another another (e.g. predicates can be pseudo imported/exported, but the
other five kinds of entities cannot).

Create the new types type_status, inst_status, ..., pred_status to represent
the status of these entities in the HLDS. For now, these are just wrappers
around the renamed old_import_status type, but I plan to replace them with
status types that *are* specialized to the applicable kind of entity,
along the lines of compiler/notes/status_proposal. This is a necessary
first step towards that proposal.

compiler/status.m:
    Define the six new entity-kind-specific status types, and replicate
    the test predicates that used to work on the import_status type
    to work on these instead.

    Define a status type, item_mercury_status, that contains just the info
    that is common to all entities in an item block, for use during
    the process of adding items to the HLDS.

    Move the predicates that converted section markers to statuses
    from here to make_hlds_passes.m, since that is the only place
    where they are used, or can be used.

    Move the combine_status predicate here from add_type.m, since
    it is needed for combining the statuses of other kinds of entities
    as well, not just types.

compiler/hlds_data.m:
    Change the HLDS types that record the information we have about
    types, du type fields, insts, modes, typeclasses and instances
    to have kind-specific status fields, instead of the old generic
    import_status type.

    Change the prefix on the field names of the hlds_instance_defn type
    to avoid a name clash, and to make them more meaningful.

    Change the prefix on the field names of the hlds_class_defn type
    to make them more meaningful.

compiler/hlds_pred.m:
    Change the HLDS type that records the information we have about predicates
    to have a kind-specific status field, instead of the old generic
    import_status type.

    Update the predicates that test predicate statuses accordingly.

compiler/hlds_module.m:
    Change the HLDS types that record the information we have about
    type constructors to be type_status, not the old generic import_status.

compiler/make_hlds_passes.m:
    As we process each item block, pass along an item_mercury_status
    instead of an import_status. The code used to use only a subset
    of the possible values of the import_status type, since we can never say
    that all the entities in an item block are e.g. pseudo-exported.
    An item_mercury_status has just the information we actually *know*
    about the item block as a whole. We convert the item_mercury_status
    to a kind-specific status if and when we need to, but for several purposes,
    the item_mercury_status is enough on its own.

    In a few cases, add a new predicate to do this conversion.

    Pass the need_qualifier flag separately from the status. It is needed
    in only a few places, but this was not apparent when we always passed it
    around paired with the import_status.

    Move the predicates that converted section markers to statuses
    to here from status.m, since here is the only place where they are used,
    or can be used.

compiler/add_class.m:
    Convert the statuses of typeclasses and instances to the statuses
    of the predicates implementing their virtual and concrete methods.

compiler/check_typeclass.m:
    Simplify some over-complex code.

compiler/add_special_pred.m:
    Convert the statuses of types to the statuses of the predicates
    implementing their unify, index, compare and solver init operations.
    Note some places where the process of this conversion is (to say the least)
    unclear and undocumented.

compiler/hlds_out_util.m:
    Provide utility predicates to print all the new kinds of statuses.
    These replace the old predicate that did the same in hlds_out_pred.m,
    but printing e.g. type statuses in hlds_out_pred doesn't seem right.

compiler/intermod.m:
    Conform to the changes above.

    Consistently use switches on the booleans returned by xxx_status_to_write,
    instead wrapping a semidet predicate around it and calling that.
    The switches yield code that is both smaller and more maintainable.

compiler/make_hlds_error.m:
    Conform to the changes above.

    Delete a simple wrapper predicate that was used only in one place.
    That place now does the wrapping itself.

compiler/qual_info.m:
    Replace the import_status field in the qual_info with a simple
    is_opt_imported/is_not_opt_imported flag, since that was the only
    thing we used the import_status field for.

compiler/accumulator.m:
compiler/add_clause.m:
compiler/add_foreign_enum.m:
compiler/add_foreign_proc.m:
compiler/add_mode.m:
compiler/add_mutable_aux_preds.m:
compiler/add_pragma.m:
compiler/add_pragma_tabling.m:
compiler/add_pragma_type_spec.m:
compiler/add_pred.m:
compiler/add_solver.m:
compiler/add_type.m:
compiler/base_typeclass_info.m:
compiler/ctgc.util.m:
compiler/dead_proc_elim.m:
compiler/dep_par_conj.m:
compiler/dependency_graph.m:
compiler/det_report.m:
compiler/elds_to_erlang.m:
compiler/equiv_type_hlds.m:
compiler/erl_code_gen.m:
compiler/export.m:
compiler/float_regs.m:
compiler/higher_order.m:
compiler/hlds_code_util.m:
compiler/hlds_out_module.m:
compiler/hlds_out_pred.m:
compiler/inst_check.m:
compiler/lambda.m:
compiler/lco.m:
compiler/make_hlds.m:
compiler/make_hlds_warn.m:
compiler/make_tags.m:
compiler/ml_proc_gen.m:
compiler/ml_type_gen.m:
compiler/mode_errors.m:
compiler/oisu_check.m:
compiler/par_loop_control.m:
compiler/polymorphism.m:
compiler/post_term_analysis.m:
compiler/post_typecheck.m:
compiler/prop_mode_constraints.m:
compiler/recompilation.usage.m:
compiler/simplify_proc.m:
compiler/smm_common.m:
compiler/special_pred.m:
compiler/ssdebug.m:
compiler/status.m:
compiler/stm_expand.m:
compiler/structure_reuse.analysis.m:
compiler/structure_reuse.direct.m:
compiler/structure_reuse.indirect.m:
compiler/structure_reuse.versions.m:
compiler/structure_sharing.analysis.m:
compiler/structure_sharing.domain.m:
compiler/superhomogeneous.m:
compiler/table_gen.m:
compiler/term_constr_initial.m:
compiler/term_constr_main.m:
compiler/termination.m:
compiler/trace_params.m:
compiler/type_class_info.m:
compiler/type_constraints.m:
compiler/type_ctor_info.m:
compiler/typecheck.m:
compiler/typecheck_info.m:
compiler/typeclasses.m:
compiler/unify_proc.m:
compiler/untupling.m:
compiler/unused_args.m:
compiler/unused_imports.m:
compiler/xml_documentation.m:
    Conform to the changes above.
2015-09-12 09:07:45 +10:00

777 lines
31 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2005-2012 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: untupling.m.
% Author: wangp.
%
% This module takes the HLDS and transforms the locally-defined procedures as
% follows: if the formal parameter of a procedure has a type consisting of a
% single function symbol then that parameter is expanded into multiple
% parameters (one for each field of the functor). Tuple types are also
% expanded. The argument lists are expanded as deeply (flatly) as possible.
%
% e.g. for the following predicate and types,
%
% :- type t ---> t(u).
% :- type u ---> u(v, w).
% :- type v ---> v1 ; v2.
% :- type w ---> w(int, string).
%
% :- pred f(t::in) is det.
% f(T) :- blah.
%
% a transformed version of f/1 would be added:
%
% :- pred f_untupled(v::in, int::in, string::in) is det.
% f_untupled(V, W1, W2) :- blah.
%
% After all the procedures have been processed in that way, a second pass is
% made to update all the calls in the module which refer to the old procedures
% to call the transformed procedures. This is done by adding deconstruction
% and construction unifications as needed, which can later be simplified by a
% simplification pass (not called from this module).
%
% e.g. a call to the predicate above,
%
% :- pred g(T::in) is det.
% g(_) :-
% A = 1,
% B = "foo",
% C = w(A, B),
% D = v1,
% E = u(D, C),
% F = t(E),
% f(F).
%
% is changed to this:
%
% g(_) :-
% A = 1,
% B = "foo",
% C = w(A, B),
% D = v1,
% E = u(D, C),
% F = t(E),
% F = t(G), % added deconstructions
% G = u(H, I),
% I = w(J, K),
% f_untupled(H, J, K).
%
% which, after simplication, should become:
%
% g(_) :-
% A = 1,
% B = "foo",
% D = v1,
% f_untupled(D, A, B).
%
% Limitations:
%
% - When a formal parameter is expanded, both the parameter's type and mode
% have to be expanded. Currently only arguments with in and out modes can
% be expanded, as I don't know how to do it for the general case.
% It should be enough for the majority of code.
%
% - Some predicates may or may not be expandable but won't be right now,
% because I don't understand the features they use (see expand_args_in_pred
% below).
%
% Julien says: "it should be possible for this transformation to work across
% module boundaries by exporting the goal templates [search for CallAux
% below] in the `.opt' files."
%
%-----------------------------------------------------------------------------%
:- module transform_hlds.untupling.
:- interface.
:- import_module hlds.hlds_module.
:- import_module io.
:- pred untuple_arguments(module_info::in, module_info::out, io::di, io::uo)
is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module check_hlds.mode_util.
:- import_module hlds.hlds_data.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_pred.
:- import_module hlds.make_goal.
:- import_module hlds.quantification.
:- import_module hlds.status.
:- import_module hlds.vartypes.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_mode.
:- import_module parse_tree.prog_type.
:- import_module parse_tree.prog_util.
:- import_module bool.
:- import_module counter.
:- import_module int.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module pair.
:- import_module require.
:- import_module string.
:- import_module term.
:- import_module varset.
%-----------------------------------------------------------------------------%
% The transform_map structure records which procedures were
% transformed into what procedures during the first pass.
%
:- type transform_map == map(pred_proc_id, transformed_proc).
:- type transformed_proc
---> transformed_proc(
pred_proc_id,
% A procedure that was generated by the
% untupling transformation.
hlds_goal
% A call goal template that is used to update
% calls referring to the old procedure to the
% new procedure.
).
untuple_arguments(!ModuleInfo, !IO) :-
expand_args_in_module(!ModuleInfo, TransformMap),
fix_calls_to_expanded_procs(TransformMap, !ModuleInfo).
%-----------------------------------------------------------------------------%
%
% Pass 1
%
% This is the top level of the first pass. It expands procedure
% arguments where possible, adding new versions of the transformed
% procedures into the module and recording the mapping between the old
% and new procedures in the transform map.
%
:- pred expand_args_in_module(module_info::in, module_info::out,
transform_map::out) is det.
expand_args_in_module(!ModuleInfo, TransformMap) :-
module_info_get_valid_pred_ids(!.ModuleInfo, PredIds),
list.foldl3(expand_args_in_pred, PredIds,
!ModuleInfo, map.init, TransformMap, counter.init(0), _).
:- pred expand_args_in_pred(pred_id::in, module_info::in, module_info::out,
transform_map::in, transform_map::out, counter::in, counter::out) is det.
expand_args_in_pred(PredId, !ModuleInfo, !TransformMap, !Counter) :-
module_info_get_type_table(!.ModuleInfo, TypeTable),
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
(
% Only perform the transformation on predicates which
% satisfy the following criteria.
pred_info_get_status(PredInfo, PredStatus),
pred_status_defined_in_this_module(PredStatus) = yes,
pred_info_get_goal_type(PredInfo, goal_type_clause),
% Some of these limitations may be able to be lifted later.
% For now, take the safe option and don't touch them.
pred_info_get_exist_quant_tvars(PredInfo, []),
pred_info_get_head_type_params(PredInfo, []),
pred_info_get_class_context(PredInfo, constraints([], [])),
pred_info_get_origin(PredInfo, origin_user(_)),
pred_info_get_arg_types(PredInfo, TypeVarSet, ExistQVars, ArgTypes),
varset.is_empty(TypeVarSet),
ExistQVars = [],
at_least_one_expandable_type(ArgTypes, TypeTable)
->
ProcIds = pred_info_non_imported_procids(PredInfo),
list.foldl3(expand_args_in_proc(PredId), ProcIds,
!ModuleInfo, !TransformMap, !Counter)
;
true
).
:- pred at_least_one_expandable_type(list(mer_type)::in, type_table::in)
is semidet.
at_least_one_expandable_type([Type | Types], TypeTable) :-
( expand_type(Type, [], TypeTable, expansion(_, _))
; at_least_one_expandable_type(Types, TypeTable)
).
%-----------------------------------------------------------------------------%
% This structure records the mapping between a head variable of the
% original procedure, and the list of variables that it was finally
% expanded into. If the head variable expands into some intermediate
% variables which are then expanded further, the intermediate
% variables are not listed in the mapping.
%
:- type untuple_map == map(prog_var, prog_vars).
:- pred expand_args_in_proc(pred_id::in, proc_id::in, module_info::in,
module_info::out, transform_map::in, transform_map::out,
counter::in, counter::out) is det.
expand_args_in_proc(PredId, ProcId, !ModuleInfo, !TransformMap, !Counter) :-
some [!ProcInfo] (
module_info_get_type_table(!.ModuleInfo, TypeTable),
module_info_pred_proc_info(!.ModuleInfo, PredId, ProcId,
PredInfo0, !:ProcInfo),
proc_info_get_headvars(!.ProcInfo, HeadVars0),
proc_info_get_argmodes(!.ProcInfo, ArgModes0),
proc_info_get_goal(!.ProcInfo, Goal0),
proc_info_get_vartypes(!.ProcInfo, VarTypes0),
proc_info_get_varset(!.ProcInfo, VarSet0),
expand_args_in_proc_2(HeadVars0, ArgModes0, HeadVars, ArgModes,
Goal0, Goal, VarSet0, VarSet, VarTypes0, VarTypes,
TypeTable, UntupleMap),
proc_info_set_headvars(HeadVars, !ProcInfo),
proc_info_set_argmodes(ArgModes, !ProcInfo),
proc_info_set_goal(Goal, !ProcInfo),
proc_info_set_varset(VarSet, !ProcInfo),
proc_info_set_vartypes(VarTypes, !ProcInfo),
requantify_proc_general(ordinary_nonlocals_no_lambda, !ProcInfo),
recompute_instmap_delta_proc(recompute_atomic_instmap_deltas,
!ProcInfo, !ModuleInfo),
counter.allocate(Num, !Counter),
create_aux_pred(PredId, ProcId, PredInfo0, !.ProcInfo, Num,
AuxPredId, AuxProcId, CallAux,
AuxPredInfo, AuxProcInfo0, !ModuleInfo),
proc_info_set_maybe_untuple_info(
yes(untuple_proc_info(UntupleMap)),
AuxProcInfo0, AuxProcInfo),
module_info_set_pred_proc_info(AuxPredId, AuxProcId,
AuxPredInfo, AuxProcInfo, !ModuleInfo),
map.det_insert(proc(PredId, ProcId),
transformed_proc(proc(AuxPredId, AuxProcId), CallAux),
!TransformMap)
).
:- pred expand_args_in_proc_2(prog_vars::in, list(mer_mode)::in,
prog_vars::out, list(mer_mode)::out, hlds_goal::in, hlds_goal::out,
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
type_table::in, untuple_map::out) is det.
expand_args_in_proc_2(HeadVars0, ArgModes0, HeadVars, ArgModes,
Goal0, hlds_goal(GoalExpr, GoalInfo), !VarSet, !VarTypes, TypeTable,
UntupleMap) :-
expand_args_in_proc_3(HeadVars0, ArgModes0, ListOfHeadVars,
ListOfArgModes, Goal0, hlds_goal(GoalExpr, GoalInfo1), !VarSet,
!VarTypes, [], TypeTable),
Context = goal_info_get_context(Goal0 ^ hlds_goal_info),
goal_info_set_context(Context, GoalInfo1, GoalInfo),
list.condense(ListOfHeadVars, HeadVars),
list.condense(ListOfArgModes, ArgModes),
build_untuple_map(HeadVars0, ListOfHeadVars, map.init, UntupleMap).
:- pred expand_args_in_proc_3(list(prog_var)::in, list(mer_mode)::in,
list(list(prog_var))::out, list(list(mer_mode))::out,
hlds_goal::in, hlds_goal::out, prog_varset::in, prog_varset::out,
vartypes::in, vartypes::out, list(mer_type)::in, type_table::in) is det.
expand_args_in_proc_3([], [], [], [], !_, !_, !_, _, _).
expand_args_in_proc_3([HeadVar0 | HeadVars0], [ArgMode0 | ArgModes0],
[HeadVar | HeadVars], [ArgMode | ArgModes],
!Goal, !VarSet, !VarTypes, ContainerTypes, TypeTable) :-
expand_one_arg_in_proc(HeadVar0, ArgMode0, HeadVar, ArgMode,
!Goal, !VarSet, !VarTypes, ContainerTypes, TypeTable),
expand_args_in_proc_3(HeadVars0, ArgModes0, HeadVars, ArgModes,
!Goal, !VarSet, !VarTypes, ContainerTypes, TypeTable).
expand_args_in_proc_3([], [_|_], _, _, !_, !_, !_, _, _) :-
unexpected($module, $pred, "length mismatch").
expand_args_in_proc_3([_|_], [], _, _, !_, !_, !_, _, _) :-
unexpected($module, $pred, "length mismatch").
:- pred expand_one_arg_in_proc(prog_var::in, mer_mode::in, prog_vars::out,
list(mer_mode)::out, hlds_goal::in, hlds_goal::out, prog_varset::in,
prog_varset::out, vartypes::in, vartypes::out, list(mer_type)::in,
type_table::in) is det.
expand_one_arg_in_proc(HeadVar0, ArgMode0, HeadVars, ArgModes,
!Goal, !VarSet, !VarTypes, ContainerTypes0, TypeTable) :-
expand_one_arg_in_proc_2(HeadVar0, ArgMode0, MaybeHeadVarsAndArgModes,
!Goal, !VarSet, !VarTypes, ContainerTypes0, ContainerTypes, TypeTable),
(
MaybeHeadVarsAndArgModes = yes(HeadVars1 - ArgModes1),
expand_args_in_proc_3(HeadVars1, ArgModes1,
ListOfHeadVars, ListOfArgModes, !Goal, !VarSet, !VarTypes,
ContainerTypes, TypeTable),
HeadVars = list.condense(ListOfHeadVars),
ArgModes = list.condense(ListOfArgModes)
;
MaybeHeadVarsAndArgModes = no,
HeadVars = [HeadVar0],
ArgModes = [ArgMode0]
).
:- pred expand_one_arg_in_proc_2(prog_var::in, mer_mode::in,
maybe(pair(list(prog_var), list(mer_mode)))::out,
hlds_goal::in, hlds_goal::out, prog_varset::in, prog_varset::out,
vartypes::in, vartypes::out, list(mer_type)::in, list(mer_type)::out,
type_table::in) is det.
expand_one_arg_in_proc_2(HeadVar0, ArgMode0, MaybeHeadVarsAndArgModes,
!Goal, !VarSet, !VarTypes, ContainerTypes0, ContainerTypes,
TypeTable) :-
lookup_var_type(!.VarTypes, HeadVar0, Type),
expand_argument(ArgMode0, Type, ContainerTypes0, TypeTable, Expansion),
(
Expansion = expansion(ConsId, NewTypes),
varset.lookup_name(!.VarSet, HeadVar0, ParentName),
create_untuple_vars(ParentName, 0, NewTypes, NewHeadVars,
!VarSet, !VarTypes),
list.duplicate(list.length(NewHeadVars), ArgMode0, NewArgModes),
MaybeHeadVarsAndArgModes = yes(NewHeadVars - NewArgModes),
( ArgMode0 = in_mode ->
construct_functor(HeadVar0, ConsId, NewHeadVars, UnifGoal),
conjoin_goals_keep_detism(UnifGoal, !Goal)
; ArgMode0 = out_mode ->
deconstruct_functor(HeadVar0, ConsId, NewHeadVars, UnifGoal),
conjoin_goals_keep_detism(!.Goal, UnifGoal, !:Goal)
;
unexpected($module, $pred, "unsupported mode")
),
ContainerTypes = [Type | ContainerTypes0]
;
Expansion = no_expansion,
MaybeHeadVarsAndArgModes = no,
ContainerTypes = ContainerTypes0
).
:- pred create_untuple_vars(string::in, int::in, list(mer_type)::in,
list(prog_var)::out, prog_varset::in, prog_varset::out,
vartypes::in, vartypes::out) is det.
create_untuple_vars(_, _, [], [], !VarSet, !VarTypes).
create_untuple_vars(ParentName, Num, [Type | Types], [NewVar | NewVars],
!VarSet, !VarTypes) :-
string.format("Untupled_%s_%d", [s(ParentName), i(Num)], Name),
varset.new_named_var(Name, NewVar, !VarSet),
add_var_type(NewVar, Type, !VarTypes),
create_untuple_vars(ParentName, Num+1, Types, NewVars, !VarSet, !VarTypes).
:- pred conjoin_goals_keep_detism(hlds_goal::in, hlds_goal::in,
hlds_goal::out) is det.
conjoin_goals_keep_detism(GoalA, GoalB, Goal) :-
goal_to_conj_list(GoalA, GoalListA),
goal_to_conj_list(GoalB, GoalListB),
list.append(GoalListA, GoalListB, GoalList),
goal_list_determinism(GoalList, Determinism),
goal_info_init(GoalInfo0),
goal_info_set_determinism(Determinism, GoalInfo0, GoalInfo),
Goal = hlds_goal(conj(plain_conj, GoalList), GoalInfo).
:- pred build_untuple_map(list(prog_var)::in, list(list(prog_var))::in,
untuple_map::in, untuple_map::out) is det.
build_untuple_map([], [], !UntupleMap).
build_untuple_map([OldVar | OldVars], [NewVars | NewVarss], !UntupleMap) :-
( NewVars = [OldVar] ->
build_untuple_map(OldVars, NewVarss, !UntupleMap)
;
map.det_insert(OldVar, NewVars, !UntupleMap),
build_untuple_map(OldVars, NewVarss, !UntupleMap)
).
build_untuple_map([], [_| _], !_) :-
unexpected($module, $pred, "length mismatch").
build_untuple_map([_| _], [], !_) :-
unexpected($module, $pred, "length mismatch").
%-----------------------------------------------------------------------------%
% This predicate makes a new version of the given procedure in a
% module. Amongst other things the new procedure is given a new
% pred_id and proc_id, a new name and a new goal.
%
% CallAux is an output variable, which is unified with a goal that
% can be used as a template for constructing calls to the newly
% created procedure.
%
% See also create_aux_pred in loop_inv.m.
%
:- pred create_aux_pred(pred_id::in, proc_id::in, pred_info::in,
proc_info::in, int::in, pred_id::out, proc_id::out, hlds_goal::out,
pred_info::out, proc_info::out, module_info::in, module_info::out)
is det.
create_aux_pred(PredId, ProcId, PredInfo, ProcInfo, Counter,
AuxPredId, AuxProcId, CallAux, AuxPredInfo, AuxProcInfo,
!ModuleInfo) :-
proc_info_get_headvars(ProcInfo, AuxHeadVars),
proc_info_get_goal(ProcInfo, Goal @ hlds_goal(_GoalExpr, GoalInfo)),
proc_info_get_initial_instmap(ProcInfo, !.ModuleInfo, InitialAuxInstMap),
pred_info_get_typevarset(PredInfo, TVarSet),
proc_info_get_vartypes(ProcInfo, VarTypes),
pred_info_get_class_context(PredInfo, ClassContext),
proc_info_get_rtti_varmaps(ProcInfo, RttiVarMaps),
proc_info_get_varset(ProcInfo, VarSet),
proc_info_get_inst_varset(ProcInfo, InstVarSet),
pred_info_get_markers(PredInfo, Markers),
pred_info_get_origin(PredInfo, OrigOrigin),
proc_info_get_has_parallel_conj(ProcInfo, HasParallelConj),
pred_info_get_var_name_remap(PredInfo, VarNameRemap),
PredModule = pred_info_module(PredInfo),
PredName = pred_info_name(PredInfo),
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
Context = goal_info_get_context(GoalInfo),
term.context_line(Context, Line),
make_pred_name_with_context(PredModule, "untupling",
PredOrFunc, PredName, Line, Counter, AuxPredSymName0),
proc_id_to_int(ProcId, ProcNo),
Suffix = string.format("_%d", [i(ProcNo)]),
add_sym_name_suffix(AuxPredSymName0, Suffix, AuxPredSymName),
Origin = origin_transformed(transform_untuple(ProcNo), OrigOrigin, PredId),
hlds_pred.define_new_pred(Origin, Goal, CallAux, AuxHeadVars, _ExtraArgs,
InitialAuxInstMap, AuxPredSymName, TVarSet, VarTypes, ClassContext,
RttiVarMaps, VarSet, InstVarSet, Markers, address_is_not_taken,
HasParallelConj, VarNameRemap, !ModuleInfo, AuxPredProcId),
AuxPredProcId = proc(AuxPredId, AuxProcId),
module_info_pred_proc_info(!.ModuleInfo, AuxPredId, AuxProcId,
AuxPredInfo, AuxProcInfo).
%-----------------------------------------------------------------------------%
%
% Pass 2
%
% This is the top level of the second pass. It takes the transform map
% built during the first pass as input. For every call to a procedure
% in the transform map, it rewrites the call to use the new procedure
% instead, inserting unifications before and after the call as necessary.
%
:- pred fix_calls_to_expanded_procs(transform_map::in, module_info::in,
module_info::out) is det.
fix_calls_to_expanded_procs(TransformMap, !ModuleInfo) :-
module_info_get_valid_pred_ids(!.ModuleInfo, PredIds),
list.foldl(fix_calls_in_pred(TransformMap), PredIds, !ModuleInfo).
:- pred fix_calls_in_pred(transform_map::in, pred_id::in, module_info::in,
module_info::out) is det.
fix_calls_in_pred(TransformMap, PredId, !ModuleInfo) :-
module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
ProcIds = pred_info_non_imported_procids(PredInfo),
list.foldl(fix_calls_in_proc(TransformMap, PredId), ProcIds, !ModuleInfo).
:- pred fix_calls_in_proc(transform_map::in, pred_id::in, proc_id::in,
module_info::in, module_info::out) is det.
fix_calls_in_proc(TransformMap, PredId, ProcId, !ModuleInfo) :-
some [!ProcInfo] (
module_info_pred_proc_info(!.ModuleInfo, PredId, ProcId,
PredInfo, !:ProcInfo),
proc_info_get_goal(!.ProcInfo, Goal0),
proc_info_get_vartypes(!.ProcInfo, VarTypes0),
proc_info_get_varset(!.ProcInfo, VarSet0),
fix_calls_in_goal(Goal0, Goal, VarSet0, VarSet,
VarTypes0, VarTypes, TransformMap, !.ModuleInfo),
( Goal0 \= Goal ->
proc_info_set_goal(Goal, !ProcInfo),
proc_info_set_varset(VarSet, !ProcInfo),
proc_info_set_vartypes(VarTypes, !ProcInfo),
requantify_proc_general(ordinary_nonlocals_no_lambda, !ProcInfo),
recompute_instmap_delta_proc(recompute_atomic_instmap_deltas,
!ProcInfo, !ModuleInfo),
module_info_set_pred_proc_info(PredId, ProcId,
PredInfo, !.ProcInfo, !ModuleInfo)
;
true
)
).
%-----------------------------------------------------------------------------%
:- pred fix_calls_in_goal(hlds_goal::in, hlds_goal::out, prog_varset::in,
prog_varset::out, vartypes::in, vartypes::out, transform_map::in,
module_info::in) is det.
fix_calls_in_goal(Goal0, Goal, !VarSet, !VarTypes, TransformMap, ModuleInfo) :-
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
(
( GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
; GoalExpr0 = generic_call(_, _, _, _, _)
; GoalExpr0 = unify(_, _, _, _, _)
),
Goal = Goal0
;
GoalExpr0 = plain_call(CalleePredId, CalleeProcId, OrigArgs, _, _, _),
(
map.search(TransformMap, proc(CalleePredId, CalleeProcId),
transformed_proc(_, hlds_goal(CallAux0, CallAuxInfo)))
->
module_info_get_type_table(ModuleInfo, TypeTable),
module_info_pred_proc_info(ModuleInfo, CalleePredId,
CalleeProcId, _CalleePredInfo, CalleeProcInfo),
proc_info_get_argmodes(CalleeProcInfo, OrigArgModes),
expand_call_args(OrigArgs, OrigArgModes, Args,
EnterUnifs, ExitUnifs, !VarSet, !VarTypes, TypeTable),
( CallAux = CallAux0 ^ call_args := Args ->
Call = hlds_goal(CallAux, CallAuxInfo),
ConjList = EnterUnifs ++ [Call] ++ ExitUnifs,
conj_list_to_goal(ConjList, GoalInfo0, Goal)
;
unexpected($module, $pred, "not a call template")
)
;
Goal = hlds_goal(GoalExpr0, GoalInfo0)
)
;
GoalExpr0 = negation(SubGoal0),
fix_calls_in_goal(SubGoal0, SubGoal, !VarSet, !VarTypes, TransformMap,
ModuleInfo),
GoalExpr = negation(SubGoal),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = scope(Reason, SubGoal0),
(
Reason = from_ground_term(_, FGT),
( FGT = from_ground_term_construct
; FGT = from_ground_term_deconstruct
)
->
% There are no calls in these scopes.
Goal = Goal0
;
fix_calls_in_goal(SubGoal0, SubGoal, !VarSet, !VarTypes,
TransformMap, ModuleInfo),
GoalExpr = scope(Reason, SubGoal),
Goal = hlds_goal(GoalExpr, GoalInfo0)
)
;
GoalExpr0 = conj(ConjType, Goals0),
(
ConjType = plain_conj,
fix_calls_in_conj(Goals0, Goals, !VarSet, !VarTypes, TransformMap,
ModuleInfo)
;
ConjType = parallel_conj,
% I am not sure whether parallel conjunctions should be treated
% with fix_calls_in_goal or fix_calls_in_goal_list. At any rate,
% this is untested.
fix_calls_in_goal_list(Goals0, Goals, !VarSet, !VarTypes,
TransformMap, ModuleInfo)
),
GoalExpr = conj(ConjType, Goals),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = disj(Goals0),
fix_calls_in_goal_list(Goals0, Goals, !VarSet, !VarTypes,
TransformMap, ModuleInfo),
GoalExpr = disj(Goals),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = switch(Var, CanFail, Cases0),
fix_calls_in_cases(Cases0, Cases, !VarSet, !VarTypes, TransformMap,
ModuleInfo),
GoalExpr = switch(Var, CanFail, Cases),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
fix_calls_in_goal(Cond0, Cond, !VarSet, !VarTypes, TransformMap,
ModuleInfo),
fix_calls_in_goal(Then0, Then, !VarSet, !VarTypes, TransformMap,
ModuleInfo),
fix_calls_in_goal(Else0, Else, !VarSet, !VarTypes, TransformMap,
ModuleInfo),
GoalExpr = if_then_else(Vars, Cond, Then, Else),
Goal = hlds_goal(GoalExpr, GoalInfo0)
;
GoalExpr0 = shorthand(_),
% These should have been expanded out by now.
unexpected($module, $pred, "shorthand")
).
%-----------------------------------------------------------------------------%
:- pred fix_calls_in_conj(hlds_goals::in, hlds_goals::out, prog_varset::in,
prog_varset::out, vartypes::in, vartypes::out, transform_map::in,
module_info::in) is det.
fix_calls_in_conj([], [], !VarSet, !VarTypes, _, _).
fix_calls_in_conj([Goal0 | Goals0], Goals, !VarSet, !VarTypes, TransformMap,
ModuleInfo) :-
fix_calls_in_goal(Goal0, Goal1, !VarSet, !VarTypes, TransformMap,
ModuleInfo),
fix_calls_in_conj(Goals0, Goals1, !VarSet, !VarTypes, TransformMap,
ModuleInfo),
( Goal1 = hlds_goal(conj(plain_conj, ConjGoals), _) ->
Goals = ConjGoals ++ Goals1
;
Goals = [Goal1 | Goals1]
).
:- pred fix_calls_in_goal_list(hlds_goals::in, hlds_goals::out,
prog_varset::in, prog_varset::out, vartypes::in, vartypes::out,
transform_map::in, module_info::in) is det.
fix_calls_in_goal_list([], [], !VarSet, !VarTypes, _, _).
fix_calls_in_goal_list([Goal0 | Goals0], [Goal | Goals], !VarSet, !VarTypes,
TransformMap, ModuleInfo) :-
fix_calls_in_goal(Goal0, Goal, !VarSet, !VarTypes,
TransformMap, ModuleInfo),
fix_calls_in_goal_list(Goals0, Goals, !VarSet, !VarTypes,
TransformMap, ModuleInfo).
:- pred fix_calls_in_cases(list(case)::in, list(case)::out, prog_varset::in,
prog_varset::out, vartypes::in, vartypes::out, transform_map::in,
module_info::in) is det.
fix_calls_in_cases([], [], !VarSet, !VarTypes, _, _).
fix_calls_in_cases([Case0 | Cases0], [Case | Cases], !VarSet, !VarTypes,
TransformMap, ModuleInfo) :-
Case0 = case(MainConsId, OtherConsIds, Goal0),
fix_calls_in_goal(Goal0, Goal, !VarSet, !VarTypes,
TransformMap, ModuleInfo),
Case = case(MainConsId, OtherConsIds, Goal),
fix_calls_in_cases(Cases0, Cases, !VarSet, !VarTypes,
TransformMap, ModuleInfo).
%-----------------------------------------------------------------------------%
:- pred expand_call_args(prog_vars::in, list(mer_mode)::in, prog_vars::out,
hlds_goals::out, hlds_goals::out, prog_varset::in, prog_varset::out,
vartypes::in, vartypes::out, type_table::in) is det.
expand_call_args(Args0, ArgModes0, Args, EnterUnifs, ExitUnifs,
!VarSet, !VarTypes, TypeTable) :-
expand_call_args_2(Args0, ArgModes0, Args, EnterUnifs, ExitUnifs,
!VarSet, !VarTypes, [], TypeTable).
:- pred expand_call_args_2(prog_vars::in, list(mer_mode)::in, prog_vars::out,
hlds_goals::out, hlds_goals::out, prog_varset::in, prog_varset::out,
vartypes::in, vartypes::out, list(mer_type)::in, type_table::in) is det.
expand_call_args_2([], [], [], [], [], !VarSet, !VarTypes, _, _).
expand_call_args_2([Arg0 | Args0], [ArgMode | ArgModes], Args,
EnterUnifs, ExitUnifs, !VarSet, !VarTypes,
ContainerTypes0, TypeTable) :-
lookup_var_type(!.VarTypes, Arg0, Arg0Type),
expand_argument(ArgMode, Arg0Type, ContainerTypes0, TypeTable, Expansion),
(
Expansion = expansion(ConsId, Types),
NumVars = list.length(Types),
varset.new_vars(NumVars, ReplacementArgs, !VarSet),
vartypes_add_corresponding_lists(ReplacementArgs, Types, !VarTypes),
list.duplicate(NumVars, ArgMode, ReplacementModes),
ContainerTypes = [Arg0Type | ContainerTypes0],
( ArgMode = in_mode ->
deconstruct_functor(Arg0, ConsId, ReplacementArgs, Unif),
EnterUnifs = [Unif | EnterUnifs1],
expand_call_args_2(ReplacementArgs ++ Args0,
ReplacementModes ++ ArgModes,
Args, EnterUnifs1, ExitUnifs, !VarSet,
!VarTypes, ContainerTypes, TypeTable)
; ArgMode = out_mode ->
construct_functor(Arg0, ConsId, ReplacementArgs, Unif),
ExitUnifs = ExitUnifs1 ++ [Unif],
expand_call_args_2(ReplacementArgs ++ Args0,
ReplacementModes ++ ArgModes,
Args, EnterUnifs, ExitUnifs1, !VarSet,
!VarTypes, ContainerTypes, TypeTable)
;
unexpected($module, $pred, "unsupported mode")
)
;
Expansion = no_expansion,
Args = [Arg0 | Args1],
expand_call_args(Args0, ArgModes, Args1, EnterUnifs,
ExitUnifs, !VarSet, !VarTypes, TypeTable)
).
expand_call_args_2([], [_|_], _, _, _, !_, !_, _, _) :-
unexpected($module, $pred, "length mismatch").
expand_call_args_2([_|_], [], _, _, _, !_, !_, _, _) :-
unexpected($module, $pred, "length mismatch").
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- type expansion_result
---> expansion(
cons_id,
% the cons_id of the expanded constructor
list(mer_type)
% the types of the arguments for the
% expanded constructor
)
; no_expansion.
% This predicate tries to expand the argument of the given mode and
% type. If this is possible then Expansion is unified with the
% `expansion' functor, giving the details of the expansion.
% Otherwise it is unified with `no_expansion'.
%
:- pred expand_argument(mer_mode::in, mer_type::in, list(mer_type)::in,
type_table::in, expansion_result::out) is det.
expand_argument(ArgMode, ArgType, ContainerTypes, TypeTable, Expansion) :-
( expandable_arg_mode(ArgMode) ->
expand_type(ArgType, ContainerTypes, TypeTable, Expansion)
;
Expansion = no_expansion
).
% This module so far only knows how to expand arguments which have
% the following modes.
%
:- pred expandable_arg_mode(mer_mode::in) is semidet.
expandable_arg_mode(in_mode).
expandable_arg_mode(out_mode).
:- pred expand_type(mer_type::in, list(mer_type)::in, type_table::in,
expansion_result::out) is det.
expand_type(Type, ContainerTypes, TypeTable, Expansion) :-
(
% Always expand tuple types.
type_to_ctor_and_args(Type, TypeCtor, TypeArgs),
type_ctor_is_tuple(TypeCtor)
->
Arity = list.length(TypeArgs),
ConsId = tuple_cons(Arity),
Expansion = expansion(ConsId, TypeArgs)
;
% Expand a discriminated union type if it has only a
% single functor and the type has no parameters.
type_to_ctor_and_args(Type, TypeCtor, []),
search_type_ctor_defn(TypeTable, TypeCtor, TypeDefn),
get_type_defn_tparams(TypeDefn, []),
get_type_defn_body(TypeDefn, TypeBody),
TypeBody ^ du_type_ctors = [SingleCtor],
SingleCtor ^ cons_exist = [],
SingleCtorName = SingleCtor ^ cons_name,
SingleCtorArgs = SingleCtor ^ cons_args,
SingleCtorArgs = [_ | _],
% Prevent infinite loop with recursive types.
\+ list.member(Type, ContainerTypes)
->
Arity = list.length(SingleCtorArgs),
ConsId = cons(SingleCtorName, Arity, TypeCtor),
ExpandedTypes = list.map(func(C) = C ^ arg_type, SingleCtorArgs),
Expansion = expansion(ConsId, ExpandedTypes)
;
Expansion = no_expansion
).
%-----------------------------------------------------------------------------%
:- end_module transform_hlds.untupling.
%-----------------------------------------------------------------------------%