mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
785 lines
33 KiB
Mathematica
785 lines
33 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2018-2019, 2022-2026 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: hlds_class.m.
|
|
% Main authors: fjh, conway.
|
|
%
|
|
% This module defines the part of the HLDS that deals with type classes
|
|
% and type class constraints.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module hlds.hlds_class.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.status.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.goal_path.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.type_util.
|
|
:- import_module parse_tree.prog_type_scan.
|
|
:- import_module parse_tree.prog_type_subst.
|
|
|
|
:- import_module cord.
|
|
:- import_module int.
|
|
:- import_module require.
|
|
:- import_module varset.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- interface.
|
|
|
|
:- type class_table == map(class_id, hlds_class_defn).
|
|
|
|
% Information about a single `typeclass' declaration.
|
|
%
|
|
:- type hlds_class_defn
|
|
---> hlds_class_defn(
|
|
classdefn_status :: typeclass_status,
|
|
|
|
% The names and kinds of type variables.
|
|
classdefn_tvarset :: tvarset,
|
|
classdefn_kinds :: tvar_kind_map,
|
|
|
|
% ClassVars.
|
|
classdefn_vars :: list(tvar),
|
|
|
|
% SuperClasses.
|
|
%
|
|
% Every type appearing in every superclass constraint must be
|
|
% either a type variable, or a ground type; types containing
|
|
% both type variables and type constructors are not allowed.
|
|
% This is enforced by parse_superclass_constraints in
|
|
% parse_class.m.
|
|
classdefn_supers :: list(prog_constraint),
|
|
|
|
% Functional dependencies.
|
|
classdefn_fundeps :: list(hlds_class_fundep),
|
|
|
|
% All ancestors which have fundeps on them.
|
|
classdefn_fundep_ancestors :: list(prog_constraint),
|
|
|
|
% The interface from the original declaration, used by
|
|
% intermod.m to write out the interface for a local typeclass
|
|
% to the `.opt' file.
|
|
% XXX METHOD intermod*.m does not refer to class_interfaces.
|
|
classdefn_interface :: class_interface,
|
|
|
|
% Methods. There is one method_info for every mode
|
|
% of every predicate or function method in the typeclass
|
|
% declaration. The order is given
|
|
% - first by the order of the predicate or function
|
|
% declarations in the typeclass declaration,
|
|
% - and then, within the sequence of method_infos for any
|
|
% given method (which will always be contiguous),
|
|
% by the order of the mode declarations for the given
|
|
% method.
|
|
classdefn_method_infos :: list(method_info),
|
|
|
|
% Location of the class declaration.
|
|
classdefn_context :: prog_context,
|
|
|
|
% Does this class have a bad definition? If yes, record
|
|
% that fact in this field, so we can avoid generating a
|
|
% misleading error message about the class having *no*
|
|
% definition.
|
|
classdefn_maybe_bad :: maybe_bad_class_defn
|
|
).
|
|
|
|
% In the HLDS, functional dependencies are represented using
|
|
% argument positions (counting from 1) rather than type variables.
|
|
% We know that there will be a one-one correspondence since
|
|
% typeclass parameters must be distinct variables, and using
|
|
% argument positions is more efficient.
|
|
%
|
|
% It is an invariant that both sets must be nonempty.
|
|
%
|
|
:- type hlds_class_fundep
|
|
---> fundep(
|
|
domain :: set(hlds_class_argpos),
|
|
range :: set(hlds_class_argpos)
|
|
).
|
|
|
|
:- type hlds_class_argpos == int.
|
|
|
|
:- type maybe_bad_class_defn
|
|
---> has_no_bad_class_defn
|
|
; has_bad_class_defn.
|
|
|
|
:- func restrict_list_elements(set(hlds_class_argpos), list(T)) = list(T).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% For each class, we keep track of a list of its instances, since there
|
|
% can be more than one instance of each class. Each visible instance
|
|
% is assigned a unique identifier (integers beginning from one).
|
|
% The position in the list of instances corresponds to integer
|
|
% inside the instance_id.
|
|
%
|
|
% We use a list for two reasons.
|
|
%
|
|
% The first reason is that a ground type may be the instance of
|
|
% more than one type containing type variables, and the same is true
|
|
% for vectors of types. This means that the search for an instance_defn
|
|
% that matches the particular type vector in a given situation
|
|
% can match more than one instance_defn, which makes the task of
|
|
% designing a data structure that does useful indexing much harder.
|
|
%
|
|
% The second reason is that in all non-pathological cases, the list is
|
|
% short enough for the lack of indexing not to be a problem.
|
|
%
|
|
:- type instance_table == map(class_id, list(hlds_instance_defn)).
|
|
:- type instance_id
|
|
---> instance_id(int).
|
|
|
|
% Information about a single `instance' declaration.
|
|
% The order of fields is intended to put the list of hlds_instance_defns
|
|
% of a class into a stable order when the hlds_instance_defns are sorted.
|
|
% ("Stable" meaning that changing *only* the order of the instance
|
|
% declarations in a source module should leave the contents of the
|
|
% .opt file unchanged.)
|
|
%
|
|
:- type hlds_instance_defn
|
|
---> hlds_instance_defn(
|
|
% Module of the instance declaration.
|
|
instdefn_module :: module_name,
|
|
|
|
% Import status of the instance declaration.
|
|
% XXX This can be set to abstract_imported even if
|
|
% the instance is NOT imported.
|
|
instdefn_status :: instance_status,
|
|
|
|
% VarNames
|
|
instdefn_tvarset :: tvarset,
|
|
|
|
% The class types. The types field is subject to the expansion
|
|
% of equivalence types; the original types field is not.
|
|
% The original types field is used only for error checking.
|
|
instdefn_orig_types :: list(mer_type),
|
|
instdefn_types :: list(mer_type),
|
|
|
|
% Constraints on the instance declaration.
|
|
instdefn_constraints :: list(prog_constraint),
|
|
|
|
% If this is a concrete instance definition in the local module
|
|
% that subsumes an abstract instance definition that is
|
|
% also local (subsuming it in the sense of being equivalent
|
|
% to it but providing a definition), then this field should
|
|
% contain the context of the subsumed definition.
|
|
%
|
|
% This field is filled in by the check_typeclass pass.
|
|
% Until that is run, this field should always contain `no'.
|
|
instdefn_subsumed_ctxt :: maybe(prog_context),
|
|
|
|
% "Proofs" of how to build the typeclass_infos for the
|
|
% superclasses of this class (that is, the constraints
|
|
% on the class declaration), for this instance.
|
|
instdefn_proofs :: constraint_proof_map,
|
|
|
|
% Methods.
|
|
instdefn_body :: instance_body,
|
|
|
|
% Before the check_typeclass pass, this field will be `no'.
|
|
% After the check_typeclass pass, it should be `yes(...)',
|
|
% with a method_info for every mode of every method.
|
|
% Each of these method_infos will contain the pred_proc_id
|
|
% of the procedure that check_typeclass.m creates as the
|
|
% implementation of the given mode of a given method.
|
|
% The body goal of this procedure will be the method's
|
|
% implementation from the instance declaration: either
|
|
% a call to the named implementation predicate or function,
|
|
% or the code of the provided clauses. Any problems with
|
|
% the implementation of a method will be detected via
|
|
% the usual semantic checks on these procedures
|
|
% carried out by mercury_compile_front_end.m.
|
|
%
|
|
% The order of the method_infos here will match
|
|
% the order of the method_infos in the hlds_class_defn.
|
|
instdefn_maybe_method_infos :: maybe(list(method_info)),
|
|
|
|
% The context of the instance declaration.
|
|
instdefn_context :: prog_context
|
|
).
|
|
|
|
% Return the value of the MR_typeclass_info_num_extra_instance_args field
|
|
% in the base_typeclass_info of the given instance.
|
|
%
|
|
:- pred num_extra_instance_args(hlds_instance_defn::in, int::out) is det.
|
|
|
|
:- type method_info
|
|
---> method_info(
|
|
% The number of this method procedure.
|
|
% The numbering starts at #1.
|
|
method_proc_number :: method_proc_num,
|
|
|
|
% The name of the method predicate or function.
|
|
% If this predicate or function has N modes,
|
|
% then the list of method_infos of the class or instance
|
|
% definition will contain N consecutive method_infos,
|
|
% all with the same method_pred_name field.
|
|
% They will also have the same pred_id in the first half
|
|
% of the method_orig_proc field, but the proc_ids in the
|
|
% second half will monotonically increase.
|
|
method_pred_name :: pred_pf_name_arity,
|
|
|
|
% The originally created implementation of this procedure.
|
|
% For method_infos in class
|
|
method_orig_proc :: pred_proc_id,
|
|
|
|
% The current implementation of this procedure.
|
|
% For method_infos in hlds_class_defns, this should be
|
|
% the same procedure as method_orig_proc.
|
|
% For method_infos in hlds_instance_defns, this should
|
|
% *start out* as the same procedure as method_orig_proc,
|
|
% but it may be updated by compiler passes after
|
|
% check_typeclass.m. These passes need not preserve
|
|
% the original grouping of method procedures into method
|
|
% predicates or functions. It is therefore possible for
|
|
% the method_info list of an instance definition to contain
|
|
% two method_infos whose method_orig_proc field contain e.g.
|
|
%
|
|
% proc(pred_id(10), proc_id(0)), and
|
|
% proc(pred_id(10), proc_id(1))
|
|
%
|
|
% (i.e. the procedures refer to the same pred_id), but whose
|
|
% method_cur_proc fields contain
|
|
%
|
|
% proc(pred_id(20), proc_id(0)), and
|
|
% proc(pred_id(21), proc_id(1)),
|
|
%
|
|
% (i.e. the procedures refer to different pred_ids).
|
|
% The direct_arg_in_out pass is one (and currently the only)
|
|
% example of a program transformation that can do this.
|
|
method_cur_proc :: pred_proc_id
|
|
).
|
|
|
|
:- type method_proc_num
|
|
---> method_proc_num(int).
|
|
|
|
:- func lookup_method_proc(list(method_info), method_proc_num) = method_info.
|
|
|
|
:- func method_infos_to_pred_proc_ids(list(method_info)) = list(pred_proc_id).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
restrict_list_elements(Elements, List) = RestrictedList :-
|
|
set.to_sorted_list(Elements, SortedElements),
|
|
restrict_list_elements_2(SortedElements, 1, List, RestrictedList).
|
|
|
|
:- pred restrict_list_elements_2(list(hlds_class_argpos)::in,
|
|
hlds_class_argpos::in, list(T)::in, list(T)::out) is det.
|
|
|
|
restrict_list_elements_2(_, _, [], []).
|
|
restrict_list_elements_2([], _, [_ | _], []).
|
|
restrict_list_elements_2([Posn | Posns], Index, [X | Xs], RestrictedXs) :-
|
|
( if Index = Posn then
|
|
restrict_list_elements_2(Posns, Index + 1, Xs, TailRestrictedXs),
|
|
RestrictedXs = [X | TailRestrictedXs]
|
|
else if Index < Posn then
|
|
restrict_list_elements_2([Posn | Posns], Index + 1, Xs, RestrictedXs)
|
|
else
|
|
restrict_list_elements_2(Posns, Index + 1, [X | Xs], RestrictedXs)
|
|
).
|
|
|
|
num_extra_instance_args(InstanceDefn, NumExtra) :-
|
|
InstanceTypes = InstanceDefn ^ instdefn_types,
|
|
InstanceConstraints = InstanceDefn ^ instdefn_constraints,
|
|
type_vars_in_types(InstanceTypes, TypeVars),
|
|
get_unconstrained_tvars(TypeVars, InstanceConstraints, Unconstrained),
|
|
list.length(InstanceConstraints, NumConstraints),
|
|
list.length(Unconstrained, NumUnconstrained),
|
|
NumExtra = NumConstraints + NumUnconstrained.
|
|
|
|
lookup_method_proc(MethodInfos, method_proc_num(MethodNum)) = MethodInfo :-
|
|
list.det_index1(MethodInfos, MethodNum, MethodInfo).
|
|
|
|
method_infos_to_pred_proc_ids(MethodInfos) = PredProcIds :-
|
|
acc_pred_proc_ids_for_methods(MethodInfos, cord.init, PredProcIdsCord),
|
|
PredProcIds = cord.list(PredProcIdsCord).
|
|
|
|
:- pred acc_pred_proc_ids_for_methods(list(method_info)::in,
|
|
cord(pred_proc_id)::in, cord(pred_proc_id)::out) is det.
|
|
|
|
acc_pred_proc_ids_for_methods([], !PredProcIdsCord).
|
|
acc_pred_proc_ids_for_methods([MethodInfo | MethodInfos], !PredProcIdsCord) :-
|
|
MethodPredProcId = MethodInfo ^ method_cur_proc,
|
|
cord.snoc(MethodPredProcId, !PredProcIdsCord),
|
|
acc_pred_proc_ids_for_methods(MethodInfos, !PredProcIdsCord).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- interface.
|
|
|
|
% Identifiers for constraints which are unique across a given type_assign.
|
|
% Integers in these values refer to the position in the list of constraints
|
|
% at that location, beginning from 1.
|
|
%
|
|
% Only identifiers for constraints appearing directly on a goal are needed
|
|
% at the moment, so there is no way to represent the appropriate identifier
|
|
% for the superclass of such a constraint.
|
|
%
|
|
% XXX A more robust and efficient solution would be to allocate
|
|
% unique integers to the constraints as they are encountered, and
|
|
% store the allocated integer in the relevant hlds_goal_expr.
|
|
%
|
|
:- type constraint_id
|
|
---> constraint_id(
|
|
% Assumed or unproven.
|
|
constraint_type,
|
|
|
|
% The id of the atomic goal which is constrained.
|
|
goal_id,
|
|
|
|
% The position of the constraint.
|
|
int
|
|
).
|
|
|
|
:- type constraint_type
|
|
---> unproven
|
|
; assumed.
|
|
|
|
% The identifier of a constraint is stored along with the constraint.
|
|
% Each value of this type may have more than one identifier because
|
|
% if two constraints in a context are equivalent, then we merge them
|
|
% together in order to not have to prove the same constraint twice.
|
|
%
|
|
:- type hlds_constraint
|
|
---> hlds_constraint(
|
|
list(constraint_id),
|
|
class_name,
|
|
list(mer_type)
|
|
).
|
|
|
|
:- type hlds_constraint_db
|
|
---> hlds_constraint_db(
|
|
% Unproven constraints. These are the constraints that we must
|
|
% prove (that is, universal constraints from the goal being
|
|
% checked, or existential constraints on the head).
|
|
hcd_unproven :: list(hlds_constraint),
|
|
|
|
% Assumed constraints. These are constraints we can use in
|
|
% proofs (that is, existential constraints from the goal being
|
|
% checked, or universal constraints on the head).
|
|
hcd_assumed :: list(hlds_constraint),
|
|
|
|
% Constraints that are known to be redundant. This includes
|
|
% constraints that have already been proved as well as
|
|
% constraints that are ancestors of other unproven, assumed
|
|
% or redundant constraints. Not all such constraints are
|
|
% included, only those which may be used for the purposes
|
|
% of improvement.
|
|
hcd_redundant :: redundant_constraints,
|
|
|
|
% Ancestors of assumed constraints.
|
|
hcd_ancestors :: ancestor_constraints
|
|
).
|
|
|
|
% We partition redundant constraints by class, which helps us
|
|
% process them more efficiently.
|
|
%
|
|
:- type redundant_constraints == map(class_id, set(hlds_constraint)).
|
|
|
|
% Constraints which are ancestors of assumed constraints, along with the
|
|
% list of constraints (following the class hierarchy) which leads to
|
|
% the assumed constraint. The assumed constraint is the last item in the
|
|
% list.
|
|
%
|
|
% Note that if there are two possible lists for the same constraint, we
|
|
% always keep the shorter one.
|
|
%
|
|
:- type ancestor_constraints == map(prog_constraint, list(prog_constraint)).
|
|
|
|
% During type checking we fill in a constraint_map which gives
|
|
% the constraint that corresponds to each identifier. This is used
|
|
% by the polymorphism translation to retrieve details of constraints.
|
|
%
|
|
:- type constraint_map == map(constraint_id, prog_constraint).
|
|
|
|
% `Proof' of why a constraint is redundant.
|
|
:- type constraint_proof
|
|
---> apply_instance(instance_id)
|
|
% Apply the instance decl with the given identifier. Note that
|
|
% we don't store the actual hlds_instance_defn for two reasons:
|
|
%
|
|
% - That would require storing a renamed version of the
|
|
% constraint_proofs for *every* use of an instance declaration.
|
|
% This wouldn't even get GCed for a long time, because it
|
|
% would be stored in the pred_info.
|
|
%
|
|
% - The superclass proofs stored in the hlds_instance_defn would
|
|
% need to store all the constraint_proofs for all its ancestors.
|
|
% This would require the class relation to be topologically
|
|
% sorted before checking the instance declarations.
|
|
|
|
; superclass(prog_constraint).
|
|
% The constraint is redundant because of the following class's
|
|
% superclass declaration.
|
|
|
|
% The constraint_proof_map is a map which for each type class constraint
|
|
% records how/why that constraint was satisfied. This information is used
|
|
% to determine how to construct the typeclass_info for that constraint.
|
|
%
|
|
:- type constraint_proof_map == map(prog_constraint, constraint_proof).
|
|
|
|
:- pred init_hlds_constraint_list(list(prog_constraint)::in,
|
|
list(hlds_constraint)::out) is det.
|
|
|
|
:- pred make_head_hlds_constraint_db(class_table::in, tvarset::in,
|
|
univ_exist_constraints::in, hlds_constraint_db::out) is det.
|
|
|
|
:- pred make_body_hlds_constraint_db(class_table::in, tvarset::in, goal_id::in,
|
|
univ_exist_constraints::in, hlds_constraint_db::out) is det.
|
|
|
|
% make_hlds_constraint_db(ClassTable, TVarSet, UnprovenConstraints,
|
|
% AssumedConstraints, Constraints):
|
|
%
|
|
% ClassTable is the class_table for the module. TVarSet is the tvarset
|
|
% for the predicate this class context is for. UnprovenConstraints is
|
|
% a list of constraints which will need to be proven (that is, universal
|
|
% constraints in the body or existential constraints in the head).
|
|
% AssumedConstraints is a list of constraints that may be used in proofs
|
|
% (that is, existential constraints in the body or universal constraints
|
|
% in the head).
|
|
%
|
|
:- pred make_hlds_constraint_db(class_table::in, tvarset::in,
|
|
list(hlds_constraint)::in, list(hlds_constraint)::in,
|
|
hlds_constraint_db::out) is det.
|
|
|
|
:- pred make_hlds_constraint_list(list(prog_constraint)::in,
|
|
constraint_type::in, goal_id::in, list(hlds_constraint)::out) is det.
|
|
|
|
:- pred merge_hlds_constraint_dbs(
|
|
hlds_constraint_db::in, hlds_constraint_db::in,
|
|
hlds_constraint_db::out) is det.
|
|
|
|
:- pred retrieve_univ_exist_constraints(hlds_constraint_db::in,
|
|
univ_exist_constraints::out) is det.
|
|
|
|
:- pred retrieve_prog_constraint_list(list(hlds_constraint)::in,
|
|
list(prog_constraint)::out) is det.
|
|
|
|
:- pred retrieve_prog_constraint(hlds_constraint::in, prog_constraint::out)
|
|
is det.
|
|
|
|
:- pred matching_constraints(hlds_constraint::in, hlds_constraint::in)
|
|
is semidet.
|
|
|
|
:- pred compare_hlds_constraints(hlds_constraint::in, hlds_constraint::in,
|
|
comparison_result::out) is det.
|
|
|
|
:- pred update_constraint_map(hlds_constraint::in, constraint_map::in,
|
|
constraint_map::out) is det.
|
|
|
|
:- pred update_redundant_constraints(class_table::in, tvarset::in,
|
|
list(hlds_constraint)::in,
|
|
redundant_constraints::in, redundant_constraints::out) is det.
|
|
|
|
:- pred lookup_hlds_constraint_list(constraint_map::in, constraint_type::in,
|
|
goal_id::in, int::in, list(prog_constraint)::out) is det.
|
|
|
|
:- pred search_hlds_constraint_list(constraint_map::in, constraint_type::in,
|
|
goal_id::in, int::in, list(prog_constraint)::out) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
init_hlds_constraint_list(ProgConstraints, Constraints) :-
|
|
list.map(init_hlds_constraint, ProgConstraints, Constraints).
|
|
|
|
:- pred init_hlds_constraint(prog_constraint::in, hlds_constraint::out) is det.
|
|
|
|
init_hlds_constraint(Constraint, HLDSConstraint) :-
|
|
Constraint = constraint(ClassName, ArgTypes),
|
|
HLDSConstraint = hlds_constraint([], ClassName, ArgTypes).
|
|
|
|
make_head_hlds_constraint_db(ClassTable, TVarSet, ProgConstraints,
|
|
ConstraintDb) :-
|
|
ProgConstraints =
|
|
univ_exist_constraints(UnivConstraints, ExistConstraints),
|
|
GoalId = goal_id_for_head_constraints,
|
|
make_hlds_constraint_list(UnivConstraints, assumed, GoalId,
|
|
AssumedConstraints),
|
|
make_hlds_constraint_list(ExistConstraints, unproven, GoalId,
|
|
UnprovenConstraints),
|
|
make_hlds_constraint_db(ClassTable, TVarSet, UnprovenConstraints,
|
|
AssumedConstraints, ConstraintDb).
|
|
|
|
make_body_hlds_constraint_db(ClassTable, TVarSet, GoalId, ProgConstraints,
|
|
ConstraintDb) :-
|
|
ProgConstraints =
|
|
univ_exist_constraints(UnivConstraints, ExistConstraints),
|
|
make_hlds_constraint_list(UnivConstraints, unproven, GoalId,
|
|
UnprovenConstraints),
|
|
make_hlds_constraint_list(ExistConstraints, assumed, GoalId,
|
|
AssumedConstraints),
|
|
make_hlds_constraint_db(ClassTable, TVarSet, UnprovenConstraints,
|
|
AssumedConstraints, ConstraintDb).
|
|
|
|
make_hlds_constraint_db(ClassTable, TVarSet, Unproven, Assumed,
|
|
ConstraintDb) :-
|
|
list.foldl(update_redundant_constraints_2(ClassTable, TVarSet),
|
|
Unproven, map.init, Redundant0),
|
|
list.foldl(update_redundant_constraints_2(ClassTable, TVarSet),
|
|
Assumed, Redundant0, Redundant),
|
|
list.foldl(update_ancestor_constraints(ClassTable, TVarSet),
|
|
Assumed, map.init, Ancestors),
|
|
ConstraintDb = hlds_constraint_db(Unproven, Assumed, Redundant, Ancestors).
|
|
|
|
make_hlds_constraint_list(ProgConstraints, ConstraintType, GoalId,
|
|
Constraints) :-
|
|
make_hlds_constraint_list_2(ProgConstraints, ConstraintType, GoalId,
|
|
1, Constraints).
|
|
|
|
:- pred make_hlds_constraint_list_2(list(prog_constraint)::in,
|
|
constraint_type::in, goal_id::in, int::in, list(hlds_constraint)::out)
|
|
is det.
|
|
|
|
make_hlds_constraint_list_2([], _, _, _, []).
|
|
make_hlds_constraint_list_2([ProgConstraint | ProgConstraints], ConstraintType,
|
|
GoalId, CurArgNum, [HLDSConstraint | HLDSConstraints]) :-
|
|
ProgConstraint = constraint(ClassName, ArgTypes),
|
|
Id = constraint_id(ConstraintType, GoalId, CurArgNum),
|
|
HLDSConstraint = hlds_constraint([Id], ClassName, ArgTypes),
|
|
make_hlds_constraint_list_2(ProgConstraints, ConstraintType,
|
|
GoalId, CurArgNum + 1, HLDSConstraints).
|
|
|
|
merge_hlds_constraint_dbs(ConstraintDbA, ConstraintDbB, ConstraintDb) :-
|
|
ConstraintDbA = hlds_constraint_db(UnprovenA, AssumedA,
|
|
RedundantA, AncestorsA),
|
|
ConstraintDbB = hlds_constraint_db(UnprovenB, AssumedB,
|
|
RedundantB, AncestorsB),
|
|
Unproven = UnprovenA ++ UnprovenB,
|
|
Assumed = AssumedA ++ AssumedB,
|
|
map.union(set.union, RedundantA, RedundantB, Redundant),
|
|
map.union(pick_shorter_list, AncestorsA, AncestorsB, Ancestors),
|
|
ConstraintDb = hlds_constraint_db(Unproven, Assumed, Redundant, Ancestors).
|
|
|
|
:- pred pick_shorter_list(list(T)::in, list(T)::in, list(T)::out) is det.
|
|
|
|
pick_shorter_list(As, Bs, Cs) :-
|
|
( if is_shorter(As, Bs) then
|
|
Cs = As
|
|
else
|
|
Cs = Bs
|
|
).
|
|
|
|
:- pred is_shorter(list(T)::in, list(T)::in) is semidet.
|
|
|
|
is_shorter([], _).
|
|
is_shorter([_ | As], [_ | Bs]) :-
|
|
is_shorter(As, Bs).
|
|
|
|
retrieve_univ_exist_constraints(ConstraintDb, UnivExistConstraints) :-
|
|
ConstraintDb = hlds_constraint_db(Unproven, Assumed, _, _),
|
|
retrieve_prog_constraint_list(Unproven, UnivProgConstraints),
|
|
retrieve_prog_constraint_list(Assumed, ExistProgConstraints),
|
|
UnivExistConstraints =
|
|
univ_exist_constraints(UnivProgConstraints, ExistProgConstraints).
|
|
|
|
retrieve_prog_constraint_list(Constraints, ProgConstraints) :-
|
|
list.map(retrieve_prog_constraint, Constraints, ProgConstraints).
|
|
|
|
retrieve_prog_constraint(Constraint, ProgConstraint) :-
|
|
Constraint = hlds_constraint(_Ids, ClassName, ArgTypes),
|
|
ProgConstraint = constraint(ClassName, ArgTypes).
|
|
|
|
matching_constraints(ConstraintA, ConstraintB) :-
|
|
ConstraintA = hlds_constraint(_IdsA, ClassName, ArgTypes),
|
|
ConstraintB = hlds_constraint(_IdsB, ClassName, ArgTypes).
|
|
|
|
compare_hlds_constraints(ConstraintA, ConstraintB, CmpRes) :-
|
|
ConstraintA = hlds_constraint(_IdsA, ClassNameA, ArgTypesA),
|
|
ConstraintB = hlds_constraint(_IdsB, ClassNameB, ArgTypesB),
|
|
compare(NameCmpRes, ClassNameA, ClassNameB),
|
|
(
|
|
NameCmpRes = (=),
|
|
compare(CmpRes, ArgTypesA, ArgTypesB)
|
|
;
|
|
( NameCmpRes = (<)
|
|
; NameCmpRes = (>)
|
|
),
|
|
CmpRes = NameCmpRes
|
|
).
|
|
|
|
update_constraint_map(HLDSConstraint, !ConstraintMap) :-
|
|
HLDSConstraint = hlds_constraint(Ids, ClassName, ArgTypes),
|
|
ProgConstraint = constraint(ClassName, ArgTypes),
|
|
list.foldl(update_constraint_map_2(ProgConstraint), Ids, !ConstraintMap).
|
|
|
|
:- pred update_constraint_map_2(prog_constraint::in, constraint_id::in,
|
|
constraint_map::in, constraint_map::out) is det.
|
|
|
|
update_constraint_map_2(ProgConstraint, ConstraintId, !ConstraintMap) :-
|
|
map.set(ConstraintId, ProgConstraint, !ConstraintMap).
|
|
|
|
update_redundant_constraints(ClassTable, TVarSet, Constraints, !Redundant) :-
|
|
list.foldl(update_redundant_constraints_2(ClassTable, TVarSet),
|
|
Constraints, !Redundant).
|
|
|
|
:- pred update_redundant_constraints_2(class_table::in, tvarset::in,
|
|
hlds_constraint::in, redundant_constraints::in,
|
|
redundant_constraints::out) is det.
|
|
|
|
update_redundant_constraints_2(ClassTable, TVarSet, Constraint, !Redundant) :-
|
|
Constraint = hlds_constraint(_Ids, ClassName, ArgTypes),
|
|
list.length(ArgTypes, Arity),
|
|
ClassId = class_id(ClassName, Arity),
|
|
map.lookup(ClassTable, ClassId, ClassDefn),
|
|
ClassAncestors0 = ClassDefn ^ classdefn_fundep_ancestors,
|
|
list.map(init_hlds_constraint, ClassAncestors0, ClassAncestors),
|
|
(
|
|
% Optimize the simple case.
|
|
ClassAncestors = []
|
|
;
|
|
ClassAncestors = [_ | _],
|
|
ClassTVarSet = ClassDefn ^ classdefn_tvarset,
|
|
ClassParams = ClassDefn ^ classdefn_vars,
|
|
|
|
% We can ignore the resulting tvarset, since any new variables
|
|
% will become bound when the arguments are bound. (This follows
|
|
% from the fact that constraints on class declarations can only use
|
|
% variables that appear in the head of the declaration.)
|
|
|
|
tvarset_merge_renaming(TVarSet, ClassTVarSet, _, Renaming),
|
|
apply_renaming_to_constraints(Renaming,
|
|
ClassAncestors, RenamedAncestors),
|
|
apply_renaming_to_tvars(Renaming, ClassParams, RenamedParams),
|
|
map.from_corresponding_lists(RenamedParams, ArgTypes, Subst),
|
|
apply_subst_to_constraints(Subst, RenamedAncestors, Ancestors),
|
|
list.foldl(add_redundant_constraint, Ancestors, !Redundant)
|
|
).
|
|
|
|
:- pred add_redundant_constraint(hlds_constraint::in,
|
|
redundant_constraints::in, redundant_constraints::out) is det.
|
|
|
|
add_redundant_constraint(Constraint, !Redundant) :-
|
|
Constraint = hlds_constraint(_Ids, ClassName, ArgTypes),
|
|
list.length(ArgTypes, Arity),
|
|
ClassId = class_id(ClassName, Arity),
|
|
( if map.search(!.Redundant, ClassId, Constraints0) then
|
|
set.insert(Constraint, Constraints0, Constraints),
|
|
map.det_update(ClassId, Constraints, !Redundant)
|
|
else
|
|
Constraints = set.make_singleton_set(Constraint),
|
|
map.det_insert(ClassId, Constraints, !Redundant)
|
|
).
|
|
|
|
lookup_hlds_constraint_list(ConstraintMap, ConstraintType, GoalId, Count,
|
|
Constraints) :-
|
|
( if
|
|
search_hlds_constraint_list_loop(ConstraintMap, ConstraintType, GoalId,
|
|
Count, [], ConstraintsPrime)
|
|
then
|
|
Constraints = ConstraintsPrime
|
|
else
|
|
unexpected($pred, "not found")
|
|
).
|
|
|
|
search_hlds_constraint_list(ConstraintMap, ConstraintType, GoalId, Count,
|
|
Constraints) :-
|
|
search_hlds_constraint_list_loop(ConstraintMap, ConstraintType, GoalId,
|
|
Count, [], Constraints).
|
|
|
|
:- pred search_hlds_constraint_list_loop(constraint_map::in,
|
|
constraint_type::in, goal_id::in, int::in,
|
|
list(prog_constraint)::in, list(prog_constraint)::out) is semidet.
|
|
|
|
search_hlds_constraint_list_loop(ConstraintMap, ConstraintType, GoalId, Count,
|
|
!Constraints) :-
|
|
( if Count = 0 then
|
|
true
|
|
else
|
|
ConstraintId = constraint_id(ConstraintType, GoalId, Count),
|
|
map.search(ConstraintMap, ConstraintId, Constraint),
|
|
!:Constraints = [Constraint | !.Constraints],
|
|
search_hlds_constraint_list_loop(ConstraintMap, ConstraintType,
|
|
GoalId, Count - 1, !Constraints)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Search the superclasses of the given constraint for a potential proof,
|
|
% and add it to the map if no better proof exists already.
|
|
%
|
|
:- pred update_ancestor_constraints(class_table::in, tvarset::in,
|
|
hlds_constraint::in, ancestor_constraints::in, ancestor_constraints::out)
|
|
is det.
|
|
|
|
update_ancestor_constraints(ClassTable, TVarSet, HLDSConstraint, !Ancestors) :-
|
|
retrieve_prog_constraint(HLDSConstraint, Constraint),
|
|
update_ancestor_constraints_2(ClassTable, TVarSet, [], Constraint,
|
|
!Ancestors).
|
|
|
|
:- pred update_ancestor_constraints_2(class_table::in, tvarset::in,
|
|
list(prog_constraint)::in, prog_constraint::in,
|
|
ancestor_constraints::in, ancestor_constraints::out) is det.
|
|
|
|
update_ancestor_constraints_2(ClassTable, TVarSet, Descendants0, Constraint,
|
|
!Ancestors) :-
|
|
Constraint = constraint(ClassName, ArgTypes),
|
|
list.length(ArgTypes, Arity),
|
|
ClassId = class_id(ClassName, Arity),
|
|
map.lookup(ClassTable, ClassId, ClassDefn),
|
|
|
|
% We can ignore the resulting tvarset, since any new variables
|
|
% will become bound when the arguments are bound. (This follows
|
|
% from the fact that constraints on class declarations can only use
|
|
% variables that appear in the head of the declaration.)
|
|
|
|
tvarset_merge_renaming(TVarSet, ClassDefn ^ classdefn_tvarset, _,
|
|
Renaming),
|
|
apply_renaming_to_prog_constraints(Renaming,
|
|
ClassDefn ^ classdefn_supers, RenamedSupers),
|
|
apply_renaming_to_tvars(Renaming, ClassDefn ^ classdefn_vars,
|
|
RenamedParams),
|
|
map.from_corresponding_lists(RenamedParams, ArgTypes, Subst),
|
|
apply_subst_to_prog_constraints(Subst, RenamedSupers, Supers),
|
|
|
|
Descendants = [Constraint | Descendants0],
|
|
list.foldl(update_ancestor_constraints_3(ClassTable, TVarSet, Descendants),
|
|
Supers, !Ancestors).
|
|
|
|
:- pred update_ancestor_constraints_3(class_table::in, tvarset::in,
|
|
list(prog_constraint)::in, prog_constraint::in,
|
|
ancestor_constraints::in, ancestor_constraints::out) is det.
|
|
|
|
update_ancestor_constraints_3(ClassTable, TVarSet, Descendants, Constraint,
|
|
!Ancestors) :-
|
|
( if
|
|
map.search(!.Ancestors, Constraint, OldDescendants),
|
|
is_shorter(OldDescendants, Descendants)
|
|
then
|
|
% We don't want to update the ancestors because we already have a
|
|
% better path. The same will apply for all superclasses, so we
|
|
% don't traverse any further.
|
|
true
|
|
else
|
|
map.set(Constraint, Descendants, !Ancestors),
|
|
update_ancestor_constraints_2(ClassTable, TVarSet, Descendants,
|
|
Constraint, !Ancestors)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module hlds.hlds_class.
|
|
%---------------------------------------------------------------------------%
|