mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
compiler/prog_type_subst.m:
compiler/type_util.m:
Apply s/apply_variable_renaming_to_/apply_renaming_to_/ and
s/_to_x_list/_to_xs/ to the names of predicate.
Conform to the change in hlds_class.m below.
compiler/hlds_class.m:
This module used to define types named (a) hlds_constraint, and
(b) hlds_constraints, and the latter was NOT a list of items
of type hlds_constraint. Rename the latter to hlds_constraint_db
to free up the name apply_renaming_to_constraints to apply
to list(hlds_constraint). However, the rename also makes code
operating on hlds_constraint_dbs easier to understand. Before
this diff, several modules used variables named Constraints
to refer to a list(hlds_constraint) in some places and to
what is now a hlds_constraint_db in other places, which is confusing;
the latter are now named ConstraintDb.
compiler/type_assign.m:
Conform to the changes above.
Add an XXX about some existing variable names that *look* right
but turn out to be subtly misleading.
compiler/add_pragma_type_spec.m:
compiler/add_type.m:
compiler/check_typeclass.m:
compiler/comp_unit_interface.m:
compiler/cse_detection.m:
compiler/ctgc.util.m:
compiler/decide_type_repn.m:
compiler/deforest.m:
compiler/equiv_type.m:
compiler/equiv_type_hlds.m:
compiler/higher_order.higher_order_global_info.m:
compiler/higher_order.make_specialized_preds.m:
compiler/higher_order.specialize_calls.m:
compiler/hlds_rtti.m:
compiler/inlining.m:
compiler/modecheck_coerce.m:
compiler/old_type_constraints.m:
compiler/polymorphism_clause.m:
compiler/polymorphism_goal.m:
compiler/polymorphism_type_class_info.m:
compiler/prog_type_unify.m:
compiler/qual_info.m:
compiler/recompilation.version.m:
compiler/resolve_unify_functor.m:
compiler/typecheck.m:
compiler/typecheck_clauses.m:
compiler/typecheck_cons_infos.m:
compiler/typecheck_debug.m:
compiler/typecheck_error_type_assign.m:
compiler/typecheck_errors.m:
compiler/typecheck_unify_var_functor.m:
compiler/typecheck_util.m:
compiler/typeclasses.m:
compiler/unify_proc.m:
compiler/var_table.m:
compiler/vartypes.m:
Conform to the changes above.
1309 lines
59 KiB
Mathematica
1309 lines
59 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1995-2012 The University of Melbourne.
|
|
% Copyright (C) 2014-2015, 2022-2025 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
% File: polymorphism_clause.m.
|
|
% Main authors: fjh and zs.
|
|
%
|
|
% This module handles the part of the polymorphism transformation
|
|
% that involves transforming the bodies of clauses. That transformation
|
|
% is described by the comment at the top of polymorphism.m.
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds.polymorphism_goal.
|
|
:- interface.
|
|
|
|
:- import_module check_hlds.polymorphism_info.
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_rtti.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- pred polymorphism_process_goal(hlds_goal::in, hlds_goal::out,
|
|
poly_info::in, poly_info::out) is det.
|
|
|
|
% Add the type_info variables for a complicated unification to
|
|
% the appropriate fields in the unification and the goal_info.
|
|
%
|
|
% Exported for modecheck_unify.m.
|
|
%
|
|
:- pred unification_typeinfos_rtti_varmaps(mer_type::in, rtti_varmaps::in,
|
|
unification::in, unification::out, hlds_goal_info::in, hlds_goal_info::out)
|
|
is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.polymorphism_lambda.
|
|
:- import_module check_hlds.polymorphism_type_class_info.
|
|
:- import_module check_hlds.polymorphism_type_info.
|
|
:- import_module hlds.const_struct.
|
|
:- import_module hlds.from_ground_term_util.
|
|
:- import_module hlds.goal_util.
|
|
:- import_module hlds.goal_vars.
|
|
:- import_module hlds.hlds_class.
|
|
:- import_module hlds.hlds_markers.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.quantification.
|
|
:- import_module hlds.type_util.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.optimization_options.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.goal_path.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.program_representation.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.builtin_lib_types.
|
|
:- import_module parse_tree.maybe_error.
|
|
:- import_module parse_tree.prog_data_foreign.
|
|
:- import_module parse_tree.prog_mode.
|
|
:- import_module parse_tree.prog_type.
|
|
:- import_module parse_tree.prog_type_scan.
|
|
:- import_module parse_tree.prog_type_subst.
|
|
:- import_module parse_tree.prog_type_test.
|
|
:- import_module parse_tree.prog_type_unify.
|
|
:- import_module parse_tree.set_of_var.
|
|
:- import_module parse_tree.var_table.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term_context.
|
|
:- import_module varset.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
polymorphism_process_goal(Goal0, Goal, !Info) :-
|
|
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
|
|
(
|
|
GoalExpr0 = unify(LHSVar, RHS, Mode, Unification, UnifyContext),
|
|
polymorphism_process_unify(LHSVar, RHS, Mode, Unification,
|
|
UnifyContext, GoalInfo0, Goal, !Info)
|
|
;
|
|
GoalExpr0 = plain_call(PredId, _, ArgVars0, _, _, _),
|
|
polymorphism_process_call(PredId, ArgVars0, GoalInfo0, GoalInfo,
|
|
ExtraVars, ExtraGoals, !Info),
|
|
ArgVars = ExtraVars ++ ArgVars0,
|
|
CallExpr = GoalExpr0 ^ call_args := ArgVars,
|
|
Call = hlds_goal(CallExpr, GoalInfo),
|
|
GoalList = ExtraGoals ++ [Call],
|
|
conj_list_to_goal(GoalList, GoalInfo0, Goal)
|
|
;
|
|
GoalExpr0 = call_foreign_proc(_, PredId, _, _, _, _, _),
|
|
poly_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
PredModule = pred_info_module(PredInfo),
|
|
PredName = pred_info_name(PredInfo),
|
|
pred_info_get_orig_arity(PredInfo, pred_form_arity(PredFormArityInt)),
|
|
( if no_type_info_builtin(PredModule, PredName, PredFormArityInt) then
|
|
Goal = Goal0
|
|
else
|
|
polymorphism_process_foreign_proc(PredInfo, GoalExpr0, GoalInfo0,
|
|
Goal, !Info)
|
|
)
|
|
;
|
|
% We don't need to add type_infos for higher order calls, since the
|
|
% type_infos are added when the closures are constructed, not when
|
|
% they are called.
|
|
GoalExpr0 = generic_call(_, _, _, _, _),
|
|
Goal = Goal0
|
|
;
|
|
% The rest of the cases just process goals recursively.
|
|
(
|
|
GoalExpr0 = conj(ConjType, Goals0),
|
|
(
|
|
ConjType = plain_conj,
|
|
polymorphism_process_plain_conj(Goals0, Goals, !Info)
|
|
;
|
|
ConjType = parallel_conj,
|
|
get_cache_maps_snapshot("parconj", InitialSnapshot, !Info),
|
|
polymorphism_process_par_conj(Goals0, Goals, InitialSnapshot,
|
|
!Info)
|
|
% Unlike with disjunctions, we do not have to reset to
|
|
% InitialSnapshot.
|
|
%
|
|
% The reason why we take InitialSnapshot is to prevent
|
|
% each conjunct from depending on e.g. type_info vars
|
|
% generated by earlier conjuncts. However, the code
|
|
% following the parallel conjunction as a whole
|
|
% *may* rely on the changes to the varmaps made by the
|
|
% last conjunct. (In fact, it could rely on the changes
|
|
% made by any conjunct, but we throw away the changes
|
|
% made by all conjuncts except the last.
|
|
),
|
|
GoalExpr = conj(ConjType, Goals)
|
|
;
|
|
GoalExpr0 = disj(Goals0),
|
|
get_cache_maps_snapshot("disj", InitialSnapshot, !Info),
|
|
polymorphism_process_disj(Goals0, Goals, InitialSnapshot, !Info),
|
|
set_cache_maps_snapshot("after disj", InitialSnapshot, !Info),
|
|
GoalExpr = disj(Goals)
|
|
;
|
|
GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
|
|
get_cache_maps_snapshot("ite", InitialSnapshot, !Info),
|
|
polymorphism_process_goal(Cond0, Cond, !Info),
|
|
% If we allowed a type_info created inside Cond to be reused
|
|
% in Then, then we are adding an output variable to Cond.
|
|
% If Cond scope had no outputs to begin with, this would change
|
|
% its determinism.
|
|
set_cache_maps_snapshot("before then", InitialSnapshot, !Info),
|
|
polymorphism_process_goal(Then0, Then, !Info),
|
|
set_cache_maps_snapshot("before else", InitialSnapshot, !Info),
|
|
polymorphism_process_goal(Else0, Else, !Info),
|
|
set_cache_maps_snapshot("after ite", InitialSnapshot, !Info),
|
|
GoalExpr = if_then_else(Vars, Cond, Then, Else)
|
|
;
|
|
GoalExpr0 = negation(SubGoal0),
|
|
get_cache_maps_snapshot("neg", InitialSnapshot, !Info),
|
|
polymorphism_process_goal(SubGoal0, SubGoal, !Info),
|
|
set_cache_maps_snapshot("after neg", InitialSnapshot, !Info),
|
|
GoalExpr = negation(SubGoal)
|
|
;
|
|
GoalExpr0 = switch(Var, CanFail, Cases0),
|
|
get_cache_maps_snapshot("switch", InitialSnapshot, !Info),
|
|
polymorphism_process_cases(Cases0, Cases, InitialSnapshot, !Info),
|
|
set_cache_maps_snapshot("after switch", InitialSnapshot, !Info),
|
|
GoalExpr = switch(Var, CanFail, Cases)
|
|
;
|
|
GoalExpr0 = scope(Reason0, SubGoal0),
|
|
(
|
|
Reason0 = from_ground_term(TermVar, Kind),
|
|
(
|
|
Kind = from_ground_term_initial,
|
|
polymorphism_process_from_ground_term_initial(TermVar,
|
|
GoalInfo0, SubGoal0, GoalExpr, !Info)
|
|
;
|
|
( Kind = from_ground_term_construct
|
|
; Kind = from_ground_term_deconstruct
|
|
; Kind = from_ground_term_other
|
|
),
|
|
polymorphism_process_goal(SubGoal0, SubGoal, !Info),
|
|
GoalExpr = scope(Reason0, SubGoal)
|
|
)
|
|
;
|
|
Reason0 = promise_solutions(_, _),
|
|
% polymorphism_process_goal may cause SubGoal to bind
|
|
% variables (such as PolyConst variables) that SubGoal0 does
|
|
% not bind. If we allowed such variables to be reused outside
|
|
% the scope, that would change the set of variables that the
|
|
% promise would have to cover. We cannot expect and do not want
|
|
% user level programmers making promises about variables added
|
|
% by the compiler.
|
|
get_cache_maps_snapshot("promise_solns", InitialSnapshot,
|
|
!Info),
|
|
polymorphism_process_goal(SubGoal0, SubGoal, !Info),
|
|
set_cache_maps_snapshot("after promise_solns", InitialSnapshot,
|
|
!Info),
|
|
GoalExpr = scope(Reason0, SubGoal)
|
|
;
|
|
( Reason0 = disable_warnings(_, _)
|
|
; Reason0 = promise_purity(_)
|
|
; Reason0 = require_detism(_)
|
|
; Reason0 = require_complete_switch(_)
|
|
; Reason0 = require_switch_arms_detism(_, _)
|
|
; Reason0 = commit(_)
|
|
; Reason0 = barrier(_)
|
|
; Reason0 = loop_control(_, _, _)
|
|
),
|
|
polymorphism_process_goal(SubGoal0, SubGoal, !Info),
|
|
GoalExpr = scope(Reason0, SubGoal)
|
|
;
|
|
Reason0 = exist_quant(_, _),
|
|
% If we allowed a type_info created inside SubGoal to be reused
|
|
% outside GoalExpr, then we are adding an output variable to
|
|
% the scope. If the scope had no outputs to begin with, this
|
|
% would change the determinism of the scope.
|
|
%
|
|
% However, using a type_info from before the scope in SubGoal
|
|
% is perfectly ok.
|
|
get_cache_maps_snapshot("exists", InitialSnapshot, !Info),
|
|
polymorphism_process_goal(SubGoal0, SubGoal, !Info),
|
|
set_cache_maps_snapshot("after exists", InitialSnapshot,
|
|
!Info),
|
|
GoalExpr = scope(Reason0, SubGoal)
|
|
;
|
|
Reason0 = trace_goal(_, _, _, _, _),
|
|
% Trace goal scopes will be deleted after semantic analysis
|
|
% if their compile-time condition turns out to be false.
|
|
% If we let later code use type_infos introduced inside the
|
|
% scope, the deletion of the scope would leave those variables
|
|
% undefined.
|
|
%
|
|
% We *could* evaluate the compile-time condition here to know
|
|
% whether the deletion will happen or not, but doing so would
|
|
% require breaching the separation between compiler passes.
|
|
get_cache_maps_snapshot("trace", InitialSnapshot, !Info),
|
|
polymorphism_process_goal(SubGoal0, SubGoal, !Info),
|
|
set_cache_maps_snapshot("after trace", InitialSnapshot, !Info),
|
|
GoalExpr = scope(Reason0, SubGoal)
|
|
)
|
|
),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
;
|
|
GoalExpr0 = shorthand(ShortHand0),
|
|
(
|
|
ShortHand0 = atomic_goal(GoalType, Outer, Inner, Vars,
|
|
MainGoal0, OrElseGoals0, OrElseInners),
|
|
get_cache_maps_snapshot("atomic", InitialSnapshot, !Info),
|
|
polymorphism_process_goal(MainGoal0, MainGoal, !Info),
|
|
polymorphism_process_disj(OrElseGoals0, OrElseGoals,
|
|
InitialSnapshot, !Info),
|
|
set_cache_maps_snapshot("after atomic", InitialSnapshot, !Info),
|
|
ShortHand = atomic_goal(GoalType, Outer, Inner, Vars,
|
|
MainGoal, OrElseGoals, OrElseInners)
|
|
;
|
|
ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
|
|
% We don't let the code inside and outside the try goal share
|
|
% type_info variables for the same reason as with lambda
|
|
% expressions; because those pieces of code will end up
|
|
% in different procedures. However, for try goals, this is true
|
|
% even for the first and second conjuncts.
|
|
get_cache_maps_snapshot("try", InitialSnapshot, !Info),
|
|
( if
|
|
SubGoal0 = hlds_goal(SubGoalExpr0, SubGoalInfo),
|
|
SubGoalExpr0 = conj(plain_conj, Conjuncts0),
|
|
Conjuncts0 = [ConjunctA0, ConjunctB0]
|
|
then
|
|
empty_cache_maps(!Info),
|
|
polymorphism_process_goal(ConjunctA0, ConjunctA, !Info),
|
|
empty_cache_maps(!Info),
|
|
polymorphism_process_goal(ConjunctB0, ConjunctB, !Info),
|
|
|
|
Conjuncts = [ConjunctA, ConjunctB],
|
|
SubGoalExpr = conj(plain_conj, Conjuncts),
|
|
SubGoal = hlds_goal(SubGoalExpr, SubGoalInfo)
|
|
else
|
|
unexpected($pred, "malformed try goal")
|
|
),
|
|
set_cache_maps_snapshot("after try", InitialSnapshot, !Info),
|
|
ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
|
|
;
|
|
ShortHand0 = bi_implication(_, _),
|
|
unexpected($pred, "bi_implication")
|
|
),
|
|
GoalExpr = shorthand(ShortHand),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo0)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Process unifications.
|
|
%
|
|
|
|
:- pred polymorphism_process_unify(prog_var::in, unify_rhs::in,
|
|
unify_mode::in, unification::in, unify_context::in, hlds_goal_info::in,
|
|
hlds_goal::out, poly_info::in, poly_info::out) is det.
|
|
|
|
polymorphism_process_unify(LHSVar, RHS0, Mode, Unification0, UnifyContext,
|
|
GoalInfo0, Goal, !Info) :-
|
|
(
|
|
RHS0 = rhs_var(_RHSVar),
|
|
% Var-var unifications (simple_test, assign, or complicated_unify)
|
|
% are basically left unchanged. Complicated unifications will
|
|
% eventually get converted into calls, but that is done later on,
|
|
% by simplify.m, not now. At this point we just need to figure out
|
|
% which type_info/typeclass_info variables the unification might need,
|
|
% and insert them in the nonlocals. We have to do that for all var-var
|
|
% unifications, because at this point we haven't done mode analysis so
|
|
% we don't know which ones will become complicated_unifies.
|
|
% Note that we also store the type_info/typeclass_info variables
|
|
% in a field in the unification, which quantification.m uses when
|
|
% requantifying things.
|
|
poly_info_get_var_table(!.Info, VarTable),
|
|
lookup_var_type(VarTable, LHSVar, Type),
|
|
unification_typeinfos(Type, Unification0, Unification,
|
|
GoalInfo0, GoalInfo, _Changed, !Info),
|
|
Goal = hlds_goal(unify(LHSVar, RHS0, Mode, Unification, UnifyContext),
|
|
GoalInfo)
|
|
;
|
|
RHS0 = rhs_functor(ConsId, _, Args),
|
|
polymorphism_process_unify_functor(LHSVar, ConsId, Args, Mode,
|
|
Unification0, UnifyContext, GoalInfo0, Goal, _Changed, !Info)
|
|
;
|
|
RHS0 = rhs_lambda_goal(Purity, Groundness, PredOrFunc,
|
|
LambdaNonLocals0, ArgVarsModes, Det, LambdaGoal0),
|
|
% For lambda expressions, we must recursively traverse the lambda goal.
|
|
% Any type_info variables needed by the lambda goal are created in the
|
|
% lambda goal (not imported from the outside), and any type_info
|
|
% variables created by the lambda goal are not available outside.
|
|
% This is because, after lambda expansion, the code inside and outside
|
|
% the lambda goal will end up in different procedures.
|
|
get_cache_maps_snapshot("lambda", InitialSnapshot, !Info),
|
|
empty_cache_maps(!Info),
|
|
polymorphism_process_goal(LambdaGoal0, LambdaGoal1, !Info),
|
|
set_cache_maps_snapshot("after lambda", InitialSnapshot, !Info),
|
|
|
|
assoc_list.keys(ArgVarsModes, ArgVars),
|
|
% Currently we don't allow lambda goals to be existentially typed.
|
|
ExistQVars = [],
|
|
requantify_lambda_goal(LambdaNonLocals0, ArgVars,
|
|
ExistQVars, LambdaGoal1, LambdaGoal,
|
|
LambdaTiTciVars, PossibleNonLocalTiTciVars, !Info),
|
|
LambdaNonLocals1 =
|
|
set_of_var.to_sorted_list(LambdaTiTciVars) ++
|
|
set_of_var.to_sorted_list(PossibleNonLocalTiTciVars) ++
|
|
LambdaNonLocals0,
|
|
list.sort_and_remove_dups(LambdaNonLocals1, LambdaNonLocals),
|
|
RHS = rhs_lambda_goal(Purity, Groundness, PredOrFunc,
|
|
LambdaNonLocals, ArgVarsModes, Det, LambdaGoal),
|
|
NonLocals0 = goal_info_get_nonlocals(GoalInfo0),
|
|
set_of_var.union(PossibleNonLocalTiTciVars, NonLocals0, NonLocals),
|
|
goal_info_set_nonlocals(NonLocals, GoalInfo0, GoalInfo),
|
|
|
|
% Complicated (in-in) argument unifications are impossible for lambda
|
|
% expressions, so we don't need to worry about adding the type_infos
|
|
% that would be required for such unifications.
|
|
GoalExpr = unify(LHSVar, RHS, Mode, Unification0, UnifyContext),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred unification_typeinfos(mer_type::in,
|
|
unification::in, unification::out, hlds_goal_info::in, hlds_goal_info::out,
|
|
bool::out, poly_info::in, poly_info::out) is det.
|
|
|
|
unification_typeinfos(Type, !Unification, !GoalInfo, Changed, !Info) :-
|
|
% Compute the type_info/type_class_info variables that would be used
|
|
% if this unification ends up being a complicated_unify.
|
|
type_vars_in_type(Type, TypeVars),
|
|
(
|
|
TypeVars = [],
|
|
Changed = no
|
|
;
|
|
TypeVars = [_ | _],
|
|
list.map_foldl(poly_get_type_info_locn, TypeVars, TypeInfoLocns,
|
|
!Info),
|
|
add_unification_typeinfos(TypeInfoLocns, !Unification, !GoalInfo),
|
|
Changed = yes
|
|
).
|
|
|
|
unification_typeinfos_rtti_varmaps(Type, RttiVarMaps,
|
|
!Unification, !GoalInfo) :-
|
|
% This variant is for use by modecheck_unify.m. During mode checking,
|
|
% all the type_infos should appear in the type_info_varmap.
|
|
|
|
% Compute the type_info/type_class_info variables that would be used
|
|
% if this unification ends up being a complicated_unify.
|
|
type_vars_in_type(Type, TypeVars),
|
|
list.map(rtti_lookup_type_info_locn(RttiVarMaps), TypeVars, TypeInfoLocns),
|
|
add_unification_typeinfos(TypeInfoLocns, !Unification, !GoalInfo).
|
|
|
|
:- pred add_unification_typeinfos(list(type_info_locn)::in,
|
|
unification::in, unification::out,
|
|
hlds_goal_info::in, hlds_goal_info::out) is det.
|
|
|
|
add_unification_typeinfos(TypeInfoLocns, !Unification, !GoalInfo) :-
|
|
list.map(type_info_locn_var, TypeInfoLocns, TypeInfoVars0),
|
|
list.remove_dups(TypeInfoVars0, TypeInfoVars),
|
|
|
|
% Insert the TypeInfoVars into the nonlocals field of the goal_info
|
|
% for the unification goal.
|
|
NonLocals0 = goal_info_get_nonlocals(!.GoalInfo),
|
|
set_of_var.insert_list(TypeInfoVars, NonLocals0, NonLocals),
|
|
goal_info_set_nonlocals(NonLocals, !GoalInfo),
|
|
|
|
% Also save those type_info vars into a field in the complicated_unify,
|
|
% so that quantification.m can recompute variable scopes properly.
|
|
% This field is also used by modecheck_unify.m -- for complicated
|
|
% unifications, it checks that all these variables are ground.
|
|
(
|
|
!.Unification = complicated_unify(Modes, CanFail, _),
|
|
!:Unification = complicated_unify(Modes, CanFail, TypeInfoVars)
|
|
;
|
|
% This can happen if an earlier stage of compilation has already
|
|
% determined that this unification is particular kind of unification.
|
|
% In that case, the type_info vars won't be needed.
|
|
( !.Unification = construct(_, _, _, _, _, _, _)
|
|
; !.Unification = deconstruct(_, _, _, _, _, _)
|
|
; !.Unification = assign(_, _)
|
|
; !.Unification = simple_test(_, _)
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred polymorphism_process_unify_functor(prog_var::in, cons_id::in,
|
|
list(prog_var)::in, unify_mode::in, unification::in, unify_context::in,
|
|
hlds_goal_info::in, hlds_goal::out, bool::out,
|
|
poly_info::in, poly_info::out) is det.
|
|
|
|
polymorphism_process_unify_functor(X0, ConsId0, ArgVars0, Mode0, Unification0,
|
|
UnifyContext, GoalInfo0, Goal, Changed, !Info) :-
|
|
poly_info_get_module_info(!.Info, ModuleInfo0),
|
|
poly_info_get_var_table(!.Info, VarTable0),
|
|
lookup_var_type(VarTable0, X0, TypeOfX),
|
|
list.length(ArgVars0, Arity),
|
|
|
|
% We replace any unifications with higher order pred constants
|
|
% by lambda expressions. For example, we replace
|
|
%
|
|
% X = list.append(Y) % Y::in, X::out
|
|
%
|
|
% with
|
|
%
|
|
% X = (
|
|
% pred(A1::in, A2::out) is ... :- list.append(Y, A1, A2)
|
|
% )
|
|
%
|
|
% We do this because it makes two things easier. First, mode analysis
|
|
% needs to check that the lambda goal doesn't bind any nonlocal variables
|
|
% (e.g. `Y' in above example). This would require a bit of moderately
|
|
% tricky special case code if we didn't expand them here. Second, this pass
|
|
% (polymorphism.m) is a lot easier if we don't have to handle higher order
|
|
% constants. If it turns out that the predicate was nonpolymorphic,
|
|
% lambda.m will turn the lambda expression back into a higher order
|
|
% constant again.
|
|
%
|
|
% Note that this transformation is also done by modecheck_unify.m, in case
|
|
% we are rerunning mode analysis after lambda.m has already been run;
|
|
% any changes to the code here will also need to be duplicated there.
|
|
|
|
( if
|
|
% Check if variable has a higher order type.
|
|
ConsId0 = closure_cons(ShroudedPredProcId),
|
|
proc(PredId, ProcId0) = unshroud_pred_proc_id(ShroudedPredProcId),
|
|
type_is_higher_order_details(TypeOfX, Purity, _PredOrFunc,
|
|
CalleeArgTypes)
|
|
then
|
|
% An `invalid_proc_id' means the predicate is multi-moded. We can't
|
|
% pick the right mode yet. Perform the rest of the transformation with
|
|
% any mode (the first) but mark the goal with a feature so that mode
|
|
% checking knows to fix it up later.
|
|
( if ProcId0 = invalid_proc_id then
|
|
module_info_pred_info(ModuleInfo0, PredId, PredInfo),
|
|
ProcIds = pred_info_all_procids(PredInfo),
|
|
(
|
|
ProcIds = [ProcId | _],
|
|
goal_info_add_feature(feature_lambda_undetermined_mode,
|
|
GoalInfo0, GoalInfo1)
|
|
;
|
|
ProcIds = [],
|
|
unexpected($pred, "no modes")
|
|
)
|
|
else
|
|
ProcId = ProcId0,
|
|
GoalInfo1 = GoalInfo0
|
|
),
|
|
% Convert the higher order pred term to a lambda goal.
|
|
Context = goal_info_get_context(GoalInfo0),
|
|
convert_pred_to_lambda_goal(ModuleInfo0, Purity, X0, PredId, ProcId,
|
|
ArgVars0, CalleeArgTypes, UnifyContext,
|
|
GoalInfo1, Context, MaybeRHS0, VarTable0, VarTable),
|
|
poly_info_set_var_table(VarTable, !Info),
|
|
(
|
|
MaybeRHS0 = ok1(RHS0),
|
|
% Process the unification in its new form.
|
|
polymorphism_process_unify(X0, RHS0, Mode0, Unification0,
|
|
UnifyContext, GoalInfo1, Goal, !Info)
|
|
;
|
|
MaybeRHS0 = error1(Specs),
|
|
poly_info_get_errors(!.Info, Specs0),
|
|
poly_info_set_errors(Specs ++ Specs0, !Info),
|
|
% It doesn't matter what Goal we return, since it won't be used.
|
|
RHS = rhs_functor(some_int_const(int_const(42)),
|
|
is_not_exist_constr, []),
|
|
polymorphism_process_unify(X0, RHS, Mode0, Unification0,
|
|
UnifyContext, GoalInfo1, Goal, !Info)
|
|
),
|
|
Changed = yes
|
|
else if
|
|
% Is this a construction or deconstruction of an existentially
|
|
% typed data type?
|
|
%
|
|
% Check whether the functor had a "new " prefix.
|
|
% If so, assume it is a construction, and strip off the prefix.
|
|
% Otherwise, assume it is a deconstruction.
|
|
ConsId0 = du_data_ctor(DuCtor0),
|
|
DuCtor0 = du_ctor(Functor0, Arity, ConsTypeCtor),
|
|
( if remove_new_prefix(Functor0, OrigFunctor) then
|
|
DuCtor = du_ctor(OrigFunctor, Arity, ConsTypeCtor),
|
|
ConsId = du_data_ctor(DuCtor),
|
|
IsExistConstr = is_exist_constr
|
|
else
|
|
ConsId = ConsId0,
|
|
DuCtor = DuCtor0,
|
|
IsExistConstr = is_not_exist_constr
|
|
),
|
|
|
|
% Check whether the functor (with the "new " prefix removed)
|
|
% is an existentially typed functor.
|
|
type_util.get_existq_cons_defn(ModuleInfo0, TypeOfX,
|
|
DuCtor, ConsDefn)
|
|
then
|
|
% Add extra arguments to the unification for the
|
|
% type_info and/or type_class_info variables.
|
|
lookup_var_types(VarTable0, ArgVars0, ActualArgTypes),
|
|
polymorphism_process_existq_unify_functor(ConsDefn,
|
|
IsExistConstr, ActualArgTypes, TypeOfX, GoalInfo0,
|
|
ExtraVars, ExtraGoals, !Info),
|
|
ArgVars = ExtraVars ++ ArgVars0,
|
|
NonLocals0 = goal_info_get_nonlocals(GoalInfo0),
|
|
set_of_var.insert_list(ExtraVars, NonLocals0, NonLocals),
|
|
goal_info_set_nonlocals(NonLocals, GoalInfo0, GoalInfo1),
|
|
|
|
% Some of the argument unifications may be complicated unifications,
|
|
% which may need type_infos.
|
|
unification_typeinfos(TypeOfX, Unification0, Unification,
|
|
GoalInfo1, GoalInfo, _Changed, !Info),
|
|
|
|
UnifyExpr = unify(X0, rhs_functor(ConsId, IsExistConstr, ArgVars),
|
|
Mode0, Unification, UnifyContext),
|
|
Unify = hlds_goal(UnifyExpr, GoalInfo),
|
|
GoalList = ExtraGoals ++ [Unify],
|
|
conj_list_to_goal(GoalList, GoalInfo0, Goal),
|
|
Changed = yes
|
|
else
|
|
% We leave construction/deconstruction unifications alone.
|
|
% Some of the argument unifications may be complicated unifications,
|
|
% which may need type_infos.
|
|
% XXX Return original Goal0 if Changed = no.
|
|
unification_typeinfos(TypeOfX, Unification0, Unification,
|
|
GoalInfo0, GoalInfo, Changed, !Info),
|
|
RHS = rhs_functor(ConsId0, is_not_exist_constr, ArgVars0),
|
|
GoalExpr = unify(X0, RHS, Mode0, Unification, UnifyContext),
|
|
Goal = hlds_goal(GoalExpr, GoalInfo)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% Compute the extra arguments that we need to add to a unification with
|
|
% an existentially quantified data constructor.
|
|
%
|
|
:- pred polymorphism_process_existq_unify_functor(ctor_defn::in,
|
|
is_exist_constr::in, list(mer_type)::in, mer_type::in,
|
|
hlds_goal_info::in, list(prog_var)::out, list(hlds_goal)::out,
|
|
poly_info::in, poly_info::out) is det.
|
|
|
|
polymorphism_process_existq_unify_functor(CtorDefn, IsExistConstr,
|
|
ActualArgTypes, ActualRetType, GoalInfo,
|
|
ExtraVars, ExtraGoals, !Info) :-
|
|
CtorDefn = ctor_defn(CtorTypeVarSet, CtorKindMap,
|
|
CtorMaybeExistConstraints, CtorArgTypes, CtorRetType),
|
|
|
|
% Rename apart the type variables in the constructor definition.
|
|
poly_info_get_typevarset(!.Info, TypeVarSet0),
|
|
tvarset_merge_renaming(TypeVarSet0, CtorTypeVarSet, TypeVarSet,
|
|
CtorToParentRenaming),
|
|
(
|
|
CtorMaybeExistConstraints = exist_constraints(CtorExistConstraints),
|
|
% XXX Could we use _Ctor{Unc,C}onstrainedExistQVars to avoid
|
|
% some of the work below?
|
|
CtorExistConstraints = cons_exist_constraints(CtorExistQVars,
|
|
CtorExistentialConstraints,
|
|
_CtorUnconstrainedExistQVars, _CtorConstrainedExistQVars),
|
|
apply_renaming_to_tvars(CtorToParentRenaming,
|
|
CtorExistQVars, ParentExistQVars),
|
|
apply_renaming_to_prog_constraints(CtorToParentRenaming,
|
|
CtorExistentialConstraints, ParentExistentialConstraints),
|
|
list.length(ParentExistentialConstraints, NumExistentialConstraints),
|
|
|
|
% Compute the set of _unconstrained_ existentially quantified type
|
|
% variables, and then apply the type bindings to those type variables
|
|
% to figure out what types they are bound to.
|
|
constraint_list_get_tvars(ParentExistentialConstraints,
|
|
ParentExistConstrainedTVars),
|
|
list.delete_elems(ParentExistQVars, ParentExistConstrainedTVars,
|
|
ParentUnconstrainedExistQVars),
|
|
apply_rec_subst_to_tvars(ParentKindMap, ParentToActualTypeSubst,
|
|
ParentUnconstrainedExistQVars, ActualExistentialTypes)
|
|
;
|
|
CtorMaybeExistConstraints = no_exist_constraints,
|
|
NumExistentialConstraints = 0,
|
|
ActualExistentialTypes = []
|
|
),
|
|
apply_renaming_to_tvar_kind_map(CtorToParentRenaming,
|
|
CtorKindMap, ParentKindMap),
|
|
apply_renaming_to_types(CtorToParentRenaming,
|
|
CtorArgTypes, ParentArgTypes),
|
|
apply_renaming_to_type(CtorToParentRenaming, CtorRetType, ParentRetType),
|
|
poly_info_set_typevarset(TypeVarSet, !Info),
|
|
|
|
% Compute the type bindings resulting from the functor's actual argument
|
|
% and return types. These are the ones that might bind the ExistQVars.
|
|
type_list_subsumes_det([ParentRetType | ParentArgTypes],
|
|
[ActualRetType | ActualArgTypes], ParentToActualTypeSubst),
|
|
|
|
% Create type_class_info variables for the type class constraints.
|
|
poly_info_get_constraint_map(!.Info, ConstraintMap),
|
|
GoalId = goal_info_get_goal_id(GoalInfo),
|
|
Context = goal_info_get_context(GoalInfo),
|
|
(
|
|
IsExistConstr = is_exist_constr,
|
|
% Assume it is a construction.
|
|
lookup_hlds_constraint_list(ConstraintMap, unproven, GoalId,
|
|
NumExistentialConstraints, ActualExistentialConstraints),
|
|
make_typeclass_info_vars(ActualExistentialConstraints, [], Context,
|
|
ExtraTypeClassVarsMCAs, ExtraTypeClassGoals, !Info),
|
|
assoc_list.keys(ExtraTypeClassVarsMCAs, ExtraTypeClassVars)
|
|
;
|
|
IsExistConstr = is_not_exist_constr,
|
|
% Assume it is a deconstruction.
|
|
lookup_hlds_constraint_list(ConstraintMap, assumed, GoalId,
|
|
NumExistentialConstraints, ActualExistentialConstraints),
|
|
make_existq_typeclass_info_vars(ActualExistentialConstraints, Context,
|
|
ExtraTypeClassVars, ExtraTypeClassGoals, !Info)
|
|
),
|
|
|
|
% Create type_info variables for the _unconstrained_ existentially
|
|
% quantified type variables.
|
|
polymorphism_do_make_type_info_vars(ActualExistentialTypes, Context,
|
|
ExtraTypeInfoVarsMCAs, ExtraTypeInfoGoals, !Info),
|
|
assoc_list.keys(ExtraTypeInfoVarsMCAs, ExtraTypeInfoVars),
|
|
|
|
% The type_class_info variables go AFTER the type_info variables
|
|
% (for consistency with the order for argument passing,
|
|
% and because the RTTI support in the runtime system relies on it)
|
|
|
|
ExtraGoals = ExtraTypeInfoGoals ++ ExtraTypeClassGoals,
|
|
ExtraVars = ExtraTypeInfoVars ++ ExtraTypeClassVars.
|
|
|
|
%---------------------%
|
|
|
|
% If the lambda goal we are processing is polymorphically typed, we may
|
|
% need to fix up the quantification (nonlocal variables) so that it
|
|
% includes the type_info variables and typeclass_info variables for
|
|
% any polymorphically typed variables in the nonlocals set or in the
|
|
% arguments (either the lambda vars or the implicit curried argument
|
|
% variables). We return this set of LambdaTiTciVars. Including typeinfos
|
|
% for arguments which are not in the nonlocals set of the lambda goal,
|
|
% i.e. unused arguments, is necessary only if typeinfo_liveness is set,
|
|
% but we do it always, since we don't have the options available here,
|
|
% and the since cost is pretty minimal.
|
|
%
|
|
% We also need to include in the nonlocals set of the lambda expression
|
|
% any type_info and/or typeclass_info variables we have added to the
|
|
% goal inside the lambda. In rare cases such as tests/valid/gh98.m,
|
|
% a typeclass_info that we inserted into the goal inside the lambda
|
|
% is defined outside the lambda *without* being in LambdaTiTciVars.
|
|
% We therefore return all type_info/typeclass_info variables that occur
|
|
% in the transformed lambda goal in AllTiTciGoalVars, for our caller
|
|
% to likewise include in the nonlocals set of the lambda goal.
|
|
%
|
|
:- pred requantify_lambda_goal(list(prog_var)::in,
|
|
list(prog_var)::in, existq_tvars::in, hlds_goal::in, hlds_goal::out,
|
|
set_of_progvar::out, set_of_progvar::out,
|
|
poly_info::in, poly_info::out) is det.
|
|
|
|
requantify_lambda_goal(LambdaNonLocals0, ArgVars, ExistQVars, !Goal,
|
|
LambdaTiTciVars, AllTiTciGoalVars, !Info) :-
|
|
poly_info_get_rtti_varmaps(!.Info, RttiVarMaps0),
|
|
( if rtti_varmaps_no_tvars(RttiVarMaps0) then
|
|
set_of_var.init(LambdaTiTciVars),
|
|
set_of_var.init(AllTiTciGoalVars)
|
|
else
|
|
poly_info_get_var_table(!.Info, VarTable0),
|
|
!.Goal = hlds_goal(_, GoalInfo0),
|
|
NonLocals = goal_info_get_nonlocals(GoalInfo0),
|
|
set_of_var.insert_list(LambdaNonLocals0, NonLocals, BothNonLocals),
|
|
set_of_var.insert_list(ArgVars, BothNonLocals, NonLocalsWithArgVars),
|
|
goal_util.extra_nonlocal_typeinfos_typeclass_infos(RttiVarMaps0,
|
|
VarTable0, ExistQVars, NonLocalsWithArgVars, LambdaTiTciVars),
|
|
|
|
vars_in_goal(!.Goal, GoalVars),
|
|
IsTiOrTci =
|
|
( pred(Var::in) is semidet :-
|
|
lookup_var_type(VarTable0, Var, VarType),
|
|
( VarType = type_info_type
|
|
; VarType = typeclass_info_type
|
|
)
|
|
),
|
|
set_of_var.filter(IsTiOrTci, GoalVars, AllTiTciGoalVars),
|
|
% Our caller will include AllTiTciGoalVars in the nonlocals set
|
|
% of the rhs_lambda_goal, since some of them may actually come
|
|
% from code outside the lambda. However, since some (or even all)
|
|
% of the variables in this set may actually be local, we insist
|
|
% on the *whole* procedure body, not just the code of this lambda,
|
|
% being requantified once its polymorphism transformation has been
|
|
% completed. (This works both for variables in AllTiTciGoalVars
|
|
% that were added by polymorphism, and those which were there before.)
|
|
%
|
|
% Note that all the variables added by the polymorphism transformation
|
|
% of !Goal that may be nonlocal are type_infos and typeclass_infos.
|
|
% The polymorphism transformation can also add integer variables
|
|
% for use in e.g. getting a type_info out of a particular slot
|
|
% of a typeclass_info, but we set the set of cached int vars
|
|
% in the poly_info to empty at the start of transformation of !Goal,
|
|
% and throw away the updated caches at its end, so any integer vars
|
|
% created by the transformation inside !:Goal will be local to !:Goal.
|
|
poly_info_set_must_requantify(!Info),
|
|
set_of_var.union(NonLocalsWithArgVars, AllTiTciGoalVars,
|
|
PossibleOutsideVars),
|
|
implicitly_quantify_goal_general(ord_nl_maybe_lambda,
|
|
PossibleOutsideVars, _Warnings, !Goal,
|
|
VarTable0, VarTable, RttiVarMaps0, RttiVarMaps),
|
|
poly_info_set_var_table_rtti(VarTable, RttiVarMaps, !Info)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Process plain calls.
|
|
%
|
|
|
|
% XXX document me
|
|
%
|
|
% XXX the following code ought to be rewritten to handle
|
|
% existential/universal type_infos and type_class_infos
|
|
% in a more consistent manner.
|
|
%
|
|
:- pred polymorphism_process_call(pred_id::in, list(prog_var)::in,
|
|
hlds_goal_info::in, hlds_goal_info::out,
|
|
list(prog_var)::out, list(hlds_goal)::out,
|
|
poly_info::in, poly_info::out) is det.
|
|
|
|
polymorphism_process_call(PredId, ArgVars0, GoalInfo0, GoalInfo,
|
|
ExtraVars, ExtraGoals, !Info) :-
|
|
poly_info_get_var_table(!.Info, VarTable),
|
|
poly_info_get_typevarset(!.Info, TypeVarSet0),
|
|
poly_info_get_module_info(!.Info, ModuleInfo),
|
|
|
|
% The order of the added variables is important, and must match the
|
|
% order specified at the top of this file.
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
pred_info_get_arg_types(PredInfo, PredTypeVarSet, PredExistQVars,
|
|
PredArgTypes),
|
|
pred_info_get_tvar_kind_map(PredInfo, PredKindMap),
|
|
pred_info_get_class_context(PredInfo, PredClassContext),
|
|
|
|
% VarTable, TypeVarSet* etc come from the caller.
|
|
% PredTypeVarSet, PredArgTypes, PredExistQVars, etc come
|
|
% directly from the callee.
|
|
% ParentArgTypes, ParentExistQVars etc come from a version
|
|
% of the callee that has been renamed apart from the caller.
|
|
%
|
|
% The difference between e.g. PredArgTypes and ParentArgTypes is the
|
|
% application of PredToParentTypeRenaming, which maps the type variables
|
|
% in the callee to new type variables in the caller. Adding the new type
|
|
% variables to TypeVarSet0 yields TypeVarSet.
|
|
( if varset.is_empty(PredTypeVarSet) then
|
|
% Optimize a common case.
|
|
map.init(PredToParentTypeRenaming),
|
|
TypeVarSet = TypeVarSet0,
|
|
ParentArgTypes = PredArgTypes,
|
|
ParentKindMap = PredKindMap,
|
|
ParentTVars = [],
|
|
ParentExistQVars = []
|
|
else
|
|
% This merge might be a performance bottleneck?
|
|
tvarset_merge_renaming(TypeVarSet0, PredTypeVarSet, TypeVarSet,
|
|
PredToParentTypeRenaming),
|
|
apply_renaming_to_types(PredToParentTypeRenaming,
|
|
PredArgTypes, ParentArgTypes),
|
|
type_vars_in_types(ParentArgTypes, ParentTVars),
|
|
apply_renaming_to_tvar_kind_map(PredToParentTypeRenaming,
|
|
PredKindMap, ParentKindMap),
|
|
apply_renaming_to_tvars(PredToParentTypeRenaming,
|
|
PredExistQVars, ParentExistQVars)
|
|
),
|
|
|
|
PredModule = pred_info_module(PredInfo),
|
|
PredName = pred_info_name(PredInfo),
|
|
pred_info_get_orig_arity(PredInfo, pred_form_arity(PredFormArityInt)),
|
|
( if
|
|
(
|
|
% Optimize for the common case of nonpolymorphic call
|
|
% with no constraints.
|
|
ParentTVars = [],
|
|
PredClassContext = univ_exist_constraints([], [])
|
|
;
|
|
% Some builtins don't need or want the type_info.
|
|
no_type_info_builtin(PredModule, PredName, PredFormArityInt)
|
|
)
|
|
then
|
|
GoalInfo = GoalInfo0,
|
|
ExtraGoals = [],
|
|
ExtraVars = []
|
|
else
|
|
poly_info_set_typevarset(TypeVarSet, !Info),
|
|
|
|
% Compute which "parent" type variables are constrained
|
|
% by the type class constraints.
|
|
apply_renaming_to_univ_exist_constraints(PredToParentTypeRenaming,
|
|
PredClassContext, ParentClassContext),
|
|
ParentClassContext = univ_exist_constraints(ParentUnivConstraints,
|
|
ParentExistConstraints),
|
|
constraint_list_get_tvars(ParentUnivConstraints,
|
|
ParentUnivConstrainedTVars),
|
|
constraint_list_get_tvars(ParentExistConstraints,
|
|
ParentExistConstrainedTVars),
|
|
|
|
% Calculate the set of unconstrained type vars. Split these into
|
|
% existential and universal type vars.
|
|
list.remove_dups(ParentTVars, ParentUnconstrainedTVars0),
|
|
list.delete_elems(ParentUnconstrainedTVars0,
|
|
ParentUnivConstrainedTVars, ParentUnconstrainedTVars1),
|
|
list.delete_elems(ParentUnconstrainedTVars1,
|
|
ParentExistConstrainedTVars, ParentUnconstrainedTVars),
|
|
list.delete_elems(ParentUnconstrainedTVars, ParentExistQVars,
|
|
ParentUnconstrainedUnivTVars),
|
|
list.delete_elems(ParentUnconstrainedTVars,
|
|
ParentUnconstrainedUnivTVars, ParentUnconstrainedExistTVars),
|
|
|
|
% Calculate the "parent to actual" binding.
|
|
lookup_var_types(VarTable, ArgVars0, ActualArgTypes),
|
|
type_list_subsumes_det(ParentArgTypes, ActualArgTypes,
|
|
ParentToActualTypeSubst),
|
|
|
|
% Make the universally quantified typeclass_infos for the call.
|
|
poly_info_get_constraint_map(!.Info, ConstraintMap),
|
|
GoalId = goal_info_get_goal_id(GoalInfo0),
|
|
list.length(ParentUnivConstraints, NumUnivConstraints),
|
|
lookup_hlds_constraint_list(ConstraintMap, unproven, GoalId,
|
|
NumUnivConstraints, ActualUnivConstraints),
|
|
apply_rec_subst_to_tvars(ParentKindMap, ParentToActualTypeSubst,
|
|
ParentExistQVars, ActualExistQVarTypes),
|
|
( if
|
|
prog_type.type_list_to_var_list(ActualExistQVarTypes,
|
|
ActualExistQVars0)
|
|
then
|
|
ActualExistQVars = ActualExistQVars0
|
|
else
|
|
unexpected($pred, "existq_tvar bound")
|
|
),
|
|
Context = goal_info_get_context(GoalInfo0),
|
|
make_typeclass_info_vars(ActualUnivConstraints, ActualExistQVars,
|
|
Context, ExtraUnivClassVarsMCAs, ExtraUnivClassGoals, !Info),
|
|
assoc_list.keys(ExtraUnivClassVarsMCAs, ExtraUnivClassVars),
|
|
|
|
% Make variables to hold any existentially quantified typeclass_infos
|
|
% in the call, insert them into the typeclass_info map.
|
|
list.length(ParentExistConstraints, NumExistConstraints),
|
|
lookup_hlds_constraint_list(ConstraintMap, assumed, GoalId,
|
|
NumExistConstraints, ActualExistConstraints),
|
|
make_existq_typeclass_info_vars(ActualExistConstraints, Context,
|
|
ExtraExistClassVars, ExtraExistClassGoals, !Info),
|
|
|
|
% Make variables to hold typeinfos for unconstrained universal type
|
|
% vars.
|
|
apply_rec_subst_to_tvars(ParentKindMap, ParentToActualTypeSubst,
|
|
ParentUnconstrainedUnivTVars, ActualUnconstrainedUnivTypes),
|
|
polymorphism_do_make_type_info_vars(ActualUnconstrainedUnivTypes,
|
|
Context, ExtraUnivTypeInfoVarsMCAs,
|
|
ExtraUnivTypeInfoGoals, !Info),
|
|
assoc_list.keys(ExtraUnivTypeInfoVarsMCAs,
|
|
ExtraUnivTypeInfoVars),
|
|
|
|
% Make variables to hold typeinfos for unconstrained existential type
|
|
% vars.
|
|
apply_rec_subst_to_tvars(ParentKindMap, ParentToActualTypeSubst,
|
|
ParentUnconstrainedExistTVars, ActualUnconstrainedExistTypes),
|
|
polymorphism_do_make_type_info_vars(ActualUnconstrainedExistTypes,
|
|
Context, ExtraExistTypeInfoVarsMCAs,
|
|
ExtraExistTypeInfoGoals, !Info),
|
|
assoc_list.keys(ExtraExistTypeInfoVarsMCAs,
|
|
ExtraExistTypeInfoVars),
|
|
|
|
% Add up the extra vars and goals.
|
|
ExtraGoals = ExtraUnivClassGoals ++ ExtraExistClassGoals
|
|
++ ExtraUnivTypeInfoGoals ++ ExtraExistTypeInfoGoals,
|
|
ExtraVars = ExtraUnivTypeInfoVars ++ ExtraExistTypeInfoVars
|
|
++ ExtraUnivClassVars ++ ExtraExistClassVars,
|
|
|
|
% Update the nonlocals.
|
|
NonLocals0 = goal_info_get_nonlocals(GoalInfo0),
|
|
set_of_var.insert_list(ExtraVars, NonLocals0, NonLocals),
|
|
goal_info_set_nonlocals(NonLocals, GoalInfo0, GoalInfo)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% Add the type_info variables for a new call goal. This predicate assumes
|
|
% that process_module has already been run so the called pred has already
|
|
% been processed.
|
|
%
|
|
% XXX This predicate does not yet handle calls whose arguments include
|
|
% existentially quantified types or type class constraints.
|
|
%
|
|
% XXX This predicate is unused.
|
|
%
|
|
:- pred polymorphism_process_new_call(pred_info::in, proc_info::in,
|
|
pred_id::in, proc_id::in, list(prog_var)::in, builtin_state::in,
|
|
maybe(call_unify_context)::in, sym_name::in, hlds_goal_info::in,
|
|
hlds_goal::out, poly_info::in, poly_info::out) is det.
|
|
:- pragma consider_used(pred(polymorphism_process_new_call/12)).
|
|
|
|
polymorphism_process_new_call(CalleePredInfo, CalleeProcInfo, PredId, ProcId,
|
|
CallArgs0, BuiltinState, MaybeCallUnifyContext, SymName,
|
|
GoalInfo0, Goal, !Info) :-
|
|
poly_info_get_typevarset(!.Info, TVarSet0),
|
|
poly_info_get_var_table(!.Info, VarTable0),
|
|
lookup_var_types(VarTable0, CallArgs0, ActualArgTypes0),
|
|
pred_info_get_arg_types(CalleePredInfo, PredTVarSet, _PredExistQVars,
|
|
PredArgTypes),
|
|
proc_info_get_headvars(CalleeProcInfo, CalleeHeadVars),
|
|
proc_info_get_rtti_varmaps(CalleeProcInfo, CalleeRttiVarMaps),
|
|
|
|
% Work out how many type_info args we need to prepend.
|
|
NCallArgs0 = list.length(ActualArgTypes0),
|
|
NPredArgs = list.length(PredArgTypes),
|
|
NExtraArgs = NPredArgs - NCallArgs0,
|
|
( if
|
|
list.drop(NExtraArgs, PredArgTypes, OrigPredArgTypes0),
|
|
list.take(NExtraArgs, CalleeHeadVars, CalleeExtraHeadVars0)
|
|
then
|
|
OrigPredArgTypes = OrigPredArgTypes0,
|
|
CalleeExtraHeadVars = CalleeExtraHeadVars0
|
|
else
|
|
unexpected($pred, "extra args not found")
|
|
),
|
|
|
|
% Work out the bindings of type variables in the call.
|
|
tvarset_merge_renaming(TVarSet0, PredTVarSet, TVarSet,
|
|
PredToParentRenaming),
|
|
apply_renaming_to_types(PredToParentRenaming,
|
|
OrigPredArgTypes, OrigParentArgTypes),
|
|
type_list_subsumes_det(OrigParentArgTypes, ActualArgTypes0,
|
|
ParentToActualTSubst),
|
|
poly_info_set_typevarset(TVarSet, !Info),
|
|
|
|
% Look up the type variables that the type_infos in the caller are for,
|
|
% and apply the type bindings to calculate the types that the caller
|
|
% should pass type_infos for.
|
|
GetTypeInfoTypes =
|
|
( pred(ProgVar::in, TypeInfoType::out) is det :-
|
|
rtti_varmaps_var_info(CalleeRttiVarMaps, ProgVar, VarInfo),
|
|
(
|
|
VarInfo = type_info_var(TypeInfoType)
|
|
;
|
|
VarInfo = typeclass_info_var(_),
|
|
unexpected($pred,
|
|
"unsupported: constraints on initialisation preds")
|
|
;
|
|
VarInfo = non_rtti_var,
|
|
unexpected($pred,
|
|
"missing rtti_var_info for initialisation pred")
|
|
)
|
|
),
|
|
list.map(GetTypeInfoTypes, CalleeExtraHeadVars, PredTypeInfoTypes),
|
|
apply_renaming_to_types(PredToParentRenaming,
|
|
PredTypeInfoTypes, ParentTypeInfoTypes),
|
|
apply_rec_subst_to_types(ParentToActualTSubst,
|
|
ParentTypeInfoTypes, ActualTypeInfoTypes),
|
|
|
|
% Construct goals to make the required type_infos.
|
|
Ctxt = term_context.dummy_context,
|
|
polymorphism_do_make_type_info_vars(ActualTypeInfoTypes, Ctxt,
|
|
ExtraArgsConstArgs, ExtraGoals, !Info),
|
|
assoc_list.keys(ExtraArgsConstArgs, ExtraArgs),
|
|
CallArgs = ExtraArgs ++ CallArgs0,
|
|
NonLocals0 = goal_info_get_nonlocals(GoalInfo0),
|
|
NonLocals1 = set_of_var.list_to_set(ExtraArgs),
|
|
set_of_var.union(NonLocals0, NonLocals1, NonLocals),
|
|
goal_info_set_nonlocals(NonLocals, GoalInfo0, GoalInfo),
|
|
CallGoalExpr = plain_call(PredId, ProcId, CallArgs, BuiltinState,
|
|
MaybeCallUnifyContext, SymName),
|
|
CallGoal = hlds_goal(CallGoalExpr, GoalInfo),
|
|
conj_list_to_goal(ExtraGoals ++ [CallGoal], GoalInfo, Goal).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Process foreign proc calls.
|
|
%
|
|
|
|
:- pred polymorphism_process_foreign_proc(pred_info::in,
|
|
hlds_goal_expr::in(bound(call_foreign_proc(ground,ground,ground,ground,
|
|
ground,ground,ground))), hlds_goal_info::in, hlds_goal::out,
|
|
poly_info::in, poly_info::out) is det.
|
|
|
|
polymorphism_process_foreign_proc(PredInfo, GoalExpr0, GoalInfo0,
|
|
Goal, !Info) :-
|
|
% Insert the type_info vars into the argname map, so that the foreign_proc
|
|
% can refer to the type_info variable for type T as `TypeInfo_for_T'.
|
|
GoalExpr0 = call_foreign_proc(Attributes, PredId, ProcId,
|
|
Args0, ProcExtraArgs, MaybeTraceRuntimeCond, Impl),
|
|
ArgVars0 = list.map(foreign_arg_var, Args0),
|
|
polymorphism_process_call(PredId, ArgVars0, GoalInfo0, GoalInfo,
|
|
ExtraVars, ExtraGoals, !Info),
|
|
polymorphism_process_foreign_proc_args(PredInfo, Impl,
|
|
ExtraVars, ExtraArgs),
|
|
Args = ExtraArgs ++ Args0,
|
|
|
|
% Plug it all back together.
|
|
CallExpr = call_foreign_proc(Attributes, PredId, ProcId,
|
|
Args, ProcExtraArgs, MaybeTraceRuntimeCond, Impl),
|
|
Call = hlds_goal(CallExpr, GoalInfo),
|
|
GoalList = ExtraGoals ++ [Call],
|
|
conj_list_to_goal(GoalList, GoalInfo0, Goal).
|
|
|
|
:- pred polymorphism_process_foreign_proc_args(pred_info::in,
|
|
pragma_foreign_proc_impl::in, list(prog_var)::in, list(foreign_arg)::out)
|
|
is det.
|
|
|
|
polymorphism_process_foreign_proc_args(PredInfo, Impl, Vars, Args) :-
|
|
pred_info_get_arg_types(PredInfo, PredTypeVarSet, ExistQVars,
|
|
PredArgTypes),
|
|
|
|
% Find out which variables are constrained (so that we don't add
|
|
% type_infos for them).
|
|
pred_info_get_class_context(PredInfo,
|
|
univ_exist_constraints(UnivCs, ExistCs)),
|
|
UnivVars0 = list.map(get_constrained_vars, UnivCs),
|
|
list.condense(UnivVars0, UnivConstrainedVars),
|
|
ExistVars0 = list.map(get_constrained_vars, ExistCs),
|
|
list.condense(ExistVars0, ExistConstrainedVars),
|
|
|
|
type_vars_in_types(PredArgTypes, PredTypeVars0),
|
|
list.remove_dups(PredTypeVars0, PredTypeVars1),
|
|
list.delete_elems(PredTypeVars1, UnivConstrainedVars, PredTypeVars2),
|
|
list.delete_elems(PredTypeVars2, ExistConstrainedVars, PredTypeVars),
|
|
|
|
% The argument order is described at the top of polymorphism.m.
|
|
|
|
in_mode(In),
|
|
out_mode(Out),
|
|
|
|
list.map(foreign_proc_add_typeclass_info(Out, Impl, PredTypeVarSet),
|
|
ExistCs, ExistTypeClassArgInfos),
|
|
list.map(foreign_proc_add_typeclass_info(In, Impl, PredTypeVarSet),
|
|
UnivCs, UnivTypeClassArgInfos),
|
|
TypeClassArgInfos = UnivTypeClassArgInfos ++ ExistTypeClassArgInfos,
|
|
|
|
list.filter(list.contains(ExistQVars), PredTypeVars,
|
|
ExistUnconstrainedVars, UnivUnconstrainedVars),
|
|
|
|
list.map_foldl(foreign_proc_add_typeinfo("Out", Out, Impl, PredTypeVarSet),
|
|
ExistUnconstrainedVars, ExistTypeArgInfos, 1, _),
|
|
list.map_foldl(foreign_proc_add_typeinfo("In", In, Impl, PredTypeVarSet),
|
|
UnivUnconstrainedVars, UnivTypeArgInfos, 1, _),
|
|
TypeInfoArgInfos = UnivTypeArgInfos ++ ExistTypeArgInfos,
|
|
|
|
ArgInfos = TypeInfoArgInfos ++ TypeClassArgInfos,
|
|
|
|
% Insert type_info/typeclass_info types for all the inserted
|
|
% type_info/typeclass_info vars into the argument type list.
|
|
TypeInfoTypes = list.map((func(_) = type_info_type), PredTypeVars),
|
|
TypeClassInfoType = typeclass_info_type,
|
|
list.length(UnivCs, NumUnivCs),
|
|
list.length(ExistCs, NumExistCs),
|
|
list.duplicate(NumUnivCs + NumExistCs, TypeClassInfoType,
|
|
TypeClassInfoTypes),
|
|
OrigArgTypes = TypeInfoTypes ++ TypeClassInfoTypes,
|
|
|
|
make_foreign_args(Vars, ArgInfos, OrigArgTypes, Args).
|
|
|
|
:- func get_constrained_vars(prog_constraint) = list(tvar).
|
|
|
|
get_constrained_vars(Constraint) = CVars :-
|
|
Constraint = constraint(_, CTypes),
|
|
type_vars_in_types(CTypes, CVars).
|
|
|
|
:- pred foreign_proc_add_typeclass_info(mer_mode::in,
|
|
pragma_foreign_proc_impl::in, tvarset::in, prog_constraint::in,
|
|
foreign_arg_name_mode_box::out) is det.
|
|
|
|
foreign_proc_add_typeclass_info(Mode, Impl, TypeVarSet, Constraint,
|
|
MaybeArgNameBox) :-
|
|
Constraint = constraint(SymName, Types),
|
|
Name = sym_name_to_string_sep(SymName, "__"),
|
|
type_vars_in_types(Types, TypeVars),
|
|
TypeVarNames = list.map(underscore_and_tvar_name(TypeVarSet), TypeVars),
|
|
string.append_list(["TypeClassInfo_for_", Name | TypeVarNames],
|
|
ConstraintVarName),
|
|
% If the variable name corresponding to the typeclass_info isn't mentioned
|
|
% in the C code fragment, don't pass the variable to the C code at all.
|
|
( if foreign_proc_uses_variable(Impl, ConstraintVarName) then
|
|
MaybeArgName = yes(foreign_arg_name_mode(ConstraintVarName, Mode))
|
|
else
|
|
MaybeArgName = no
|
|
),
|
|
MaybeArgNameBox =
|
|
foreign_arg_name_mode_box(MaybeArgName, bp_native_if_possible).
|
|
|
|
:- pred foreign_proc_add_typeinfo(string::in, mer_mode::in,
|
|
pragma_foreign_proc_impl::in, tvarset::in,
|
|
tvar::in, foreign_arg_name_mode_box::out, int::in, int::out) is det.
|
|
|
|
foreign_proc_add_typeinfo(InOut, Mode, Impl, TypeVarSet, TVar, MaybeArgNameBox,
|
|
!N) :-
|
|
( if varset.search_name(TypeVarSet, TVar, TypeVarName) then
|
|
OldCVarName = "TypeInfo_for_" ++ TypeVarName,
|
|
NewCVarName = "TypeInfo_" ++ InOut ++ "_" ++ string.int_to_string(!.N),
|
|
% If the variable name corresponding to the type_info isn't mentioned
|
|
% in the C code fragment, don't pass the variable to the C code at all.
|
|
( if
|
|
( foreign_proc_uses_variable(Impl, OldCVarName)
|
|
; foreign_proc_uses_variable(Impl, NewCVarName)
|
|
)
|
|
then
|
|
MaybeArgName = yes(foreign_arg_name_mode(OldCVarName, Mode))
|
|
else
|
|
MaybeArgName = no
|
|
)
|
|
else
|
|
MaybeArgName = no
|
|
),
|
|
MaybeArgNameBox =
|
|
foreign_arg_name_mode_box(MaybeArgName, bp_native_if_possible),
|
|
!:N = !.N + 1.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Process scopes.
|
|
%
|
|
|
|
:- pred polymorphism_process_from_ground_term_initial(prog_var::in,
|
|
hlds_goal_info::in, hlds_goal::in, hlds_goal_expr::out,
|
|
poly_info::in, poly_info::out) is det.
|
|
|
|
polymorphism_process_from_ground_term_initial(TermVar, GoalInfo0, SubGoal0,
|
|
GoalExpr, !Info) :-
|
|
SubGoal0 = hlds_goal(SubGoalExpr0, SubGoalInfo0),
|
|
( if SubGoalExpr0 = conj(plain_conj, SubGoals0Prime) then
|
|
SubGoals0 = SubGoals0Prime
|
|
else
|
|
unexpected($pred, "from_ground_term_initial goal is not plain conj")
|
|
),
|
|
polymorphism_process_fgti_goals(SubGoals0,
|
|
[], ConstructOrderMarkedSubGoals,
|
|
fgt_invariants_kept, InvariantsStatus, !Info),
|
|
(
|
|
InvariantsStatus = fgt_invariants_kept,
|
|
Reason = from_ground_term(TermVar, from_ground_term_initial),
|
|
GoalExpr = scope(Reason, SubGoal0)
|
|
;
|
|
InvariantsStatus = fgt_invariants_broken,
|
|
poly_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_opt_tuple(Globals, OptTuple),
|
|
Threshold = OptTuple ^ ot_from_ground_term_threshold,
|
|
introduce_partial_fgt_scopes(Threshold, deconstruct_top_down,
|
|
GoalInfo0, SubGoalInfo0, ConstructOrderMarkedSubGoals, SubGoal),
|
|
% Delete the scope wrapper around SubGoal0.
|
|
SubGoal = hlds_goal(GoalExpr, _)
|
|
).
|
|
|
|
:- pred polymorphism_process_fgti_goals(list(hlds_goal)::in,
|
|
list(fgt_marked_goal)::in, list(fgt_marked_goal)::out,
|
|
fgt_invariants_status::in, fgt_invariants_status::out,
|
|
poly_info::in, poly_info::out) is det.
|
|
|
|
polymorphism_process_fgti_goals([], !ConstructOrderMarkedGoals,
|
|
!InvariantsStatus, !Info).
|
|
polymorphism_process_fgti_goals([Goal0 | Goals0], !ConstructOrderMarkedGoals,
|
|
!InvariantsStatus, !Info) :-
|
|
% This is used only if polymorphism_fgt_sanity_tests is enabled.
|
|
OldInfo = !.Info,
|
|
Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
|
|
( if
|
|
GoalExpr0 = unify(LHSVarPrime, RHS, ModePrime, UnificationPrime,
|
|
UnifyContextPrime),
|
|
RHS = rhs_functor(ConsIdPrime, _, RHSVarsPrime)
|
|
then
|
|
LHSVar = LHSVarPrime,
|
|
Mode = ModePrime,
|
|
Unification = UnificationPrime,
|
|
UnifyContext = UnifyContextPrime,
|
|
ConsId = ConsIdPrime,
|
|
RHSVars = RHSVarsPrime
|
|
else
|
|
unexpected($pred,
|
|
"from_ground_term_initial conjunct is not functor unify")
|
|
),
|
|
polymorphism_process_unify_functor(LHSVar, ConsId, RHSVars, Mode,
|
|
Unification, UnifyContext, GoalInfo0, Goal, Changed, !Info),
|
|
(
|
|
Changed = no,
|
|
trace [compiletime(flag("polymorphism_fgt_sanity_tests"))] (
|
|
poly_info_get_var_table(OldInfo, VarTableBefore),
|
|
MaxVarBefore = var_table_count(VarTableBefore),
|
|
poly_info_get_num_reuses(OldInfo, NumReusesBefore),
|
|
|
|
poly_info_get_var_table(!.Info, VarTableAfter),
|
|
MaxVarAfter = var_table_count(VarTableAfter),
|
|
poly_info_get_num_reuses(!.Info, NumReusesAfter),
|
|
|
|
expect(unify(MaxVarBefore, MaxVarAfter), $pred,
|
|
"MaxVarBefore != MaxVarAfter"),
|
|
expect(unify(NumReusesBefore, NumReusesAfter), $pred,
|
|
"NumReusesBefore != NumReusesAfter"),
|
|
expect(unify(Goal0, Goal), $pred,
|
|
"Goal0 != Goal")
|
|
),
|
|
MarkedGoal = fgt_kept_goal(Goal0, LHSVar, RHSVars)
|
|
;
|
|
Changed = yes,
|
|
MarkedGoal = fgt_broken_goal(Goal, LHSVar, RHSVars),
|
|
!:InvariantsStatus = fgt_invariants_broken
|
|
),
|
|
!:ConstructOrderMarkedGoals = [MarkedGoal | !.ConstructOrderMarkedGoals],
|
|
polymorphism_process_fgti_goals(Goals0, !ConstructOrderMarkedGoals,
|
|
!InvariantsStatus, !Info).
|
|
|
|
:- func underscore_and_tvar_name(tvarset, tvar) = string.
|
|
|
|
underscore_and_tvar_name(TypeVarSet, TVar) = TVarName :-
|
|
varset.lookup_name(TypeVarSet, TVar, TVarName0),
|
|
TVarName = "_" ++ TVarName0.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Process compound goals.
|
|
%
|
|
|
|
:- pred polymorphism_process_plain_conj(list(hlds_goal)::in,
|
|
list(hlds_goal)::out, poly_info::in, poly_info::out) is det.
|
|
|
|
polymorphism_process_plain_conj([], [], !Info).
|
|
polymorphism_process_plain_conj([Goal0 | Goals0], [Goal | Goals], !Info) :-
|
|
polymorphism_process_goal(Goal0, Goal, !Info),
|
|
polymorphism_process_plain_conj(Goals0, Goals, !Info).
|
|
|
|
:- pred polymorphism_process_par_conj(list(hlds_goal)::in,
|
|
list(hlds_goal)::out, cache_maps::in, poly_info::in, poly_info::out)
|
|
is det.
|
|
|
|
polymorphism_process_par_conj([], [], _, !Info).
|
|
polymorphism_process_par_conj([Goal0 | Goals0], [Goal | Goals],
|
|
InitialSnapshot, !Info) :-
|
|
% Any variable that a later parallel conjunct reuses from an earlier
|
|
% parallel conjunct (a) will definitely require synchronization, whose
|
|
% cost will be greater than the cost of building a typeinfo from scratch,
|
|
% and (b) may drastically reduce the available parallelism, if the earlier
|
|
% conjunct produces the variable late but the later conjunct requires it
|
|
% early.
|
|
set_cache_maps_snapshot("par conjunct", InitialSnapshot, !Info),
|
|
polymorphism_process_goal(Goal0, Goal, !Info),
|
|
polymorphism_process_par_conj(Goals0, Goals, InitialSnapshot, !Info).
|
|
|
|
:- pred polymorphism_process_disj(list(hlds_goal)::in, list(hlds_goal)::out,
|
|
cache_maps::in, poly_info::in, poly_info::out) is det.
|
|
|
|
polymorphism_process_disj([], [], _, !Info).
|
|
polymorphism_process_disj([Goal0 | Goals0], [Goal | Goals], InitialSnapshot,
|
|
!Info) :-
|
|
set_cache_maps_snapshot("disjunct", InitialSnapshot, !Info),
|
|
polymorphism_process_goal(Goal0, Goal, !Info),
|
|
polymorphism_process_disj(Goals0, Goals, InitialSnapshot, !Info).
|
|
|
|
:- pred polymorphism_process_cases(list(case)::in, list(case)::out,
|
|
cache_maps::in, poly_info::in, poly_info::out) is det.
|
|
|
|
polymorphism_process_cases([], [], _, !Info).
|
|
polymorphism_process_cases([Case0 | Cases0], [Case | Cases], InitialSnapshot,
|
|
!Info) :-
|
|
Case0 = case(MainConsId, OtherConsIds, Goal0),
|
|
set_cache_maps_snapshot("case", InitialSnapshot, !Info),
|
|
polymorphism_process_goal(Goal0, Goal, !Info),
|
|
Case = case(MainConsId, OtherConsIds, Goal),
|
|
polymorphism_process_cases(Cases0, Cases, InitialSnapshot, !Info).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module check_hlds.polymorphism_goal.
|
|
%---------------------------------------------------------------------------%
|