mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-21 00:39:37 +00:00
Estimated hours taken: 240
Branches: main
Remove the assumption made by polymorphism.m that all type variables
appearing in class constraints also appear in the type being constrained.
This is a first step towards adding functional dependencies, since in the
presence of functional dependencies (or "improvement" in general) this
assumption no longer holds.
The assumption made by polymorphism manifests itself in the fact that
constraints on atomic goals are reconstructed by unifying the types of
formal parameters with the types of actual arguments, and then applying
the resulting substitution to the constraints. Any type variables in
constraints that don't appear in the formal parameters will therefore
remain unbound.
This change overcomes the assumption by building up a map from constraint
identifiers to constraints during typechecking, and then looking up this
map in order to reconstruct the constraint during the polymorphism
transformation.
To support this, the type 'class_constraint' has been removed and replaced
by two distinct types, 'prog_constraint' and 'hlds_constraint'. The former
is part of the parse tree and holds the same information as the old
class_constraint. The latter is part of the HLDS, and is used during
typechecking; in addition to the information in prog_constraints, it also
stores a set of identifiers that represent where the constraint came from.
These identifiers are used as the keys in the aforementioned map.
At this stage the constraint identifiers are only used by typechecking to
build the constraint map. Other passes use either prog_constraints or
hlds_constraints with an empty set of identifiers.
compiler/hlds_data.m:
Define the constraint_id type, which is used to uniquely identify
class constraints. A better scheme than this one has been suggested,
but that will be left to a later change. An XXX comment to that
effect has been added.
Define the hlds_constraint type, which is like prog_constraint but
it also includes a set of constraint_ids. Define a set of predicates
to initialise and manipulate these.
Define the constraint_map type here. Move the definition of
constraint_proof_map to here, where it more sensibly belongs.
Update the comments in hlds_instance_defn slightly, with information
that I found I needed to know when making this change.
compiler/hlds_pred.m:
Add a field to the pred_info to store the constraint_map.
Move the definition of constraint_proof_map from here.
compiler/hlds_out.m:
Print out a representation of the constraint map if it isn't empty.
compiler/type_util.m:
Change the predicates that used to operate on class_constraints so
that they now operate on hlds_constraints. The old versions of these
predicates have now moved to prog_util.
Add some utility predicates to manipulate constraint_maps.
Add a predicate to apply a variable renaming to constraint_proof_maps.
compiler/prog_data.m:
Rename class_constraint(s) to prog_constraint(s).
compiler/prog_util.m:
Provide a set of predicates for manipulating prog_constraints.
compiler/typecheck.m:
Ensure that goal_paths are filled in before the first iteration
of typechecking.
Pass the hlds_goal_info down through typecheck_goal_2 so that the
goal_path can be retrieved when needed to assign identifiers to
constraints. Thread the goal_path through to wherever it is needed.
Store hlds_constraints in the args_type_assign rather than
prog_constraints, so that the required information is available
when creating the new set of type_assigns. Do likewise for the
cons_type_info type. Don't pass the module_info through
make_pred_cons_info*, since it isn't used. Do pass the goal_path,
though, so that constraints in cons_type_infos can be given the
correct identifier.
Add a constraint_map field to the typecheck_info, initialised to empty.
When retrieving the final information from a typecheck_info, return
the resulting constraint_map, after applying any type bindings.
Ensure that any constraints that may not have been entered into the
constraint_map are put there now. Call the new predicate in type_util
to rename the constraint_proof_map, rather than doing it longhand
here.
Make the following changes to context reduction:
- Thread the constraint_map through, so that it can be updated
as constraints are eliminated.
- Instead of simply calling sort_and_remove_dups on the
set of constraints remaining after one iteration, merge the
constraints in such a way that the complete set of
constraint_ids is retained.
- Disregard the constraint_ids when deleting newly introduced
constraints that are equivalent to constraints that have
already been seen.
- Simplify the code of find_matching_instance_rule_2 by
moving the deterministic code out of the condition of the
if-then-else.
Move find_first_map into the library.
compiler/polymorphism.m:
Ensure that the goal_path is set when constructing lambda goals.
In process_call, look up the constraints in the constraint_map
using the goal_path as part of the key, rather than calculating
the constraints by applying the ParentToActual type substitution.
Rearrange this code so that it is divided into easier to understand
blocks.
Add a field to the poly_info to store the constraint_map, and
initialise it from the pred_info.
compiler/goal_path.m:
Fill slots in lambda_goals, since constraints inside these will
otherwise not be identified properly. The goal_paths inside here
do not entirely make sense, since there is no goal_path_step for
the lambda_goal itself. However, there is enough information
retained to distinguish these goal_paths from any other possible
goal_path, which is all that we require to identify constraints.
Add a warning not to fill in the goal slots between the typechecking
and polymorphism passes, since doing so could potentially render the
constraint_maps incorrect.
compiler/make_hlds.m:
Initialise the constraint_map to empty in pred_infos.
Move the code for updating the superclass_table into a separate
predicate. Initially this change was made because, in an earlier
version of the change, the superclass_table had some extra
information that needed to be filled in. That part of the change
is not needed in this diff, but the new predicate simplifies the
code a bit so I've left it there.
compiler/check_typeclass.m:
Convert the prog_constraints into hlds_constraints before passing
them to typecheck.reduce_context_by_rule_application. They are
assigned no identifiers, since these constraints are not required
to be put into the constraint map.
Change the name of the function get_constraint_id to
get_constraint_class_id, since it would now be ambiguous otherwise.
compiler/cse_detection.m:
Import parse_tree__prog_util, since that is where renamings of
prog_constraints are now defined.
compiler/higher_order.m:
Initialise pred_infos here with an empty constraint_map.
compiler/post_typecheck.m:
When binding type vars to void, apply the void substitution to the
constraint_map.
compiler/table_gen.m:
Pass the constraint_map when creating a new pred_info.
compiler/unused_args.m:
Create the pred_info with an empty constraint_map. The constraint_map
won't be used by this stage anyway.
compiler/*.m:
Update to use the new type names. Also update to use the existing
type synonyms typeclass_info_varmap and constraint_proof_map.
Change names of predicates and functions to use prog_constraint
instead of class_constraint, where applicable.
library/list.m:
Add find_first_map from typecheck. Also add find_first_map{2,3},
since at one stage during development I needed find_first_map3, and,
although it's not used in the current diff, there is little point
removing it now.
205 lines
7.3 KiB
Mathematica
205 lines
7.3 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1997-2005 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.
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% This module looks after goal paths, which associate each goal
|
|
% with its position in a procedure definition,
|
|
|
|
% Main author: zs.
|
|
|
|
:- module check_hlds__goal_path.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds__hlds_goal.
|
|
:- import_module hlds__hlds_module.
|
|
:- import_module hlds__hlds_pred.
|
|
|
|
:- import_module bool.
|
|
|
|
% IMPORTANT: the type constraint_id in hlds_data.m makes use of
|
|
% goal_paths to identify constraints between the typechecking pass
|
|
% and the polymorphism pass. For this reason, goal paths should not
|
|
% be recalculated anywhere between these two passes. See the XXX
|
|
% comment near the declaration of constraint_id.
|
|
|
|
:- pred goal_path__fill_slots(module_info::in, proc_info::in, proc_info::out)
|
|
is det.
|
|
|
|
% Fill in the goal_paths for goals in the clauses_info of the predicate.
|
|
% Clauses are given goal paths `disj(1)', ..., `disj(N)'. If the bool
|
|
% argument is true then the goal paths are stored in a form where any
|
|
% prefix consisting of `disj(_)', `neg', `exist(_)' and `ite_else'
|
|
% components is removed. This is used to optimise the constraint-based
|
|
% mode analysis where the instantiatedness of a variable at such a goal
|
|
% path is always equivalent to its instantiatedness at the parent goal
|
|
% path.
|
|
|
|
:- pred goal_path__fill_slots_in_clauses(module_info::in, bool::in,
|
|
pred_info::in, pred_info::out) is det.
|
|
|
|
:- pred goal_path__fill_slots_in_goal(hlds_goal::in, vartypes::in,
|
|
module_info::in, hlds_goal::out) is det.
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds__type_util.
|
|
:- import_module hlds__hlds_data.
|
|
:- import_module hlds__hlds_goal.
|
|
:- import_module parse_tree__prog_data.
|
|
|
|
:- import_module char.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module require.
|
|
:- import_module std_util.
|
|
|
|
:- type slot_info
|
|
---> slot_info(
|
|
vartypes :: vartypes,
|
|
module_info :: module_info,
|
|
omit_mode_equiv_prefix :: bool
|
|
).
|
|
|
|
goal_path__fill_slots(ModuleInfo, !Proc) :-
|
|
proc_info_goal(!.Proc, Goal0),
|
|
proc_info_vartypes(!.Proc, VarTypes),
|
|
goal_path__fill_slots_in_goal(Goal0, VarTypes, ModuleInfo, Goal),
|
|
proc_info_set_goal(Goal, !Proc).
|
|
|
|
goal_path__fill_slots_in_clauses(ModuleInfo, OmitModeEquivPrefix, !PredInfo) :-
|
|
pred_info_clauses_info(!.PredInfo, ClausesInfo0),
|
|
clauses_info_clauses(ClausesInfo0, Clauses0),
|
|
clauses_info_vartypes(ClausesInfo0, VarTypes),
|
|
SlotInfo = slot_info(VarTypes, ModuleInfo, OmitModeEquivPrefix),
|
|
list__map_foldl(fill_slots_in_clause(SlotInfo), Clauses0, Clauses,
|
|
1, _),
|
|
clauses_info_set_clauses(Clauses, ClausesInfo0, ClausesInfo),
|
|
pred_info_set_clauses_info(ClausesInfo, !PredInfo).
|
|
|
|
:- pred fill_slots_in_clause(slot_info::in, clause::in, clause::out,
|
|
int::in, int::out) is det.
|
|
|
|
fill_slots_in_clause(SlotInfo, Clause0, Clause, ClauseNum, ClauseNum + 1) :-
|
|
Clause0 = clause(ProcIds, Goal0, Lang, Context),
|
|
fill_goal_slots(Goal0, [disj(ClauseNum)], SlotInfo, Goal),
|
|
Clause = clause(ProcIds, Goal, Lang, Context).
|
|
|
|
goal_path__fill_slots_in_goal(Goal0, VarTypes, ModuleInfo, Goal) :-
|
|
SlotInfo = slot_info(VarTypes, ModuleInfo, no),
|
|
fill_goal_slots(Goal0, [], SlotInfo, Goal).
|
|
|
|
:- pred fill_goal_slots(hlds_goal::in, goal_path::in, slot_info::in,
|
|
hlds_goal::out) is det.
|
|
|
|
fill_goal_slots(Expr0 - Info0, Path0, SlotInfo, Expr - Info) :-
|
|
OmitModeEquivPrefix = SlotInfo ^ omit_mode_equiv_prefix,
|
|
(
|
|
OmitModeEquivPrefix = yes,
|
|
list__takewhile(mode_equiv_step, Path0, _, Path)
|
|
;
|
|
OmitModeEquivPrefix = no,
|
|
Path = Path0
|
|
),
|
|
goal_info_set_goal_path(Info0, Path, Info),
|
|
fill_expr_slots(Expr0, Info, Path0, SlotInfo, Expr).
|
|
|
|
:- pred mode_equiv_step(goal_path_step::in) is semidet.
|
|
|
|
mode_equiv_step(Step) :-
|
|
( Step = disj(_)
|
|
; Step = neg
|
|
; Step = scope(_)
|
|
; Step = ite_else
|
|
).
|
|
|
|
:- pred fill_expr_slots(hlds_goal_expr::in, hlds_goal_info::in, goal_path::in,
|
|
slot_info::in, hlds_goal_expr::out) is det.
|
|
|
|
fill_expr_slots(conj(Goals0), _, Path0, SlotInfo, conj(Goals)) :-
|
|
fill_conj_slots(Goals0, Path0, 0, SlotInfo, Goals).
|
|
fill_expr_slots(par_conj(Goals0), _, Path0, SlotInfo,
|
|
par_conj(Goals)) :-
|
|
fill_conj_slots(Goals0, Path0, 0, SlotInfo, Goals).
|
|
fill_expr_slots(disj(Goals0), _, Path0, SlotInfo, disj(Goals)) :-
|
|
fill_disj_slots(Goals0, Path0, 0, SlotInfo, Goals).
|
|
fill_expr_slots(switch(Var, B, Cases0), _, Path0, SlotInfo,
|
|
switch(Var, B, Cases)) :-
|
|
VarTypes = SlotInfo ^ vartypes,
|
|
ModuleInfo = SlotInfo ^ module_info,
|
|
map__lookup(VarTypes, Var, Type),
|
|
(
|
|
type_util__switch_type_num_functors(ModuleInfo, Type,
|
|
NumFunctors)
|
|
->
|
|
NumCases = NumFunctors
|
|
;
|
|
NumCases = -1
|
|
),
|
|
fill_switch_slots(Cases0, Path0, 0, NumCases, SlotInfo, Cases).
|
|
fill_expr_slots(not(Goal0), _, Path0, SlotInfo, not(Goal)) :-
|
|
fill_goal_slots(Goal0, [neg | Path0], SlotInfo, Goal).
|
|
fill_expr_slots(scope(Reason, Goal0), OuterInfo, Path0, SlotInfo,
|
|
scope(Reason, Goal)) :-
|
|
Goal0 = _ - InnerInfo,
|
|
goal_info_get_determinism(OuterInfo, OuterDetism),
|
|
goal_info_get_determinism(InnerInfo, InnerDetism),
|
|
( InnerDetism = OuterDetism ->
|
|
MaybeCut = no_cut
|
|
;
|
|
MaybeCut = cut
|
|
),
|
|
fill_goal_slots(Goal0, [scope(MaybeCut) | Path0], SlotInfo, Goal).
|
|
fill_expr_slots(if_then_else(A, Cond0, Then0, Else0), _, Path0, SlotInfo,
|
|
if_then_else(A, Cond, Then, Else)) :-
|
|
fill_goal_slots(Cond0, [ite_cond | Path0], SlotInfo, Cond),
|
|
fill_goal_slots(Then0, [ite_then | Path0], SlotInfo, Then),
|
|
fill_goal_slots(Else0, [ite_else | Path0], SlotInfo, Else).
|
|
fill_expr_slots(unify(LHS, RHS0, Mode, Kind, Context), _, Path0, SlotInfo,
|
|
unify(LHS, RHS, Mode, Kind, Context)) :-
|
|
(
|
|
RHS0 = lambda_goal(A, B, C, D, E, F, G, H, LambdaGoal0)
|
|
->
|
|
fill_goal_slots(LambdaGoal0, Path0, SlotInfo, LambdaGoal),
|
|
RHS = lambda_goal(A, B, C, D, E, F, G, H, LambdaGoal)
|
|
;
|
|
RHS = RHS0
|
|
).
|
|
fill_expr_slots(Goal @ call(_, _, _, _, _, _), _, _, _, Goal).
|
|
fill_expr_slots(Goal @ generic_call(_, _, _, _), _, _, _, Goal).
|
|
fill_expr_slots(Goal @ foreign_proc(_, _, _, _, _, _), _, _, _, Goal).
|
|
fill_expr_slots(shorthand(_), _, _, _, _) :-
|
|
% these should have been expanded out by now
|
|
error("fill_expr_slots: unexpected shorthand").
|
|
|
|
:- pred fill_conj_slots(list(hlds_goal)::in, goal_path::in, int::in,
|
|
slot_info::in, list(hlds_goal)::out) is det.
|
|
|
|
fill_conj_slots([], _, _, _, []).
|
|
fill_conj_slots([Goal0 | Goals0], Path0, N0, SlotInfo, [Goal | Goals]) :-
|
|
N1 = N0 + 1,
|
|
fill_goal_slots(Goal0, [conj(N1) | Path0], SlotInfo, Goal),
|
|
fill_conj_slots(Goals0, Path0, N1, SlotInfo, Goals).
|
|
|
|
:- pred fill_disj_slots(list(hlds_goal)::in, goal_path::in, int::in,
|
|
slot_info::in, list(hlds_goal)::out) is det.
|
|
|
|
fill_disj_slots([], _, _, _, []).
|
|
fill_disj_slots([Goal0 | Goals0], Path0, N0, SlotInfo, [Goal | Goals]) :-
|
|
N1 = N0 + 1,
|
|
fill_goal_slots(Goal0, [disj(N1) | Path0], SlotInfo, Goal),
|
|
fill_disj_slots(Goals0, Path0, N1, SlotInfo, Goals).
|
|
|
|
:- pred fill_switch_slots(list(case)::in, goal_path::in, int::in, int::in,
|
|
slot_info::in, list(case)::out) is det.
|
|
|
|
fill_switch_slots([], _, _, _, _, []).
|
|
fill_switch_slots([case(A, Goal0) | Cases0], Path0, N0, NumCases, SlotInfo,
|
|
[case(A, Goal) | Cases]) :-
|
|
N1 = N0 + 1,
|
|
fill_goal_slots(Goal0, [switch(N1, NumCases) | Path0], SlotInfo, Goal),
|
|
fill_switch_slots(Cases0, Path0, N1, NumCases, SlotInfo, Cases).
|