mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-20 20:03:44 +00:00
Estimated hours taken: 220
Aditi update syntax, type and mode checking.
Change the hlds_goal for constructions in preparation for
structure reuse to avoid making multiple conflicting changes.
compiler/hlds_goal.m:
Merge `higher_order_call' and `class_method_call' into a single
`generic_call' goal type. This also has alternatives for the
various Aditi builtins for which type declarations can't
be written.
Remove the argument types field from higher-order/class method calls.
It wasn't used often, and wasn't updated by optimizations
such as inlining. The types can be obtained from the vartypes
field of the proc_info.
Add a `lambda_eval_method' field to lambda_goals.
Add a field to constructions to identify which RL code fragment should
be used for an top-down Aditi closure.
Add fields to constructions to hold structure reuse information.
This is currently ignored -- the changes to implement structure
reuse will be committed to the alias branch.
This is included here to avoid lots of CVS conflicts caused by
changing the definition of `hlds_goal' twice.
Add a field to `some' goals to specify whether the quantification
can be removed. This is used to make it easier to ensure that
indexes are used for updates.
Add a field to lambda_goals to describe whether the modes were
guessed by the compiler and may need fixing up after typechecking
works out the argument types.
Add predicate `hlds_goal__generic_call_id' to work out a call_id
for a generic call for use in error messages.
compiler/purity.m:
compiler/post_typecheck.m:
Fill in the modes of Aditi builtin calls and closure constructions.
This needs to know which are the `aditi__state' arguments, so
it must be done after typechecking.
compiler/prog_data.m:
Added `:- type sym_name_and_arity ---> sym_name/arity'.
Add a type `lambda_eval_method', which describes how a closure
is to be executed. The alternatives are normal Mercury execution,
bottom-up execution by Aditi and top-down execution by Aditi.
compiler/prog_out.m:
Add predicate `prog_out__write_sym_name_and_arity', which
replaces duplicated inline code in a few places.
compiler/hlds_data.m:
Add a `lambda_eval_method' field to `pred_const' cons_ids and
`pred_closure_tag' cons_tags.
compiler/hlds_pred.m:
Remove type `pred_call_id', replace it with type `simple_call_id',
which combines a `pred_or_func' and a `sym_name_and_arity'.
Add a type `call_id' which describes all the different types of call,
including normal calls, higher-order and class-method calls
and Aditi builtins.
Add `aditi_top_down' to the type `marker'.
Remove `aditi_interface' from type `marker'. Interfacing to
Aditi predicates is now handled by `generic_call' hlds_goals.
Add a type `rl_exprn_id' which identifies a predicate to
be executed top-down by Aditi.
Add a `maybe(rl_exprn_id)' field to type `proc_info'.
Add predicate `adjust_func_arity' to convert between the arity
of a function to its arity as a predicate.
Add predicates `get_state_args' and `get_state_args_det' to
extract the DCG state arguments from an argument list.
Add predicate `pred_info_get_call_id' to get a `simple_call_id'
for a predicate for use in error messages.
compiler/hlds_out.m:
Write the new representation for call_ids.
Add a predicate `hlds_out__write_call_arg_id' which
replaces similar code in mode_errors.m and typecheck.m.
compiler/prog_io_goal.m:
Add support for `aditi_bottom_up' and `aditi_top_down' annotations
on pred expressions.
compiler/prog_io_util.m:
compiler/prog_io_pragma.m:
Add predicates
- `prog_io_util:parse_name_and_arity' to parse `SymName/Arity'
(moved from prog_io_pragma.m).
- `prog_io_util:parse_pred_or_func_name_and_arity to parse
`pred SymName/Arity' or `func SymName/Arity'.
- `prog_io_util:parse_pred_or_func_and_args' to parse terms resembling
a clause head (moved from prog_io_pragma.m).
compiler/type_util.m:
Add support for `aditi_bottom_up' and `aditi_top_down' annotations
on higher-order types.
Add predicates `construct_higher_order_type',
`construct_higher_order_pred_type' and
`construct_higher_order_func_type' to avoid some code duplication.
compiler/mode_util.m:
Add predicate `unused_mode/1', which returns `builtin:unused'.
Add functions `aditi_di_mode/0', `aditi_ui_mode/0' and
`aditi_uo_mode/0' which return `in', `in', and `out', but will
be changed to return `di', `ui' and `uo' when alias tracking
is implemented.
compiler/goal_util.m:
Add predicate `goal_util__generic_call_vars' which returns
any arguments to a generic_call which are not in the argument list,
for example the closure passed to a higher-order call or
the typeclass_info for a class method call.
compiler/llds.m:
compiler/exprn_aux.m:
compiler/dupelim.m:
compiler/llds_out.m:
compiler/opt_debug.m:
Add builtin labels for the Aditi update operations.
compiler/hlds_module.m:
Add predicate predicate_table_search_pf_sym, used for finding
possible matches for a call with the wrong number of arguments.
compiler/intermod.m:
Don't write predicates which build `aditi_top_down' goals,
because there is currently no way to tell importing modules
which RL code fragment to use.
compiler/simplify.m:
Obey the `cannot_remove' field of explicit quantification goals.
compiler/make_hlds.m:
Parse Aditi updates.
Don't typecheck clauses for which syntax errors in Aditi updates
are found - this avoids spurious "undefined predicate `aditi_insert/3'"
errors.
Factor out some common code to handle terms of the form `Head :- Body'.
Factor out common code in the handling of pred and func expressions.
compiler/typecheck.m:
Typecheck Aditi builtins.
Allow the argument types of matching predicates to be adjusted
when typechecking the higher-order arguments of Aditi builtins.
Change `typecheck__resolve_pred_overloading' to take a list of
argument types rather than a `map(var, type)' and a list of
arguments to allow a transformation to be performed on the
argument types before passing them.
compiler/error_util.m:
Move the part of `report_error_num_args' which writes
"wrong number of arguments (<x>; expected <y>)" from
typecheck.m for use by make_hlds.m when reporting errors
for Aditi builtins.
compiler/modes.m:
compiler/unique_modes.m:
compiler/modecheck_call.m:
Modecheck Aditi builtins.
compiler/lambda.m:
Handle the markers for predicates introduced for
`aditi_top_down' and `aditi_bottom_up' lambda expressions.
compiler/polymorphism.m:
Add extra type_infos to `aditi_insert' calls
describing the tuple to insert.
compiler/call_gen.m:
Generate code for Aditi builtins.
compiler/unify_gen.m:
compiler/bytecode_gen.m:
Abort on `aditi_top_down' and `aditi_bottom_up' lambda
expressions - code generation for them is not yet implemented.
compiler/magic.m:
Use the `aditi_call' generic_call rather than create
a new procedure for each Aditi predicate called from C.
compiler/rl_out.pp:
compiler/rl_gen.m:
compiler/rl.m:
Move some utility code used by magic.m and call_gen.m into rl.m.
Remove an XXX comment about reference counting being not yet
implemented - Evan has fixed that.
library/ops.m:
compiler/mercury_to_mercury.m:
doc/transition_guide.texi:
Add unary prefix operators `aditi_bottom_up' and `aditi_top_down',
used as qualifiers on lambda expressions.
Add infix operator `==>' to separate the tuples in an
`aditi_modify' call.
compiler/follow_vars.m:
Thread a `map(prog_var, type)' through, needed because
type information is no longer held in higher-order call goals.
compiler/table_gen.m:
Use the `make_*_construction' predicates in hlds_goal.m
to construct constants.
compiler/*.m:
Trivial changes to add extra fields to hlds_goal structures.
doc/reference_manual.texi:
Document Aditi updates.
Use @samp{pragma base_relation} instead of
@samp{:- pragma base_relation} throughout the Aditi documentation
to be consistent with other parts of the reference manual.
tests/valid/Mmakefile:
tests/valid/aditi_update.m:
tests/valid/aditi.m:
Test case.
tests/valid/Mmakefile:
Remove some hard-coded --intermodule-optimization rules which are
no longer needed because `mmake depend' is now run in this directory.
tests/invalid/*.err_exp:
Fix expected output for changes in reporting of call_ids
in typecheck.m.
tests/invalid/Mmakefile
tests/invalid/aditi_update_errors.{m,err_exp}:
tests/invalid/aditi_update_mode_errors.{m,err_exp}:
Test error messages for Aditi updates.
tests/valid/aditi.m:
tests/invalid/aditi.m:
Cut down version of extras/aditi/aditi.m to provide basic declarations
for Aditi compilation such as `aditi__state' and the modes
`aditi_di', `aditi_uo' and `aditi_ui'. Installing extras/aditi/aditi.m
somewhere would remove the need for these.
1017 lines
35 KiB
Mathematica
1017 lines
35 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1998-1999 University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
% File: rl_key.m
|
|
% Main author: stayl
|
|
%
|
|
% Extract key ranges from join and select conditions.
|
|
% B-tree operations work by specifying a key range (upper and lower bound)
|
|
% on which the operation is to be applied. The B-tree structure allows
|
|
% the system to efficiently find the lower bound of the key range then
|
|
% traverse the relation in sorted order to the upper bound.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
:- module rl_key.
|
|
|
|
:- interface.
|
|
|
|
:- import_module hlds_goal, hlds_module, prog_data, rl.
|
|
:- import_module list, map.
|
|
|
|
% Work out the upper and lower bounds for the inputs to an
|
|
% goal which could result in the goal succeeding.
|
|
:- pred rl_key__extract_indexing(rl_goal_inputs::in,
|
|
list(hlds_goal)::in, module_info::in, map(prog_var, type)::in,
|
|
list(rl_var_bounds)::out) is det.
|
|
|
|
% Given an index specifier, work out whether the bounds of the
|
|
% arguments give useful key ranges for that index. If so, return
|
|
% the list of key ranges.
|
|
:- pred rl_key__get_select_key_ranges(module_info::in, map(prog_var, type)::in,
|
|
list(prog_var)::in, index_spec::in, list(rl_var_bounds)::in,
|
|
list(key_range)::out) is semidet.
|
|
|
|
% As above except for joins. The first list of arguments is
|
|
% for the non-indexed relation. The values of these arguments
|
|
% are used to construct the key_ranges for the indexed second
|
|
% relation.
|
|
:- pred rl_key__get_join_key_ranges(module_info::in, map(prog_var, type)::in,
|
|
list(prog_var)::in, list(prog_var)::in, index_spec::in,
|
|
list(rl_var_bounds)::in, list(key_range)::out) is semidet.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds_data, hlds_pred, prog_util, type_util.
|
|
:- import_module assoc_list, bool, int, require, set, std_util.
|
|
|
|
rl_key__extract_indexing(no_inputs, _, _, _, []).
|
|
rl_key__extract_indexing(one_input(Args), Goals, ModuleInfo, VarTypes,
|
|
ArgBounds) :-
|
|
rl_key__compute_var_bound_maps(Goals, ModuleInfo, VarTypes, VarMaps),
|
|
list__filter_map(
|
|
rl_key__compute_var_bounds(ModuleInfo, Args),
|
|
VarMaps, ArgBounds).
|
|
rl_key__extract_indexing(two_inputs(Args1, Args2), Goals, ModuleInfo, VarTypes,
|
|
ArgBounds) :-
|
|
list__append(Args1, Args2, Args),
|
|
rl_key__compute_var_bound_maps(Goals, ModuleInfo, VarTypes, VarMaps),
|
|
list__filter_map(
|
|
rl_key__compute_var_bounds(ModuleInfo, Args),
|
|
VarMaps, ArgBounds).
|
|
|
|
:- pred rl_key__compute_var_bound_maps(list(hlds_goal)::in, module_info::in,
|
|
map(prog_var, type)::in, list(var_map)::out) is det.
|
|
|
|
rl_key__compute_var_bound_maps(Goals, ModuleInfo, VarTypes, Cnstrs) :-
|
|
map__init(VarMap),
|
|
map__init(Compares),
|
|
KeyInfo0 = key_info(ModuleInfo, VarTypes, [var_map(VarMap, Compares)]),
|
|
list__foldl(rl_key__extract_key_range, Goals, KeyInfo0, KeyInfo),
|
|
key_info_get_constraints(Cnstrs, KeyInfo, _).
|
|
|
|
:- pred rl_key__compute_var_bounds(module_info::in, list(prog_var)::in,
|
|
var_map::in, rl_var_bounds::out) is semidet.
|
|
|
|
rl_key__compute_var_bounds(ModuleInfo, Args, VarMap, ArgBounds) :-
|
|
list__map(rl_key__get_var_bounds(ModuleInfo, VarMap),
|
|
Args, ArgBounds0),
|
|
list__filter_map(rl_key__useful_bounds(ModuleInfo),
|
|
ArgBounds0, ArgBounds1),
|
|
ArgBounds1 \= [],
|
|
map__from_assoc_list(ArgBounds1, ArgBounds).
|
|
|
|
:- pred rl_key__useful_bounds(module_info::in,
|
|
pair(prog_var, pair(key_term))::in,
|
|
pair(prog_var, pair(key_term))::out) is semidet.
|
|
|
|
rl_key__useful_bounds(ModuleInfo, Var - (LowerBound0 - UpperBound0),
|
|
Var - (LowerBound - UpperBound)) :-
|
|
rl_key__remove_useless_info(ModuleInfo, LowerBound0, LowerBound),
|
|
rl_key__remove_useless_info(ModuleInfo, UpperBound0, UpperBound),
|
|
( rl_key__useful_var_bounds(Var, LowerBound)
|
|
; rl_key__useful_var_bounds(Var, UpperBound)
|
|
).
|
|
|
|
% Convert functor(ConsId, Type, [unknown, ...]) into unknown if
|
|
% ConsId is the only functor in Type.
|
|
:- pred rl_key__remove_useless_info(module_info::in, key_term::in,
|
|
key_term::out) is det.
|
|
|
|
rl_key__remove_useless_info(_ModuleInfo, var - Vars, var - Vars).
|
|
rl_key__remove_useless_info(ModuleInfo,
|
|
functor(ConsId, Type, ArgBounds0) - Vars,
|
|
Bound) :-
|
|
list__map(rl_key__remove_useless_info(ModuleInfo),
|
|
ArgBounds0, ArgBounds),
|
|
(
|
|
\+ (
|
|
list__member(ArgBound, ArgBounds),
|
|
ArgBound \= var - _
|
|
),
|
|
classify_type(Type, ModuleInfo, TypeClass),
|
|
( TypeClass = user_type
|
|
; TypeClass = enum_type
|
|
),
|
|
module_info_types(ModuleInfo, Types),
|
|
type_to_type_id(Type, TypeId, _),
|
|
map__lookup(Types, TypeId, TypeDefn),
|
|
hlds_data__get_type_defn_body(TypeDefn, Body),
|
|
Body = du_type(Ctors, _, _, _),
|
|
Ctors = [_]
|
|
->
|
|
Bound = var - Vars
|
|
;
|
|
Bound = functor(ConsId, Type, ArgBounds) - Vars
|
|
).
|
|
|
|
:- pred rl_key__useful_var_bounds(prog_var::in, key_term::in) is semidet.
|
|
|
|
rl_key__useful_var_bounds(_, functor(_, _, _) - _).
|
|
rl_key__useful_var_bounds(Var, var - Vars0) :-
|
|
set__delete(Vars0, Var, Vars),
|
|
\+ set__empty(Vars).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
rl_key__get_select_key_ranges(ModuleInfo, VarTypes, Args, Index,
|
|
VarBoundsLists, KeyRanges) :-
|
|
list__map(rl_key__bounds_to_key_range(no, Args, VarTypes, Index),
|
|
VarBoundsLists, KeyRanges0),
|
|
rl_key__merge_key_ranges(ModuleInfo, KeyRanges0, [], KeyRanges).
|
|
|
|
rl_key__get_join_key_ranges(ModuleInfo, VarTypes, Args1, Args2, Index,
|
|
VarBoundsLists, KeyRanges) :-
|
|
list__map(rl_key__bounds_to_key_range(yes(Args1), Args2,
|
|
VarTypes, Index), VarBoundsLists, KeyRanges0),
|
|
rl_key__merge_key_ranges(ModuleInfo, KeyRanges0, [], KeyRanges).
|
|
|
|
:- pred rl_key__bounds_to_key_range(maybe(list(prog_var))::in,
|
|
list(prog_var)::in, map(prog_var, type)::in, index_spec::in,
|
|
map(prog_var, pair(key_term))::in, key_range::out) is semidet.
|
|
|
|
rl_key__bounds_to_key_range(MaybeConstructArgs, Args, VarTypes,
|
|
index_spec(_, Attrs), VarBoundMap, KeyRange) :-
|
|
( MaybeConstructArgs = yes(ConstructArgs) ->
|
|
map__apply_to_list(ConstructArgs, VarTypes, ConstructArgTypes),
|
|
MaybeConstructArgTypes = yes(ConstructArgTypes)
|
|
;
|
|
MaybeConstructArgTypes = no
|
|
),
|
|
% Partial matches on indexes aren't yet allowed.
|
|
list__map(lambda([Attr::in, (Attr - AttrBound)::out] is semidet, (
|
|
list__index0(Args, Attr, KeyArg),
|
|
map__search(VarBoundMap, KeyArg, AttrBound)
|
|
)), Attrs, AttrBounds),
|
|
|
|
list__map(list__index0(Args), Attrs, KeyArgs),
|
|
map__apply_to_list(KeyArgs, VarTypes, ArgTypes),
|
|
|
|
rl_key__split_key_tuples(AttrBounds, ArgTypes,
|
|
LowerTuple, UpperTuple, Types),
|
|
rl_key__convert_bound(MaybeConstructArgs,
|
|
LowerTuple, LowerBound),
|
|
rl_key__convert_bound(MaybeConstructArgs,
|
|
UpperTuple, UpperBound),
|
|
\+ (LowerBound = infinity, UpperBound = infinity),
|
|
KeyRange = key_range(LowerBound, UpperBound,
|
|
MaybeConstructArgTypes, Types).
|
|
|
|
:- pred rl_key__convert_bound(maybe(list(prog_var))::in,
|
|
assoc_list(int, key_term)::in, bounding_tuple::out) is det.
|
|
|
|
rl_key__convert_bound(MaybeArgs, Tuple, Bound) :-
|
|
(
|
|
% XXX this is temporary, until Aditi supports infinities
|
|
% within bounds tuples.
|
|
(
|
|
list__member(Attr, Tuple),
|
|
(
|
|
MaybeArgs = no,
|
|
Attr = _ - (var - _)
|
|
;
|
|
MaybeArgs = yes(Args),
|
|
Attr = _ - (var - Vars),
|
|
\+ (
|
|
set__member(Var, Vars),
|
|
list__member(Var, Args)
|
|
)
|
|
)
|
|
)
|
|
->
|
|
Bound = infinity
|
|
;
|
|
list__member(Attr, Tuple),
|
|
\+ (
|
|
MaybeArgs = yes(Args),
|
|
Attr = _ - (var - Vars),
|
|
set__member(Var, Vars),
|
|
list__member(Var, Args)
|
|
)
|
|
->
|
|
assoc_list__keys(Tuple, Indexes),
|
|
assoc_list__values(Tuple, Terms),
|
|
list__map(rl_key__convert_key_attr(MaybeArgs), Terms, Attrs),
|
|
assoc_list__from_corresponding_lists(Indexes, Attrs, KeyTuple),
|
|
Bound = bound(KeyTuple)
|
|
;
|
|
Bound = infinity
|
|
).
|
|
|
|
:- pred rl_key__convert_key_attr(maybe(list(prog_var))::in,
|
|
key_term::in, key_attr::out) is det.
|
|
|
|
rl_key__convert_key_attr(MaybeArgs, var - Vars, Attr) :-
|
|
(
|
|
MaybeArgs = yes(Args),
|
|
set__list_to_set(Args, ArgSet),
|
|
set__intersect(ArgSet, Vars, Intersection),
|
|
set__to_sorted_list(Intersection, [Arg | _]),
|
|
list__nth_member_search(Args, Arg, Index0)
|
|
->
|
|
Index is Index0 - 1,
|
|
Attr = input_field(Index)
|
|
;
|
|
Attr = infinity
|
|
).
|
|
rl_key__convert_key_attr(MaybeArgs, functor(ConsId, Type, Terms) - _,
|
|
functor(ConsId, Type, Attrs)) :-
|
|
list__map(rl_key__convert_key_attr(MaybeArgs), Terms, Attrs).
|
|
|
|
:- pred rl_key__split_key_tuples(assoc_list(int, pair(key_term))::in,
|
|
list(type)::in, assoc_list(int, key_term)::out,
|
|
assoc_list(int, key_term)::out, list(type)::out) is det.
|
|
|
|
rl_key__split_key_tuples([], _, [], [], []).
|
|
rl_key__split_key_tuples([Index - (Lower - Upper) | Tuples0], IndexArgTypes,
|
|
[Index - Lower | Lowers], [Index - Upper | Uppers],
|
|
[Type | Types]) :-
|
|
list__index1_det(IndexArgTypes, Index, Type),
|
|
rl_key__split_key_tuples(Tuples0, IndexArgTypes,
|
|
Lowers, Uppers, Types).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Merge overlapping key ranges. For joins, this really needs
|
|
% to be done at runtime, since the key ranges depend on the tuples
|
|
% of the non-indexed relation.
|
|
:- pred rl_key__merge_key_ranges(module_info::in,
|
|
list(key_range)::in, list(key_range)::in,
|
|
list(key_range)::out) is det.
|
|
|
|
rl_key__merge_key_ranges(_, [], Ranges, Ranges).
|
|
rl_key__merge_key_ranges(ModuleInfo, [Range | Ranges0],
|
|
NeededRanges0, NeededRanges) :-
|
|
rl_key__merge_key_ranges_2(ModuleInfo, Range,
|
|
Ranges0, Ranges2, RangeNeeded),
|
|
( RangeNeeded = yes ->
|
|
NeededRanges1 = [Range | NeededRanges0]
|
|
;
|
|
NeededRanges1 = NeededRanges0
|
|
),
|
|
rl_key__merge_key_ranges(ModuleInfo,
|
|
Ranges2, NeededRanges1, NeededRanges).
|
|
|
|
:- pred rl_key__merge_key_ranges_2(module_info::in, key_range::in,
|
|
list(key_range)::in, list(key_range)::out, bool::out) is det.
|
|
|
|
rl_key__merge_key_ranges_2(_, _, [], [], yes).
|
|
rl_key__merge_key_ranges_2(ModuleInfo, Range1, [Range2 | Ranges0],
|
|
Ranges, IsNeeded) :-
|
|
Range1 = key_range(Lower1, Upper1, Attrs, Schema),
|
|
Range2 = key_range(Lower2, Upper2, _, _),
|
|
(
|
|
(
|
|
bounding_tuple_less_or_equal(ModuleInfo,
|
|
lower, Lower2, upper, Upper1),
|
|
bounding_tuple_less_or_equal(ModuleInfo,
|
|
lower, Lower1, upper, Upper2)
|
|
;
|
|
bounding_tuple_less_or_equal(ModuleInfo,
|
|
lower, Lower1, upper, Upper2),
|
|
bounding_tuple_less_or_equal(ModuleInfo,
|
|
lower, Lower2, upper, Upper1)
|
|
)
|
|
->
|
|
rl_key__min_max(ModuleInfo, lower, Lower1,
|
|
lower, Lower2, Lower, _),
|
|
rl_key__min_max(ModuleInfo, upper, Upper1,
|
|
upper, Upper2, _, Upper),
|
|
Range = key_range(Lower, Upper, Attrs, Schema),
|
|
Ranges = [Range | Ranges0],
|
|
IsNeeded = no
|
|
;
|
|
rl_key__merge_key_ranges_2(ModuleInfo, Range1, Ranges0,
|
|
Ranges1, IsNeeded),
|
|
Ranges = [Range2 | Ranges1]
|
|
).
|
|
|
|
:- pred rl_key__min_max(module_info::in, upper_lower::in,
|
|
bounding_tuple::in, upper_lower::in, bounding_tuple::in,
|
|
bounding_tuple::out, bounding_tuple::out) is det.
|
|
|
|
rl_key__min_max(ModuleInfo, UpperLower1, Tuple1,
|
|
UpperLower2, Tuple2, Min, Max) :-
|
|
(
|
|
bounding_tuple_less_or_equal(ModuleInfo,
|
|
UpperLower1, Tuple1, UpperLower2, Tuple2)
|
|
->
|
|
Min = Tuple1,
|
|
Max = Tuple2
|
|
;
|
|
Min = Tuple2,
|
|
Max = Tuple1
|
|
).
|
|
|
|
:- pred bounding_tuple_less_or_equal(module_info::in, upper_lower::in,
|
|
bounding_tuple::in, upper_lower::in, bounding_tuple::in) is semidet.
|
|
|
|
bounding_tuple_less_or_equal(_ModuleInfo, lower, infinity, _, _).
|
|
bounding_tuple_less_or_equal(_ModuleInfo, _, _, upper, infinity).
|
|
bounding_tuple_less_or_equal(ModuleInfo, UpperLower1, bound(Bound1),
|
|
UpperLower2, bound(Bound2)) :-
|
|
key_tuple_less_or_equal(ModuleInfo, UpperLower1, Bound1,
|
|
UpperLower2, Bound2).
|
|
|
|
:- pred key_tuple_less_or_equal(module_info::in, upper_lower::in,
|
|
assoc_list(int, key_attr)::in, upper_lower::in,
|
|
assoc_list(int, key_attr)::in) is semidet.
|
|
|
|
key_tuple_less_or_equal(_ModuleInfo, _UpperLower, [], _, []).
|
|
key_tuple_less_or_equal(ModuleInfo, UpperLower1, [Index1 - Term1 | Tuple1],
|
|
UpperLower2, [Index2 - Term2 | Tuple2]) :-
|
|
Index1 = Index2,
|
|
key_term_less_or_equal(ModuleInfo, UpperLower1, Term1,
|
|
UpperLower2, Term2),
|
|
key_tuple_less_or_equal(ModuleInfo, UpperLower1, Tuple1,
|
|
UpperLower2, Tuple2).
|
|
|
|
:- pred key_term_less_or_equal(module_info::in, upper_lower::in,
|
|
key_attr::in, upper_lower::in, key_attr::in) is semidet.
|
|
|
|
key_term_less_or_equal(_ModuleInfo, _, functor(_, _, _), upper, infinity).
|
|
key_term_less_or_equal(_ModuleInfo, lower, infinity, _, functor(_, _, _)).
|
|
key_term_less_or_equal(ModuleInfo, UpperLower1, functor(ConsId1, _, Args1),
|
|
UpperLower2, functor(ConsId2, Type, Args2)) :-
|
|
( ConsId1 = ConsId2 ->
|
|
assoc_list__from_corresponding_lists(Args1, Args2, Args),
|
|
\+ (
|
|
list__member(Arg1 - Arg2, Args),
|
|
\+ key_term_less_or_equal(ModuleInfo,
|
|
UpperLower1, Arg1, UpperLower2, Arg2)
|
|
)
|
|
;
|
|
rl_key__choose_cons_id(ModuleInfo, lower, Type,
|
|
ConsId1, ConsId2, ConsId),
|
|
ConsId = ConsId1
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_key__get_var_bounds(module_info::in, var_map::in,
|
|
prog_var::in, pair(prog_var, pair(key_term))::out) is det.
|
|
|
|
rl_key__get_var_bounds(ModuleInfo, VarMap, Var,
|
|
Var - (LowerBound - UpperBound)) :-
|
|
VarMap = var_map(Map, _),
|
|
( map__search(Map, Var, VarInfo) ->
|
|
VarInfo = var_info(LowerBound0, UpperBound0),
|
|
set__init(PropagatedVars),
|
|
rl_key__propagate_alias_bounds(ModuleInfo, lower, VarMap,
|
|
LowerBound0, var - PropagatedVars, LowerBound),
|
|
rl_key__propagate_alias_bounds(ModuleInfo, upper, VarMap,
|
|
UpperBound0, var - PropagatedVars, UpperBound)
|
|
;
|
|
set__singleton_set(Vars, Var),
|
|
LowerBound = var - Vars,
|
|
UpperBound = var - Vars
|
|
).
|
|
|
|
:- type upper_lower
|
|
---> upper
|
|
; lower
|
|
.
|
|
|
|
% Propagate the collected aliasing information into the bounds
|
|
% so they are as accurate as possible.
|
|
:- pred rl_key__propagate_alias_bounds(module_info::in, upper_lower::in,
|
|
var_map::in, key_term::in, key_term::in, key_term::out) is det.
|
|
|
|
rl_key__propagate_alias_bounds(ModuleInfo, UpperLower, VarMap, Term - Vars0,
|
|
Bound0, Bound) :-
|
|
set__to_sorted_list(Vars0, Vars),
|
|
list__foldl(rl_key__propagate_var_bounds(ModuleInfo, UpperLower,
|
|
VarMap),
|
|
Vars, Bound0, Bound1),
|
|
rl_key__propagate_alias_bounds_2(ModuleInfo, UpperLower, VarMap,
|
|
Term, Bound1, Bound).
|
|
|
|
:- pred rl_key__propagate_alias_bounds_2(module_info::in, upper_lower::in,
|
|
var_map::in, key_term_node::in, key_term::in, key_term::out) is det.
|
|
|
|
rl_key__propagate_alias_bounds_2(_, _, _, var, Bound, Bound).
|
|
rl_key__propagate_alias_bounds_2(ModuleInfo, UpperLower, VarMap,
|
|
functor(ConsId1, Type, ArgTerms), Bound0, Bound) :-
|
|
(
|
|
Bound0 = functor(ConsId2, _, ArgBounds0) - Vars,
|
|
( ConsId2 = ConsId1 ->
|
|
ConsId = ConsId1,
|
|
rl_key__propagate_alias_bounds_list(ModuleInfo,
|
|
UpperLower, VarMap, ArgTerms, ArgBounds0,
|
|
ArgBounds)
|
|
;
|
|
list__length(ArgTerms, Arity),
|
|
set__init(NoVars),
|
|
list__duplicate(Arity, var - NoVars, UnknownArgs),
|
|
rl_key__det_choose_cons_id(ModuleInfo, UpperLower,
|
|
Type, ConsId1, UnknownArgs,
|
|
ConsId2, ArgBounds0, ConsId, ArgBounds1),
|
|
|
|
( ConsId = ConsId1 ->
|
|
rl_key__propagate_alias_bounds_list(
|
|
ModuleInfo, UpperLower, VarMap,
|
|
ArgTerms, ArgBounds1, ArgBounds)
|
|
;
|
|
ArgBounds = ArgBounds0
|
|
)
|
|
),
|
|
Bound = functor(ConsId, Type, ArgBounds) - Vars
|
|
;
|
|
Bound0 = var - Vars,
|
|
list__length(ArgTerms, Arity),
|
|
set__init(NoVars),
|
|
list__duplicate(Arity, var - NoVars, ArgBounds0),
|
|
rl_key__propagate_alias_bounds_list(ModuleInfo,
|
|
UpperLower, VarMap, ArgTerms, ArgBounds0, ArgBounds),
|
|
Bound = functor(ConsId1, Type, ArgBounds) - Vars
|
|
).
|
|
|
|
:- pred rl_key__propagate_alias_bounds_list(module_info::in,
|
|
upper_lower::in, var_map::in, list(key_term)::in,
|
|
list(key_term)::in, list(key_term)::out) is det.
|
|
|
|
rl_key__propagate_alias_bounds_list(_, _, _, [], [], []).
|
|
rl_key__propagate_alias_bounds_list(_, _, _, [_|_], [], _) :-
|
|
error("rl_key__propagate_alias_bounds").
|
|
rl_key__propagate_alias_bounds_list(_, _, _, [], [_|_], _) :-
|
|
error("rl_key__propagate_alias_bounds").
|
|
rl_key__propagate_alias_bounds_list(ModuleInfo, UpperLower, VarMap,
|
|
[ArgTerm | ArgTerms], [ArgBound0 | ArgBounds0],
|
|
[ArgBound | ArgBounds]) :-
|
|
rl_key__propagate_alias_bounds(ModuleInfo, UpperLower, VarMap,
|
|
ArgTerm, ArgBound0, ArgBound),
|
|
rl_key__propagate_alias_bounds_list(ModuleInfo, UpperLower,
|
|
VarMap, ArgTerms, ArgBounds0, ArgBounds).
|
|
|
|
:- pred rl_key__propagate_var_bounds(module_info::in, upper_lower::in,
|
|
var_map::in, prog_var::in, key_term::in, key_term::out) is det.
|
|
|
|
rl_key__propagate_var_bounds(ModuleInfo, UpperLower, VarMap, Var,
|
|
Bound0, Bound) :-
|
|
Bound0 = Term0 - PropagatedVars0,
|
|
( set__member(Var, PropagatedVars0) ->
|
|
Bound = Bound0
|
|
;
|
|
set__insert(PropagatedVars0, Var, PropagatedVars1),
|
|
Bound1 = Term0 - PropagatedVars1,
|
|
VarMap = var_map(Map, _),
|
|
( map__search(Map, Var, VarInfo) ->
|
|
VarInfo = var_info(VarLowerBound, VarUpperBound),
|
|
(
|
|
UpperLower = upper,
|
|
VarBound = VarUpperBound
|
|
;
|
|
UpperLower = lower,
|
|
VarBound = VarLowerBound
|
|
),
|
|
rl_key__propagate_alias_bounds(ModuleInfo,
|
|
UpperLower, VarMap, VarBound, Bound1, Bound)
|
|
;
|
|
Bound = Bound0
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Try to infer upper and lower bounds on variables involved in a goal.
|
|
:- pred rl_key__extract_key_range(hlds_goal::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__extract_key_range(Goal) -->
|
|
( { Goal = unify(_, _, _, Unify, _) - _ } ->
|
|
rl_key__extract_key_range_unify(Unify)
|
|
; { Goal = call(PredId, ProcId, CallArgs, _, _, _) - _ } ->
|
|
rl_key__extract_key_range_call(PredId, ProcId, CallArgs)
|
|
; { Goal = disj(Goals, _) - _ } ->
|
|
key_info_get_constraints(Cnstrs0),
|
|
rl_key__extract_key_range_disj(Cnstrs0, Goals, [], Cnstrs),
|
|
key_info_set_constraints(Cnstrs)
|
|
; { Goal = switch(Var, _CanFail, Cases, _) - _ } ->
|
|
key_info_get_constraints(Cnstrs0),
|
|
rl_key__extract_key_range_switch(Cnstrs0, Var, Cases,
|
|
[], Cnstrs),
|
|
key_info_set_constraints(Cnstrs)
|
|
; { Goal = if_then_else(_, Cond, Then, Else, _) - _ } ->
|
|
key_info_get_constraints(Cnstrs0),
|
|
rl_key__extract_key_range(Cond),
|
|
rl_key__extract_key_range(Then),
|
|
key_info_get_constraints(Cnstrs1),
|
|
key_info_set_constraints(Cnstrs0),
|
|
rl_key__extract_key_range(Else),
|
|
key_info_get_constraints(Cnstrs2),
|
|
{ list__append(Cnstrs1, Cnstrs2, Cnstrs) },
|
|
key_info_set_constraints(Cnstrs)
|
|
;
|
|
[]
|
|
).
|
|
|
|
:- pred rl_key__extract_key_range_disj(list(var_map)::in,
|
|
list(hlds_goal)::in, list(var_map)::in, list(var_map)::out,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__extract_key_range_disj(_, [], Maps, Maps) --> [].
|
|
rl_key__extract_key_range_disj(Cnstrs0, [Goal | Goals], Maps0, Maps) -->
|
|
key_info_set_constraints(Cnstrs0),
|
|
rl_key__extract_key_range(Goal),
|
|
key_info_get_constraints(Cnstrs),
|
|
{ list__append(Cnstrs, Maps0, Maps1) },
|
|
rl_key__extract_key_range_disj(Cnstrs0, Goals, Maps1, Maps).
|
|
|
|
:- pred rl_key__extract_key_range_switch(list(var_map)::in, prog_var::in,
|
|
list(case)::in, list(var_map)::in, list(var_map)::out,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__extract_key_range_switch(_, _Var, [], Maps, Maps) --> [].
|
|
rl_key__extract_key_range_switch(Cnstrs0, Var, [Case | Cases],
|
|
Maps0, Maps) -->
|
|
{ Case = case(ConsId, Goal) },
|
|
key_info_set_constraints(Cnstrs0),
|
|
rl_key__add_functor_constraint(Var, ConsId),
|
|
rl_key__extract_key_range(Goal),
|
|
key_info_get_constraints(Cnstrs),
|
|
{ list__append(Cnstrs, Maps0, Maps1) },
|
|
rl_key__extract_key_range_switch(Cnstrs0, Var, Cases, Maps1, Maps).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Interpret calls to comparison and unification procedures.
|
|
% XXX Interpret integer and floating point builtins as well.
|
|
:- pred rl_key__extract_key_range_call(pred_id::in, proc_id::in,
|
|
list(prog_var)::in, key_info::in, key_info::out) is det.
|
|
|
|
rl_key__extract_key_range_call(PredId, ProcId, Args) -->
|
|
key_info_get_module_info(ModuleInfo),
|
|
{ module_info_pred_info(ModuleInfo, PredId, PredInfo) },
|
|
{ pred_info_module(PredInfo, PredModule) },
|
|
{ pred_info_name(PredInfo, PredName) },
|
|
{ list__length(Args, Arity) },
|
|
(
|
|
{ hlds_pred__in_in_unification_proc_id(ProcId) },
|
|
{ is_builtin_unify_pred(PredModule, PredName, Arity) }
|
|
->
|
|
% Find the last two arguments, the rest will be type_infos.
|
|
{ list__reverse(Args, RevArgs) },
|
|
( { RevArgs = [Arg1, Arg2 | _] } ->
|
|
rl_key__unify_var_var(Arg1, Arg2)
|
|
;
|
|
{ error("rl_key__extract_key_range_call: __Unify__") }
|
|
)
|
|
;
|
|
{ is_builtin_compare_pred(PredModule,
|
|
PredName, Arity, CompareType) }
|
|
->
|
|
rl_key__update_compare_bounds(CompareType, Args)
|
|
;
|
|
[]
|
|
).
|
|
|
|
|
|
:- pred rl_key__update_compare_bounds(compare_type::in, list(prog_var)::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__update_compare_bounds(result(Result), Args0) -->
|
|
{ list__reverse(Args0, Args) },
|
|
( { Args = [Arg2, Arg1 | _] } ->
|
|
rl_key__update_compare_bounds_2(Result, Arg1, Arg2)
|
|
;
|
|
{ error("rl_key__update_compare_bounds") }
|
|
).
|
|
rl_key__update_compare_bounds(unknown, Args) -->
|
|
( { Args = [Arg2, Arg1, ResultVar | _] } ->
|
|
rl_key__add_compare_result(ResultVar, Arg1, Arg2)
|
|
;
|
|
{ error("rl_key__update_compare_bounds") }
|
|
).
|
|
|
|
:- pred rl_key__add_compare_result(prog_var::in, prog_var::in, prog_var::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__add_compare_result(CompareResult, Arg1, Arg2) -->
|
|
key_info_get_constraints(Cnstrs0),
|
|
{ UpdateCompares =
|
|
lambda([VarMap0::in, VarMap::out] is det, (
|
|
VarMap0 = var_map(Map, Compares0),
|
|
map__set(Compares0, CompareResult,
|
|
Arg1 - Arg2, Compares),
|
|
VarMap = var_map(Map, Compares)
|
|
)) },
|
|
{ list__map(UpdateCompares, Cnstrs0, Cnstrs) },
|
|
key_info_set_constraints(Cnstrs).
|
|
|
|
:- pred rl_key__update_compare_bounds_2(comparison_result::in,
|
|
prog_var::in, prog_var::in, key_info::in, key_info::out) is det.
|
|
|
|
rl_key__update_compare_bounds_2((<), Arg1, Arg2) -->
|
|
rl_key__add_var_upper_bound(Arg1, Arg2),
|
|
rl_key__add_var_lower_bound(Arg2, Arg1).
|
|
rl_key__update_compare_bounds_2((>), Arg1, Arg2) -->
|
|
rl_key__add_var_lower_bound(Arg1, Arg2),
|
|
rl_key__add_var_upper_bound(Arg2, Arg1).
|
|
rl_key__update_compare_bounds_2((=), Arg1, Arg2) -->
|
|
rl_key__unify_var_var(Arg1, Arg2).
|
|
|
|
|
|
% Note that all ranges in a B-tree are closed, so '<' is treated
|
|
% as '=<' when computing the key range. The join condition must be
|
|
% sure to do the '\=' comparison at run-time.
|
|
:- type compare_type
|
|
---> result(comparison_result) % ordering if the predicate succeeds.
|
|
; unknown % result of compare/3, to be tested later.
|
|
.
|
|
|
|
:- pred is_builtin_compare_pred(sym_name::in, string::in,
|
|
int::in, compare_type::out) is semidet.
|
|
|
|
is_builtin_compare_pred(_, "__Compare__", _, unknown).
|
|
is_builtin_compare_pred(Module, "compare", 4, unknown) :-
|
|
mercury_public_builtin_module(Module).
|
|
is_builtin_compare_pred(unqualified("int"), "<", 2, result(<)).
|
|
is_builtin_compare_pred(unqualified("int"), "=<", 2, result(<)).
|
|
is_builtin_compare_pred(unqualified("int"), ">", 2, result(>)).
|
|
is_builtin_compare_pred(unqualified("int"), ">=", 2, result(>)).
|
|
is_builtin_compare_pred(Module, "builtin_compare_int", 3, unknown) :-
|
|
mercury_private_builtin_module(Module).
|
|
is_builtin_compare_pred(unqualified("float"), "<", 2, result(<)).
|
|
is_builtin_compare_pred(unqualified("float"), "=<", 2, result(<)).
|
|
is_builtin_compare_pred(unqualified("float"), ">", 2, result(>)).
|
|
is_builtin_compare_pred(unqualified("float"), ">=", 2, result(>)).
|
|
is_builtin_compare_pred(Module, "builtin_compare_float", 3, unknown) :-
|
|
mercury_private_builtin_module(Module).
|
|
is_builtin_compare_pred(Module, "builtin_compare_string", 3, unknown) :-
|
|
mercury_private_builtin_module(Module).
|
|
|
|
:- pred is_builtin_unify_pred(sym_name::in, string::in, int::in) is semidet.
|
|
|
|
is_builtin_unify_pred(_, "__Unify__", _).
|
|
is_builtin_unify_pred(Module, "unify", 3) :-
|
|
mercury_public_builtin_module(Module).
|
|
is_builtin_unify_pred(Module, "builtin_unify_int", 2) :-
|
|
mercury_private_builtin_module(Module).
|
|
is_builtin_unify_pred(Module, "builtin_unify_character", 2) :-
|
|
mercury_private_builtin_module(Module).
|
|
is_builtin_unify_pred(Module, "builtin_unify_string", 2) :-
|
|
mercury_private_builtin_module(Module).
|
|
is_builtin_unify_pred(Module, "builtin_unify_float", 2) :-
|
|
mercury_private_builtin_module(Module).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred rl_key__extract_key_range_unify(unification::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__extract_key_range_unify(simple_test(Var1, Var2)) -->
|
|
rl_key__unify_var_var(Var1, Var2).
|
|
rl_key__extract_key_range_unify(assign(Var1, Var2)) -->
|
|
rl_key__unify_var_var(Var1, Var2).
|
|
rl_key__extract_key_range_unify(construct(Var, ConsId, Args, _, _, _, _)) -->
|
|
rl_key__unify_functor(Var, ConsId, Args).
|
|
rl_key__extract_key_range_unify(
|
|
deconstruct(Var, ConsId, Args, _, _)) -->
|
|
rl_key__unify_functor(Var, ConsId, Args).
|
|
rl_key__extract_key_range_unify(complicated_unify(_, _, _)) -->
|
|
{ error("rl_key__extract_key_range_unify") }.
|
|
|
|
:- pred rl_key__unify_functor(prog_var::in, cons_id::in, list(prog_var)::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__unify_functor(Var, ConsId, Args) -->
|
|
key_info_get_constraints(Constraints0),
|
|
{ GetArgTerm = lambda([Arg::in, ArgTerm::out] is det, (
|
|
set__singleton_set(ArgSet, Arg),
|
|
ArgTerm = var - ArgSet
|
|
)) },
|
|
{ list__map(GetArgTerm, Args, ArgTerms) },
|
|
|
|
{ set__singleton_set(VarSet, Var) },
|
|
key_info_get_vartypes(VarTypes),
|
|
{ map__lookup(VarTypes, Var, Type) },
|
|
{ Term1 = functor(ConsId, Type, ArgTerms) - VarSet },
|
|
key_info_get_module_info(ModuleInfo),
|
|
{ AddConstraint =
|
|
lambda([VarMap0::in, VarMap::out] is semidet, (
|
|
VarMap0 = var_map(Map0, CompRes),
|
|
( map__search(Map0, Var, VarInfo0) ->
|
|
VarInfo0 = var_info(LBound0, UBound0),
|
|
rl_key__unify_term(ModuleInfo,
|
|
lower, Term1, LBound0, LBound),
|
|
rl_key__unify_term(ModuleInfo,
|
|
upper, Term1, UBound0, UBound),
|
|
VarInfo = var_info(LBound, UBound)
|
|
;
|
|
VarInfo = var_info(Term1, Term1)
|
|
),
|
|
map__set(Map0, Var, VarInfo, Map),
|
|
VarMap = var_map(Map, CompRes)
|
|
)) },
|
|
{ list__filter_map(AddConstraint, Constraints0, Constraints) },
|
|
key_info_set_constraints(Constraints).
|
|
|
|
:- pred rl_key__unify_var_var(prog_var::in, prog_var::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__unify_var_var(Var1, Var2) -->
|
|
key_info_get_constraints(Constraints0),
|
|
{ AddEquality =
|
|
lambda([Map0::in, Map::out] is det, (
|
|
rl_key__add_alias(Var1, Var2, Map0, Map1),
|
|
rl_key__add_alias(Var2, Var1, Map1, Map)
|
|
)) },
|
|
{ list__map(AddEquality, Constraints0, Constraints) },
|
|
key_info_set_constraints(Constraints).
|
|
|
|
:- pred rl_key__add_alias(prog_var::in, prog_var::in,
|
|
var_map::in, var_map::out) is det.
|
|
|
|
rl_key__add_alias(Var1, Var2, Map0, Map) :-
|
|
Map0 = var_map(VarMap0, Compares0),
|
|
( map__search(VarMap0, Var1, VarInfo0) ->
|
|
VarInfo0 = var_info(LBound0, UBound0),
|
|
rl_key__add_var_to_node(LBound0, Var2, LBound),
|
|
rl_key__add_var_to_node(UBound0, Var2, UBound),
|
|
VarInfo = var_info(LBound, UBound)
|
|
;
|
|
set__singleton_set(Vars, Var2),
|
|
VarInfo = var_info(var - Vars, var - Vars)
|
|
),
|
|
map__set(VarMap0, Var1, VarInfo, VarMap),
|
|
Map = var_map(VarMap, Compares0).
|
|
|
|
:- pred rl_key__add_var_to_node(key_term::in,
|
|
prog_var::in, key_term::out) is det.
|
|
|
|
rl_key__add_var_to_node(Node - Vars0, Var, Node - Vars) :-
|
|
set__insert(Vars0, Var, Vars).
|
|
|
|
:- pred rl_key__add_functor_constraint(prog_var::in, cons_id::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__add_functor_constraint(Var, ConsId) -->
|
|
(
|
|
{ ConsId = int_const(_), Arity = 0
|
|
; ConsId = float_const(_), Arity = 0
|
|
; ConsId = string_const(_), Arity = 0
|
|
; ConsId = cons(_, Arity)
|
|
}
|
|
->
|
|
key_info_get_vartypes(VarTypes),
|
|
{ map__lookup(VarTypes, Var, VarType) },
|
|
{ set__init(InitVars) },
|
|
{ list__duplicate(Arity, var - InitVars, Args) },
|
|
{ Term = functor(ConsId, VarType, Args) - InitVars },
|
|
rl_key__add_equality_constraint(Var, Term)
|
|
;
|
|
[]
|
|
).
|
|
|
|
:- pred rl_key__add_equality_constraint(prog_var::in, key_term::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__add_equality_constraint(Var, Term) -->
|
|
rl_key__update_bounds(Var, Term, Term).
|
|
|
|
:- pred rl_key__add_var_lower_bound(prog_var::in, prog_var::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__add_var_lower_bound(Var, BoundVar) -->
|
|
{ set__singleton_set(Vars, BoundVar) },
|
|
rl_key__add_lower_bound(Var, var - Vars).
|
|
|
|
:- pred rl_key__add_var_upper_bound(prog_var::in, prog_var::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__add_var_upper_bound(Var, BoundVar) -->
|
|
{ set__singleton_set(Vars, BoundVar) },
|
|
rl_key__add_upper_bound(Var, var - Vars).
|
|
|
|
:- pred rl_key__add_lower_bound(prog_var::in, key_term::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__add_lower_bound(Var, Term) -->
|
|
{ set__init(Vars) },
|
|
rl_key__update_bounds(Var, Term, var - Vars).
|
|
|
|
:- pred rl_key__add_upper_bound(prog_var::in, key_term::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__add_upper_bound(Var, Term) -->
|
|
{ set__init(Vars) },
|
|
rl_key__update_bounds(Var, var - Vars, Term).
|
|
|
|
:- pred rl_key__update_bounds(prog_var::in, key_term::in, key_term::in,
|
|
key_info::in, key_info::out) is det.
|
|
|
|
rl_key__update_bounds(Var, LowerBound1, UpperBound1) -->
|
|
key_info_get_constraints(Cnstrs0),
|
|
key_info_get_module_info(ModuleInfo),
|
|
{ UpdateBounds =
|
|
lambda([VarMap0::in, VarMap::out] is det, (
|
|
VarMap0 = var_map(Map0, CompRes),
|
|
( map__search(Map0, Var, VarInfo0) ->
|
|
VarInfo0 = var_info(LowerBound0, UpperBound0),
|
|
rl_key__unify_term(ModuleInfo, lower,
|
|
LowerBound0, LowerBound1, LowerBound),
|
|
rl_key__unify_term(ModuleInfo, upper,
|
|
UpperBound0, UpperBound1, UpperBound),
|
|
VarInfo = var_info(LowerBound, UpperBound)
|
|
;
|
|
VarInfo = var_info(LowerBound1, UpperBound1)
|
|
),
|
|
map__set(Map0, Var, VarInfo, Map),
|
|
VarMap = var_map(Map, CompRes)
|
|
)) },
|
|
{ list__map(UpdateBounds, Cnstrs0, Cnstrs) },
|
|
key_info_set_constraints(Cnstrs).
|
|
|
|
% If the bounds can't be combined, we should throw away
|
|
% this set of constraints, since they are unsatisfiable.
|
|
:- pred rl_key__unify_term(module_info::in, upper_lower::in,
|
|
key_term::in, key_term::in, key_term::out) is det.
|
|
|
|
rl_key__unify_term(ModuleInfo, UpperLower, Term1 - Vars1,
|
|
Term2 - Vars2, Term - Vars) :-
|
|
set__union(Vars1, Vars2, Vars),
|
|
rl_key__unify_term_2(ModuleInfo, UpperLower,
|
|
Term1, Term2, Term).
|
|
|
|
:- pred rl_key__unify_term_2(module_info::in, upper_lower::in,
|
|
key_term_node::in, key_term_node::in, key_term_node::out) is det.
|
|
|
|
rl_key__unify_term_2(_, _, var, var, var).
|
|
rl_key__unify_term_2(_, _, functor(ConsId, Type, Args),
|
|
var, functor(ConsId, Type, Args)).
|
|
rl_key__unify_term_2(_, _, var, functor(ConsId, Type, Args),
|
|
functor(ConsId, Type, Args)).
|
|
rl_key__unify_term_2(ModuleInfo, UpperLower,
|
|
functor(ConsId1, Type, Args1),
|
|
functor(ConsId2, _, Args2),
|
|
functor(ConsId, Type, Args)) :-
|
|
( ConsId1 = ConsId2 ->
|
|
ConsId = ConsId1,
|
|
assoc_list__from_corresponding_lists(Args1, Args2, Args3),
|
|
UpdatePair =
|
|
lambda([Pair::in, Bound::out] is det, (
|
|
Pair = Bound1 - Bound2,
|
|
rl_key__unify_term(ModuleInfo,
|
|
UpperLower, Bound1, Bound2, Bound)
|
|
)),
|
|
list__map(UpdatePair, Args3, Args)
|
|
;
|
|
rl_key__det_choose_cons_id(ModuleInfo, UpperLower,
|
|
Type, ConsId1, Args1, ConsId2, Args2, ConsId, Args)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Given two non-identical cons_ids of the same type, choose
|
|
% the one which gives the largest range on the possible
|
|
% value of the term.
|
|
:- pred rl_key__det_choose_cons_id(module_info::in, upper_lower::in,
|
|
(type)::in, cons_id::in, list(T)::in, cons_id::in, list(T)::in,
|
|
cons_id::out, list(T)::out) is det.
|
|
|
|
rl_key__det_choose_cons_id(ModuleInfo, UpperLower, Type, ConsId1, Args1,
|
|
ConsId2, Args2, ConsId, Args) :-
|
|
(
|
|
rl_key__choose_cons_id(ModuleInfo, UpperLower,
|
|
Type, ConsId1, ConsId2, ChosenConsId)
|
|
->
|
|
ConsId = ChosenConsId,
|
|
( ConsId = ConsId1 ->
|
|
Args = Args1
|
|
;
|
|
Args = Args2
|
|
)
|
|
;
|
|
error("rl_key__det_choose_cons_id: invalid cons_id pair")
|
|
).
|
|
|
|
:- pred rl_key__choose_cons_id(module_info::in, upper_lower::in,
|
|
(type)::in, cons_id::in, cons_id::in, cons_id::out) is semidet.
|
|
|
|
rl_key__choose_cons_id(ModuleInfo, UpperLower, Type,
|
|
ConsId1, ConsId2, ConsId) :-
|
|
(
|
|
ConsId1 = cons(_, _),
|
|
ConsId2 = cons(_, _)
|
|
->
|
|
module_info_types(ModuleInfo, Types),
|
|
type_to_type_id(Type, TypeId, _),
|
|
map__lookup(Types, TypeId, TypeDefn),
|
|
hlds_data__get_type_defn_body(TypeDefn, Body),
|
|
% If there's a user defined equality pred we're in trouble.
|
|
Body = du_type(Ctors, _, _, no),
|
|
rl_key__choose_cons_id_2(Ctors, UpperLower, ConsId1,
|
|
ConsId2, ConsId)
|
|
;
|
|
% int_consts etc. can be directly compared.
|
|
compare(CompareRes, ConsId1, ConsId2),
|
|
rl_key__choose_cons_id_3(CompareRes, UpperLower,
|
|
ConsId1, ConsId2, ConsId)
|
|
).
|
|
|
|
% Find the cons_id which compares lowest, then choose
|
|
% the one which gives the largest range.
|
|
:- pred rl_key__choose_cons_id_2(list(constructor)::in, upper_lower::in,
|
|
cons_id::in, cons_id::in, cons_id::out) is semidet.
|
|
|
|
rl_key__choose_cons_id_2([], _, _, _, _) :-
|
|
error("rl_key__choose_cons_id_2: couldn't find ctor").
|
|
rl_key__choose_cons_id_2([Ctor | Ctors], UpperLower,
|
|
ConsId1, ConsId2, ConsId) :-
|
|
Ctor = ctor(_, _, SymName, Args),
|
|
list__length(Args, Arity),
|
|
ThisConsId = cons(SymName, Arity),
|
|
( ThisConsId = ConsId1 ->
|
|
rl_key__choose_cons_id_3((<), UpperLower,
|
|
ConsId1, ConsId2, ConsId)
|
|
; ThisConsId = ConsId2 ->
|
|
rl_key__choose_cons_id_3((<), UpperLower,
|
|
ConsId2, ConsId1, ConsId)
|
|
;
|
|
rl_key__choose_cons_id_2(Ctors, UpperLower,
|
|
ConsId1, ConsId2, ConsId)
|
|
).
|
|
|
|
:- pred rl_key__choose_cons_id_3(comparison_result::in, upper_lower::in,
|
|
cons_id::in, cons_id::in, cons_id::out) is semidet.
|
|
|
|
rl_key__choose_cons_id_3((<), upper, _, ConsId, ConsId).
|
|
rl_key__choose_cons_id_3((<), lower, ConsId, _, ConsId).
|
|
rl_key__choose_cons_id_3((>), upper, ConsId, _, ConsId).
|
|
rl_key__choose_cons_id_3((>), lower, _, ConsId, ConsId).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type key_info
|
|
---> key_info(
|
|
module_info,
|
|
map(prog_var, type),
|
|
list(var_map)
|
|
).
|
|
|
|
:- type var_map
|
|
---> var_map(
|
|
map(prog_var, var_info),
|
|
map(prog_var, pair(prog_var)) % map from comparison result
|
|
% to the pair of compared vars
|
|
).
|
|
|
|
:- type var_info
|
|
---> var_info(
|
|
key_term, % lower bound term
|
|
key_term % upper bound term
|
|
).
|
|
|
|
:- pred key_info_get_module_info(module_info, key_info, key_info).
|
|
:- mode key_info_get_module_info(out, in, out) is det.
|
|
|
|
key_info_get_module_info(ModuleInfo, Info, Info) :-
|
|
Info = key_info(ModuleInfo, _, _).
|
|
|
|
:- pred key_info_get_vartypes(map(prog_var, type), key_info, key_info).
|
|
:- mode key_info_get_vartypes(out, in, out) is det.
|
|
|
|
key_info_get_vartypes(VarTypes, Info, Info) :-
|
|
Info = key_info(_, VarTypes, _).
|
|
|
|
:- pred key_info_get_constraints(list(var_map), key_info, key_info).
|
|
:- mode key_info_get_constraints(out, in, out) is det.
|
|
|
|
key_info_get_constraints(Cnstrs, Info, Info) :-
|
|
Info = key_info(_, _, Cnstrs).
|
|
|
|
:- pred key_info_set_constraints(list(var_map), key_info, key_info).
|
|
:- mode key_info_set_constraints(in, in, out) is det.
|
|
|
|
key_info_set_constraints(Cnstrs, Info0, Info) :-
|
|
Info0 = key_info(A, B, _),
|
|
Info = key_info(A, B, Cnstrs).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|