Files
mercury/compiler/modecheck_unify.m
Simon Taylor 2725b1a331 Aditi update syntax, type and mode checking.
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.
1999-07-13 08:55:28 +00:00

1238 lines
41 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1996-1999 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: modecheck_unify.m.
% Main author: fjh.
%
% This module contains the code to modecheck a unification.
%
% Check that the unification doesn't attempt to unify two free variables
% (or in general two free sub-terms) unless one of them is dead. (Also we
% ought to split unifications up if necessary to avoid complicated
% sub-unifications.)
%
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- module modecheck_unify.
:- interface.
:- import_module hlds_goal, prog_data, mode_info.
% Modecheck a unification
:- pred modecheck_unification(prog_var, unify_rhs, unification, unify_context,
hlds_goal_info, hlds_goal_expr, mode_info, mode_info).
:- mode modecheck_unification(in, in, in, in, in, out,
mode_info_di, mode_info_uo) is det.
% Create a unification between the two given variables.
% The goal's mode and determinism information is not filled in.
:- pred modecheck_unify__create_var_var_unification(prog_var, prog_var, type,
mode_info, hlds_goal).
:- mode modecheck_unify__create_var_var_unification(in, in, in,
mode_info_ui, out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module llds, prog_util, type_util, module_qual, instmap.
:- import_module hlds_module, hlds_goal, hlds_pred, hlds_data, hlds_out.
:- import_module mode_debug, mode_util, mode_info, modes, mode_errors.
:- import_module inst_match, inst_util, unify_proc, code_util, unique_modes.
:- import_module typecheck, modecheck_call, (inst), quantification, make_hlds.
:- import_module polymorphism.
:- import_module bool, list, map, std_util, int, set, require.
:- import_module string, assoc_list.
:- import_module term, varset.
%-----------------------------------------------------------------------------%
modecheck_unification(X, var(Y), Unification0, UnifyContext, _GoalInfo,
Unify, ModeInfo0, ModeInfo) :-
mode_info_get_module_info(ModeInfo0, ModuleInfo0),
mode_info_get_instmap(ModeInfo0, InstMap0),
instmap__lookup_var(InstMap0, X, InstOfX),
instmap__lookup_var(InstMap0, Y, InstOfY),
mode_info_var_is_live(ModeInfo0, X, LiveX),
mode_info_var_is_live(ModeInfo0, Y, LiveY),
(
( LiveX = live, LiveY = live ->
BothLive = live
;
BothLive = dead
),
abstractly_unify_inst(BothLive, InstOfX, InstOfY,
real_unify, ModuleInfo0, UnifyInst, Det1, ModuleInfo1)
->
Inst = UnifyInst,
Det = Det1,
mode_info_set_module_info(ModeInfo0, ModuleInfo1, ModeInfo1),
modecheck_set_var_inst(X, Inst, ModeInfo1, ModeInfo2),
modecheck_set_var_inst(Y, Inst, ModeInfo2, ModeInfo3),
ModeOfX = (InstOfX -> Inst),
ModeOfY = (InstOfY -> Inst),
mode_info_get_var_types(ModeInfo3, VarTypes),
categorize_unify_var_var(ModeOfX, ModeOfY, LiveX, LiveY, X, Y,
Det, UnifyContext, VarTypes, Unification0, ModeInfo3,
Unify, ModeInfo)
;
set__list_to_set([X, Y], WaitingVars),
mode_info_error(WaitingVars, mode_error_unify_var_var(X, Y,
InstOfX, InstOfY), ModeInfo0, ModeInfo1),
% If we get an error, set the inst to not_reached
% to suppress follow-on errors
% But don't call categorize_unification, because
% that could cause an invalid call to
% `unify_proc__request_unify'
Inst = not_reached,
modecheck_set_var_inst(X, Inst, ModeInfo1, ModeInfo2),
modecheck_set_var_inst(Y, Inst, ModeInfo2, ModeInfo),
% return any old garbage
Unification = assign(X, Y),
ModeOfX = (InstOfX -> Inst),
ModeOfY = (InstOfY -> Inst),
Modes = ModeOfX - ModeOfY,
Unify = unify(X, var(Y), Modes, Unification, UnifyContext)
).
modecheck_unification(X0, functor(ConsId0, ArgVars0), Unification0,
UnifyContext, GoalInfo0, Goal, ModeInfo0, ModeInfo) :-
mode_info_get_module_info(ModeInfo0, ModuleInfo0),
mode_info_get_var_types(ModeInfo0, VarTypes0),
map__lookup(VarTypes0, X0, TypeOfX),
%
% We replace any unifications with higher-order pred constants
% by lambda expressions. For example, we replace
%
% X = list__append(Y) % Y::in, X::out
%
% with
%
% X = lambda [A1::in, A2::out] (list__append(Y, A1, A2))
%
% Normally this is done by polymorphism__process_unify_functor,
% but if we're re-modechecking goals after lambda.m has been run
% (e.g. for deforestation), then we may need to do it again here.
% Note that any changes to this code here will probably need to be
% duplicated there too.
%
(
% check if variable has a higher-order type
type_is_higher_order(TypeOfX, PredOrFunc, EvalMethod,
PredArgTypes),
ConsId0 = cons(PName, _),
% but in case we are redoing mode analysis, make sure
% we don't mess with the address constants for type_info
% fields created by polymorphism.m
Unification0 \= construct(_, code_addr_const(_, _),
_, _, _, _, _),
Unification0 \= deconstruct(_, code_addr_const(_, _), _, _, _)
->
%
% convert the pred term to a lambda expression
%
mode_info_get_varset(ModeInfo0, VarSet0),
mode_info_get_context(ModeInfo0, Context),
mode_info_get_predid(ModeInfo0, ThisPredId),
module_info_pred_info(ModuleInfo0, ThisPredId, ThisPredInfo),
pred_info_typevarset(ThisPredInfo, TVarSet),
convert_pred_to_lambda_goal(PredOrFunc, EvalMethod,
X0, ConsId0, PName, ArgVars0, PredArgTypes, TVarSet,
Unification0, UnifyContext, GoalInfo0, Context,
ModuleInfo0, VarSet0, VarTypes0,
Functor0, VarSet, VarTypes),
mode_info_set_varset(VarSet, ModeInfo0, ModeInfo1),
mode_info_set_var_types(VarTypes, ModeInfo1, ModeInfo2),
%
% modecheck this unification in its new form
%
modecheck_unification(X0, Functor0, Unification0, UnifyContext,
GoalInfo0, Goal, ModeInfo2, ModeInfo)
;
%
% It's not a higher-order pred unification - just
% call modecheck_unify_functor to do the ordinary thing.
%
modecheck_unify_functor(X0, TypeOfX,
ConsId0, ArgVars0, Unification0, UnifyContext,
GoalInfo0, Goal, ModeInfo0, ModeInfo)
).
modecheck_unification(X,
lambda_goal(PredOrFunc, EvalMethod, _, ArgVars,
Vars, Modes0, Det, Goal0),
Unification0, UnifyContext, _GoalInfo,
unify(X, RHS, Mode, Unification, UnifyContext),
ModeInfo0, ModeInfo) :-
%
% First modecheck the lambda goal itself:
%
% initialize the initial insts of the lambda variables,
% check that the non-local vars are ground (XXX or any),
% mark the non-local vars as shared,
% lock the non-local vars,
% mark the non-clobbered lambda variables as live,
% modecheck the goal,
% check that the final insts are correct,
% unmark the live vars,
% unlock the non-local vars,
% restore the original instmap.
%
% XXX or should we merge the original and the final instmaps???
%
% The reason that we need to merge the original and final instmaps
% is as follows. The lambda goal will not have bound any variables
% (since they were locked), but it may have added some information
% or lost some uniqueness. We cannot use the final instmap,
% because that may have too much information. If we use the
% initial instmap, variables will be considered as unique
% even if they become shared or clobbered in the lambda goal!
%
% However even this may not be enough. If a unique non-local
% variable is used in its unique inst (e.g. it's used in a ui
% mode) and then shared within the lambda body, this is unsound.
% This variable should be marked as shared at the _top_ of the
% lambda goal. As for implementing this, it probably means that
% the lambda goal should be re-modechecked, or even modechecked
% to a fixpoint.
%
% For the moment, since doing all that properly seems too hard,
% we just share all non-local variables at the top of the lambda goal.
% This is safe, but perhaps too conservative.
%
mode_info_get_module_info(ModeInfo0, ModuleInfo0),
mode_info_get_how_to_check(ModeInfo0, HowToCheckGoal),
( HowToCheckGoal = check_modes ->
% This only needs to be done once.
mode_info_get_types_of_vars(ModeInfo0, Vars, VarTypes),
propagate_types_into_mode_list(VarTypes, ModuleInfo0,
Modes0, Modes)
;
Modes = Modes0
),
% initialize the initial insts of the lambda variables
mode_list_get_initial_insts(Modes, ModuleInfo0, VarInitialInsts),
assoc_list__from_corresponding_lists(Vars, VarInitialInsts, VarInstAL),
instmap_delta_from_assoc_list(VarInstAL, VarInstMapDelta),
mode_info_get_instmap(ModeInfo0, InstMap0),
instmap__apply_instmap_delta(InstMap0, VarInstMapDelta, InstMap1),
mode_info_set_instmap(InstMap1, ModeInfo0, ModeInfo1),
% mark the non-clobbered lambda variables as live
get_arg_lives(Modes, ModuleInfo0, ArgLives),
get_live_vars(Vars, ArgLives, LiveVarsList),
set__list_to_set(LiveVarsList, LiveVars),
mode_info_add_live_vars(LiveVars, ModeInfo1, ModeInfo2),
% lock the non-locals
% (a lambda goal is not allowed to bind any of the non-local
% variables, since it could get called more than once, or
% from inside a negation)
Goal0 = _ - GoalInfo0,
goal_info_get_nonlocals(GoalInfo0, NonLocals0),
set__delete_list(NonLocals0, Vars, NonLocals),
set__to_sorted_list(NonLocals, NonLocalsList),
instmap__lookup_vars(NonLocalsList, InstMap1, NonLocalInsts),
mode_info_get_module_info(ModeInfo2, ModuleInfo2),
(
% XXX This test is too conservative.
%
% We should allow non-local variables to be non-ground
% sometimes, possibly dependent on whether or not they
% are dead after this unification. In addition, we
% should not "share" a unique non-local variable if
% these two conditions hold:
%
% - It is dead after this unification.
% - It is not shared within the lambda body.
%
% Unfortunately, we can't test the latter condition
% until after we've mode-checked the lambda body.
% (See the above comment on merging the initial and
% final instmaps.)
% XXX This test is also not conservative enough!
%
% We should not allow non-local vars to have inst `any';
% because that can lead to unsoundness.
% However, disallowing that idiom would break
% extras/trailed_update/samples/vqueens.m, and
% would make freeze/3 basically useless...
% so for now at least, let's not disallow it,
% even though it is unsafe.
inst_list_is_ground_or_any(NonLocalInsts, ModuleInfo2)
->
make_shared_inst_list(NonLocalInsts, ModuleInfo2,
SharedNonLocalInsts, ModuleInfo3),
instmap__set_vars(InstMap1, NonLocalsList, SharedNonLocalInsts,
InstMap2),
mode_info_set_module_info(ModeInfo2, ModuleInfo3, ModeInfo3),
mode_info_set_instmap(InstMap2, ModeInfo3, ModeInfo4),
mode_info_lock_vars(lambda(PredOrFunc), NonLocals,
ModeInfo4, ModeInfo5),
mode_checkpoint(enter, "lambda goal", ModeInfo5, ModeInfo6),
% if we're being called from unique_modes.m, then we need to
% call unique_modes__check_goal rather than modecheck_goal.
(
HowToCheckGoal = check_unique_modes
->
unique_modes__check_goal(Goal0, Goal, ModeInfo6,
ModeInfo7)
;
modecheck_goal(Goal0, Goal, ModeInfo6, ModeInfo7)
),
mode_list_get_final_insts(Modes, ModuleInfo0, FinalInsts),
modecheck_final_insts(Vars, FinalInsts, ModeInfo7, ModeInfo8),
mode_checkpoint(exit, "lambda goal", ModeInfo8, ModeInfo9),
mode_info_remove_live_vars(LiveVars, ModeInfo9, ModeInfo10),
mode_info_unlock_vars(lambda(PredOrFunc), NonLocals,
ModeInfo10, ModeInfo11),
%
% Ensure that the non-local vars are shared OUTSIDE the
% lambda unification as well as inside.
%
instmap__set_vars(InstMap0, NonLocalsList, SharedNonLocalInsts,
InstMap11),
mode_info_set_instmap(InstMap11, ModeInfo11, ModeInfo12),
%
% Now modecheck the unification of X with the lambda-expression.
%
RHS0 = lambda_goal(PredOrFunc, EvalMethod, modes_are_ok,
ArgVars, Vars, Modes, Det, Goal),
modecheck_unify_lambda(X, PredOrFunc, ArgVars, Modes,
Det, RHS0, Unification0, Mode,
RHS, Unification, ModeInfo12, ModeInfo)
;
list__filter(lambda([Var :: in] is semidet,
( instmap__lookup_var(InstMap1, Var, Inst),
\+ inst_is_ground(ModuleInfo2, Inst)
)),
NonLocalsList, NonGroundNonLocals),
( NonGroundNonLocals = [BadVar | _] ->
instmap__lookup_var(InstMap1, BadVar, BadInst),
set__singleton_set(WaitingVars, BadVar),
mode_info_error(WaitingVars,
mode_error_non_local_lambda_var(BadVar,
BadInst),
ModeInfo2, ModeInfo)
;
error("modecheck_unification(lambda): very strange var")
),
% return any old garbage
RHS = lambda_goal(PredOrFunc, EvalMethod, modes_are_ok,
ArgVars, Vars, Modes0, Det, Goal0),
Mode = (free -> free) - (free -> free),
Unification = Unification0
).
:- pred modecheck_unify_lambda(prog_var, pred_or_func, list(prog_var),
list(mode), determinism, unify_rhs, unification,
pair(mode), unify_rhs, unification, mode_info, mode_info).
:- mode modecheck_unify_lambda(in, in, in, in, in, in, in,
out, out, out, mode_info_di, mode_info_uo) is det.
modecheck_unify_lambda(X, PredOrFunc, ArgVars, LambdaModes,
LambdaDet, RHS0, Unification0, Mode, RHS, Unification,
ModeInfo0, ModeInfo) :-
mode_info_get_module_info(ModeInfo0, ModuleInfo0),
mode_info_get_instmap(ModeInfo0, InstMap0),
instmap__lookup_var(InstMap0, X, InstOfX),
InstOfY = ground(unique, yes(LambdaPredInfo)),
LambdaPredInfo = pred_inst_info(PredOrFunc, LambdaModes, LambdaDet),
(
abstractly_unify_inst(dead, InstOfX, InstOfY, real_unify,
ModuleInfo0, UnifyInst, _Det, ModuleInfo1)
->
Inst = UnifyInst,
mode_info_set_module_info(ModeInfo0, ModuleInfo1, ModeInfo1),
ModeOfX = (InstOfX -> Inst),
ModeOfY = (InstOfY -> Inst),
Mode = ModeOfX - ModeOfY,
% the lambda expression just maps its argument variables
% from their current insts to the same inst
instmap__lookup_vars(ArgVars, InstMap0, ArgInsts),
inst_lists_to_mode_list(ArgInsts, ArgInsts, ArgModes),
categorize_unify_var_lambda(ModeOfX, ArgModes,
X, ArgVars, PredOrFunc,
RHS0, Unification0, ModeInfo1,
RHS, Unification, ModeInfo2),
modecheck_set_var_inst(X, Inst, ModeInfo2, ModeInfo)
;
set__list_to_set([X], WaitingVars),
mode_info_error(WaitingVars,
mode_error_unify_var_lambda(X, InstOfX, InstOfY),
ModeInfo0, ModeInfo1
),
% If we get an error, set the inst to not_reached
% to avoid cascading errors
% But don't call categorize_unification, because
% that could cause an invalid call to
% `unify_proc__request_unify'
Inst = not_reached,
modecheck_set_var_inst(X, Inst, ModeInfo1, ModeInfo),
ModeOfX = (InstOfX -> Inst),
ModeOfY = (InstOfY -> Inst),
Mode = ModeOfX - ModeOfY,
% return any old garbage
Unification = Unification0,
RHS = RHS0
).
:- pred modecheck_unify_functor(prog_var, (type), cons_id, list(prog_var),
unification, unify_context, hlds_goal_info, hlds_goal_expr,
mode_info, mode_info).
:- mode modecheck_unify_functor(in, in, in, in, in, in, in,
out, mode_info_di, mode_info_uo) is det.
modecheck_unify_functor(X, TypeOfX, ConsId0, ArgVars0, Unification0,
UnifyContext, GoalInfo0, Goal, ModeInfo0,
FinalModeInfo) :-
mode_info_get_module_info(ModeInfo0, ModuleInfo0),
mode_info_get_how_to_check(ModeInfo0, HowToCheckGoal),
%
% fully module qualify all cons_ids
% (except for builtins such as ints and characters).
%
(
ConsId0 = cons(Name, OrigArity),
type_to_type_id(TypeOfX, TypeId, _),
TypeId = qualified(TypeModule, _) - _
->
unqualify_name(Name, UnqualName),
ConsId = cons(qualified(TypeModule, UnqualName), OrigArity)
;
ConsId = ConsId0
),
mode_info_get_instmap(ModeInfo0, InstMap0),
instmap__lookup_var(InstMap0, X, InstOfX),
instmap__lookup_vars(ArgVars0, InstMap0, InstArgs),
mode_info_var_is_live(ModeInfo0, X, LiveX),
mode_info_var_list_is_live(ArgVars0, ModeInfo0, LiveArgs),
InstOfY = bound(unique, [functor(ConsId, InstArgs)]),
(
% The occur check: X = f(X) is considered a mode error
% unless X is ground. (Actually it wouldn't be that
% hard to generate code for it - it always fails! -
% but it's most likely to be a programming error,
% so it's better to report it.)
list__member(X, ArgVars0),
\+ inst_is_ground(ModuleInfo0, InstOfX)
->
set__list_to_set([X], WaitingVars),
mode_info_error(WaitingVars,
mode_error_unify_var_functor(X, ConsId, ArgVars0,
InstOfX, InstArgs),
ModeInfo0, ModeInfo1
),
Inst = not_reached,
Det = erroneous,
% If we get an error, set the inst to not_reached
% to avoid cascading errors
% But don't call categorize_unification, because
% that could cause an invalid call to
% `unify_proc__request_unify'
ModeOfX = (InstOfX -> Inst),
ModeOfY = (InstOfY -> Inst),
Mode = ModeOfX - ModeOfY,
modecheck_set_var_inst(X, Inst, ModeInfo1, ModeInfo2),
( bind_args(Inst, ArgVars0, ModeInfo2, ModeInfo3) ->
ModeInfo = ModeInfo3
;
error("bind_args failed")
),
% return any old garbage
Unification = Unification0,
ArgVars = ArgVars0,
ExtraGoals = no_extra_goals
;
abstractly_unify_inst_functor(LiveX, InstOfX, ConsId,
InstArgs, LiveArgs, real_unify, ModuleInfo0,
UnifyInst, Det1, ModuleInfo1)
->
Inst = UnifyInst,
Det = Det1,
mode_info_set_module_info(ModeInfo0, ModuleInfo1, ModeInfo1),
ModeOfX = (InstOfX -> Inst),
ModeOfY = (InstOfY -> Inst),
Mode = ModeOfX - ModeOfY,
( get_mode_of_args(Inst, InstArgs, ModeArgs0) ->
ModeArgs = ModeArgs0
;
error("get_mode_of_args failed")
),
(
inst_expand(ModuleInfo1, InstOfX, InstOfX1),
list__length(ArgVars0, Arity),
get_arg_insts(InstOfX1, ConsId, Arity, InstOfXArgs),
get_mode_of_args(Inst, InstOfXArgs, ModeOfXArgs0)
->
ModeOfXArgs = ModeOfXArgs0
;
error("get_(inst/mode)_of_args failed")
),
mode_info_get_var_types(ModeInfo1, VarTypes),
categorize_unify_var_functor(ModeOfX, ModeOfXArgs, ModeArgs,
X, ConsId, ArgVars0, VarTypes, UnifyContext,
Unification0, ModeInfo1,
Unification1, ModeInfo2),
split_complicated_subunifies(Unification1, ArgVars0,
Unification, ArgVars, ExtraGoals,
ModeInfo2, ModeInfo3),
modecheck_set_var_inst(X, Inst, ModeInfo3, ModeInfo4),
( bind_args(Inst, ArgVars, ModeInfo4, ModeInfo5) ->
ModeInfo = ModeInfo5
;
error("bind_args failed")
)
;
set__list_to_set([X | ArgVars0], WaitingVars), % conservative
mode_info_error(WaitingVars,
mode_error_unify_var_functor(X, ConsId, ArgVars0,
InstOfX, InstArgs),
ModeInfo0, ModeInfo1
),
% If we get an error, set the inst to not_reached
% to avoid cascading errors
% But don't call categorize_unification, because
% that could cause an invalid call to
% `unify_proc__request_unify'
Inst = not_reached,
Det = erroneous,
ModeOfX = (InstOfX -> Inst),
ModeOfY = (InstOfY -> Inst),
Mode = ModeOfX - ModeOfY,
modecheck_set_var_inst(X, Inst, ModeInfo1, ModeInfo2),
( bind_args(Inst, ArgVars0, ModeInfo2, ModeInfo3) ->
ModeInfo = ModeInfo3
;
error("bind_args failed")
),
% return any old garbage
Unification = Unification0,
ArgVars = ArgVars0,
ExtraGoals = no_extra_goals
),
%
% Optimize away construction of unused terms by
% replacing the unification with `true'. Optimize
% away unifications which always fail by replacing
% them with `fail'.
%
(
Unification = construct(ConstructTarget, _, _, _, _, _, _),
mode_info_var_is_live(ModeInfo, ConstructTarget, dead)
->
Goal = conj([]),
FinalModeInfo = ModeInfo
;
Det = failure
->
% This optimisation is safe because the only way that
% we can analyse a unification as having no solutions
% is that the unification always fails.
%,
% Unifying two preds is not erroneous as far as the
% mode checker is concerned, but a mode _error_.
map__init(Empty),
Goal = disj([], Empty),
FinalModeInfo = ModeInfo
;
Functor = functor(ConsId, ArgVars),
Unify = unify(X, Functor, Mode, Unification,
UnifyContext),
X = X0,
%
% modecheck_unification sometimes needs to introduce
% new goals to handle complicated sub-unifications
% in deconstructions. The only time this can happen
% during unique mode analysis is if the instmap is
% unreachable, since inst_is_bound succeeds for not_reached.
% (If it did in other cases, the code would be wrong since it
% wouldn't have the correct determinism annotations.)
%
(
HowToCheckGoal = check_unique_modes,
ExtraGoals \= no_extra_goals,
instmap__is_reachable(InstMap0)
->
error("unique_modes.m: re-modecheck of unification encountered complicated sub-unifies")
;
true
),
handle_extra_goals(Unify, ExtraGoals, GoalInfo0,
[X0|ArgVars0], [X|ArgVars],
InstMap0, Goal, ModeInfo, FinalModeInfo)
).
%-----------------------------------------------------------------------------%
% The argument unifications in a construction or deconstruction
% unification must be simple assignments, they cannot be
% complicated unifications. If they are, we split them out
% into separate unifications by introducing fresh variables here.
:- pred split_complicated_subunifies(unification, list(prog_var),
unification, list(prog_var), extra_goals,
mode_info, mode_info).
:- mode split_complicated_subunifies(in, in, out, out, out,
mode_info_di, mode_info_uo) is det.
split_complicated_subunifies(Unification0, ArgVars0,
Unification, ArgVars, ExtraGoals) -->
(
{ Unification0 = deconstruct(X, ConsId, ArgVars0, ArgModes0,
Det) }
->
(
split_complicated_subunifies_2(ArgVars0, ArgModes0,
ArgVars1, ExtraGoals1)
->
{ ExtraGoals = ExtraGoals1 },
{ ArgVars = ArgVars1 },
{ Unification = deconstruct(X, ConsId, ArgVars,
ArgModes0, Det) }
;
{ error("split_complicated_subunifies_2 failed") }
)
;
{ Unification = Unification0 },
{ ArgVars = ArgVars0 },
{ ExtraGoals = no_extra_goals }
).
:- pred split_complicated_subunifies_2(list(prog_var), list(uni_mode),
list(prog_var), extra_goals, mode_info, mode_info).
:- mode split_complicated_subunifies_2(in, in, out, out,
mode_info_di, mode_info_uo) is semidet.
split_complicated_subunifies_2([], [], [], no_extra_goals) --> [].
split_complicated_subunifies_2([Var0 | Vars0], [UniMode0 | UniModes0],
Vars, ExtraGoals, ModeInfo0, ModeInfo) :-
mode_info_get_module_info(ModeInfo0, ModuleInfo),
UniMode0 = (InitialInstX - InitialInstY -> FinalInstX - FinalInstY),
ModeX = (InitialInstX -> FinalInstX),
ModeY = (InitialInstY -> FinalInstY),
mode_info_get_var_types(ModeInfo0, VarTypes0),
map__lookup(VarTypes0, Var0, VarType),
(
mode_to_arg_mode(ModuleInfo, ModeX, VarType, top_in),
mode_to_arg_mode(ModuleInfo, ModeY, VarType, top_in)
->
% introduce a new variable `Var'
mode_info_get_varset(ModeInfo0, VarSet0),
mode_info_get_var_types(ModeInfo0, VarTypes0),
varset__new_var(VarSet0, Var, VarSet),
map__set(VarTypes0, Var, VarType, VarTypes),
mode_info_set_varset(VarSet, ModeInfo0, ModeInfo1),
mode_info_set_var_types(VarTypes, ModeInfo1, ModeInfo2),
modecheck_unify__create_var_var_unification(Var0, Var,
VarType, ModeInfo2, ExtraGoal),
% insert the new unification at
% the start of the extra goals
ExtraGoals0 = extra_goals([], [ExtraGoal]),
% recursive call to handle the remaining variables...
split_complicated_subunifies_2(Vars0, UniModes0,
Vars1, ExtraGoals1, ModeInfo2, ModeInfo),
Vars = [Var | Vars1],
append_extra_goals(ExtraGoals0, ExtraGoals1, ExtraGoals)
;
split_complicated_subunifies_2(Vars0, UniModes0,
Vars1, ExtraGoals, ModeInfo0, ModeInfo),
Vars = [Var0 | Vars1]
).
modecheck_unify__create_var_var_unification(Var0, Var, Type, ModeInfo,
Goal - GoalInfo) :-
mode_info_get_context(ModeInfo, Context),
mode_info_get_mode_context(ModeInfo, ModeContext),
mode_context_to_unify_context(ModeContext, ModeInfo, UnifyContext),
UnifyContext = unify_context(MainContext, SubContexts),
create_atomic_unification(Var0, var(Var), Context,
MainContext, SubContexts, Goal0 - GoalInfo0),
%
% compute the goal_info nonlocal vars for the newly created goal
% (excluding the type_info vars -- they are added below).
% N.B. This may overestimate the set of non-locals,
% but that shouldn't cause any problems.
%
set__list_to_set([Var0, Var], NonLocals),
goal_info_set_nonlocals(GoalInfo0, NonLocals, GoalInfo1),
goal_info_set_context(GoalInfo1, Context, GoalInfo2),
%
% Look up the map(tvar, type_info_locn) in the proc_info,
% since it is needed by polymorphism__unification_typeinfos
%
mode_info_get_module_info(ModeInfo, ModuleInfo),
mode_info_get_predid(ModeInfo, PredId),
mode_info_get_procid(ModeInfo, ProcId),
module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
_PredInfo, ProcInfo),
proc_info_typeinfo_varmap(ProcInfo, TypeInfoVarMap),
%
% Call polymorphism__unification_typeinfos to add the appropriate
% type-info and type-class-info variables to the nonlocals
% and to the unification.
%
(
Goal0 = unify(X, Y, Mode, Unification0, FinalUnifyContext)
->
polymorphism__unification_typeinfos(Type, TypeInfoVarMap,
Unification0, GoalInfo2, Unification, GoalInfo),
Goal = unify(X, Y, Mode, Unification, FinalUnifyContext)
;
error("modecheck_unify__create_var_var_unification")
).
%-----------------------------------------------------------------------------%
% Work out what kind of unification a var-var unification is.
:- pred categorize_unify_var_var(mode, mode, is_live, is_live, prog_var,
prog_var, determinism, unify_context, map(prog_var, type),
unification, mode_info, hlds_goal_expr, mode_info).
:- mode categorize_unify_var_var(in, in, in, in, in, in, in, in, in, in,
mode_info_di, out, mode_info_uo) is det.
% categorize_unify_var_var works out which category a unification
% between a variable and another variable expression is - whether it is
% an assignment, a simple test or a complicated unify.
categorize_unify_var_var(ModeOfX, ModeOfY, LiveX, LiveY, X, Y, Det,
UnifyContext, VarTypes, Unification0, ModeInfo0,
Unify, ModeInfo) :-
mode_info_get_module_info(ModeInfo0, ModuleInfo0),
(
mode_is_output(ModuleInfo0, ModeOfX)
->
Unification = assign(X, Y),
ModeInfo = ModeInfo0
;
mode_is_output(ModuleInfo0, ModeOfY)
->
Unification = assign(Y, X),
ModeInfo = ModeInfo0
;
mode_is_unused(ModuleInfo0, ModeOfX),
mode_is_unused(ModuleInfo0, ModeOfY)
->
% For free-free unifications, we pretend for a moment that they
% are an assignment to the dead variable - they will then
% be optimized away.
( LiveX = dead ->
Unification = assign(X, Y)
; LiveY = dead ->
Unification = assign(Y, X)
;
error("categorize_unify_var_var: free-free unify!")
),
ModeInfo = ModeInfo0
;
%
% Check for unreachable unifications
%
( mode_get_insts(ModuleInfo0, ModeOfX, not_reached, _)
; mode_get_insts(ModuleInfo0, ModeOfY, not_reached, _)
)
->
%
% For these, we can generate any old junk here --
% we just need to avoid calling modecheck_complicated_unify,
% since that might abort.
%
Unification = simple_test(X, Y),
ModeInfo = ModeInfo0
;
map__lookup(VarTypes, X, Type),
(
type_is_atomic(Type, ModuleInfo0)
->
Unification = simple_test(X, Y),
ModeInfo = ModeInfo0
;
modecheck_complicated_unify(X, Y,
Type, ModeOfX, ModeOfY, Det, UnifyContext,
Unification0, ModeInfo0,
Unification, ModeInfo)
)
),
%
% Optimize away unifications with dead variables
% and simple tests that cannot fail
% by replacing them with `true'.
% (The optimization of simple tests is necessary
% because otherwise determinism analysis assumes they can fail.
% The optimization of assignments to dead variables may be
% necessary to stop the code generator from getting confused.)
% Optimize away unifications which always fail by replacing
% them with `fail'.
%
(
Unification = assign(AssignTarget, _),
mode_info_var_is_live(ModeInfo, AssignTarget, dead)
->
Unify = conj([])
;
Unification = simple_test(_, _),
Det = det
->
Unify = conj([])
;
Det = failure
->
% This optimisation is safe because the only way that
% we can analyse a unification as having no solutions
% is that the unification always fails.
%
% Unifying two preds is not erroneous as far as the
% mode checker is concerned, but a mode _error_.
map__init(Empty),
Unify = disj([], Empty)
;
Unify = unify(X, var(Y), ModeOfX - ModeOfY, Unification,
UnifyContext)
).
%
% modecheck_complicated_unify does some extra checks that are needed
% for mode-checking complicated unifications.
%
:- pred modecheck_complicated_unify(prog_var, prog_var,
type, mode, mode, determinism, unify_context,
unification, mode_info, unification, mode_info).
:- mode modecheck_complicated_unify(in, in, in, in, in, in, in,
in, mode_info_di, out, mode_info_uo) is det.
modecheck_complicated_unify(X, Y, Type, ModeOfX, ModeOfY, Det, UnifyContext,
Unification0, ModeInfo0, Unification, ModeInfo) :-
%
% Build up the unification
%
mode_info_get_module_info(ModeInfo0, ModuleInfo0),
mode_get_insts(ModuleInfo0, ModeOfX, InitialInstX, FinalInstX),
mode_get_insts(ModuleInfo0, ModeOfY, InitialInstY, FinalInstY),
UniMode = ((InitialInstX - InitialInstY) -> (FinalInstX - FinalInstY)),
determinism_components(Det, CanFail, _),
( Unification0 = complicated_unify(_, _, UnifyTypeInfoVars0) ->
UnifyTypeInfoVars = UnifyTypeInfoVars0
;
error("modecheck_complicated_unify")
),
Unification = complicated_unify(UniMode, CanFail, UnifyTypeInfoVars),
%
% check that all the type_info or type_class_info variables used
% by the polymorphic unification are ground.
%
( UnifyTypeInfoVars = [] ->
% optimize common case
ModeInfo2 = ModeInfo0
;
list__length(UnifyTypeInfoVars, NumTypeInfoVars),
list__duplicate(NumTypeInfoVars, ground(shared, no),
ExpectedInsts),
mode_info_set_call_context(unify(UnifyContext),
ModeInfo0, ModeInfo1),
InitialArgNum = 0,
modecheck_var_has_inst_list(UnifyTypeInfoVars, ExpectedInsts,
InitialArgNum, ModeInfo1, ModeInfo2)
),
mode_info_get_module_info(ModeInfo2, ModuleInfo2),
(
mode_info_get_errors(ModeInfo2, Errors),
Errors \= []
->
ModeInfo = ModeInfo2
;
%
% Check that we're not trying to do a polymorphic unification
% in a mode other than (in, in).
% [Actually we also allow `any' insts, since the (in, in)
% mode of unification for types which have `any' insts must
% also be able to handle (in(any), in(any)) unifications.]
%
Type = term__variable(_),
\+ inst_is_ground_or_any(ModuleInfo2, InitialInstX)
->
set__singleton_set(WaitingVars, X),
mode_info_error(WaitingVars,
mode_error_poly_unify(X, InitialInstX),
ModeInfo2, ModeInfo)
;
Type = term__variable(_),
\+ inst_is_ground_or_any(ModuleInfo2, InitialInstY)
->
set__singleton_set(WaitingVars, Y),
mode_info_error(WaitingVars,
mode_error_poly_unify(Y, InitialInstY),
ModeInfo2, ModeInfo)
;
%
% check that we're not trying to do a higher-order unification
%
type_is_higher_order(Type, PredOrFunc, _, _)
->
% We do not want to report this as an error
% if it occurs in a compiler-generated
% predicate - instead, we delay the error
% until runtime so that it only occurs if
% the compiler-generated predicate gets called.
% not_reached is considered bound, so the
% error message would be spurious if the
% instmap is unreachable.
mode_info_get_predid(ModeInfo2, PredId),
module_info_pred_info(ModuleInfo2, PredId,
PredInfo),
mode_info_get_instmap(ModeInfo2, InstMap0),
(
( code_util__compiler_generated(PredInfo)
; instmap__is_unreachable(InstMap0)
)
->
ModeInfo = ModeInfo2
;
set__init(WaitingVars),
mode_info_error(WaitingVars,
mode_error_unify_pred(X, error_at_var(Y),
Type, PredOrFunc),
ModeInfo2, ModeInfo)
)
;
%
% Ensure that we will generate code for the unification
% procedure that will be used to implement this complicated
% unification.
%
type_to_type_id(Type, TypeId, _)
->
mode_info_get_context(ModeInfo2, Context),
unify_proc__request_unify(TypeId - UniMode,
Det, Context, ModuleInfo2, ModuleInfo),
mode_info_set_module_info(ModeInfo2, ModuleInfo,
ModeInfo)
;
ModeInfo = ModeInfo2
).
% categorize_unify_var_lambda works out which category a unification
% between a variable and a lambda expression is - whether it is a construction
% unification or a deconstruction. It also works out whether it will
% be deterministic or semideterministic.
:- pred categorize_unify_var_lambda(mode, list(mode),
prog_var, list(prog_var), pred_or_func, unify_rhs, unification,
mode_info, unify_rhs, unification, mode_info).
:- mode categorize_unify_var_lambda(in, in, in, in, in, in,
in, mode_info_di, out, out, mode_info_uo) is det.
categorize_unify_var_lambda(ModeOfX, ArgModes0, X, ArgVars,
PredOrFunc, RHS0, Unification0, ModeInfo0, RHS,
Unification, ModeInfo) :-
% if we are re-doing mode analysis, preserve the existing cons_id
list__length(ArgVars, Arity),
( Unification0 = construct(_, ConsId0, _, _, _, _, AditiInfo0) ->
AditiInfo = AditiInfo0,
ConsId = ConsId0
; Unification0 = deconstruct(_, ConsId1, _, _, _) ->
AditiInfo = no,
ConsId = ConsId1
;
% the real cons_id will be computed by lambda.m;
% we just put in a dummy one for now
AditiInfo = no,
ConsId = cons(unqualified("__LambdaGoal__"), Arity)
),
mode_info_get_module_info(ModeInfo0, ModuleInfo),
mode_util__modes_to_uni_modes(ArgModes0, ArgModes0,
ModuleInfo, ArgModes),
mode_info_get_instmap(ModeInfo0, InstMap),
(
mode_is_output(ModuleInfo, ModeOfX)
->
(
% If pred_consts are present, lambda expansion
% has already been done. Rerunning mode analysis
% should not produce a lambda_goal which cannot
% be directly converted back into a higher-order
% predicate constant.
% If the instmap is not reachable, the call
% may have been handled as an implied mode,
% since not_reached is considered to be bound.
% In this case the lambda_goal may not be
% converted back to a predicate constant, but
% that doesn't matter since the code will be
% pruned away later by simplify.m.
ConsId = pred_const(PredId, ProcId, EvalMethod),
instmap__is_reachable(InstMap)
->
(
RHS0 = lambda_goal(_, EvalMethod, _,
_, _, _, _, Goal),
Goal = call(PredId, ProcId, _, _, _, _) - _
->
module_info_pred_info(ModuleInfo,
PredId, PredInfo),
pred_info_module(PredInfo, PredModule),
pred_info_name(PredInfo, PredName),
RHS = functor(
cons(qualified(PredModule, PredName),
Arity),
ArgVars)
;
error("categorize_unify_var_lambda - \
reintroduced lambda goal")
)
;
RHS = RHS0
),
Unification = construct(X, ConsId, ArgVars, ArgModes,
no, cell_is_unique, AditiInfo),
ModeInfo = ModeInfo0
;
instmap__is_reachable(InstMap)
->
% If it's a deconstruction, it is a mode error.
% The error message would be incorrect in unreachable
% code, since not_reached is considered bound.
set__init(WaitingVars),
mode_info_get_var_types(ModeInfo0, VarTypes0),
map__lookup(VarTypes0, X, Type),
mode_info_error(WaitingVars,
mode_error_unify_pred(X,
error_at_lambda(ArgVars, ArgModes0),
Type, PredOrFunc),
ModeInfo0, ModeInfo),
% return any old garbage
Unification = Unification0,
RHS = RHS0
;
ModeInfo = ModeInfo0,
Unification = Unification0,
RHS = RHS0
).
% categorize_unify_var_functor works out which category a unification
% between a variable and a functor is - whether it is a construction
% unification or a deconstruction. It also works out whether it will
% be deterministic or semideterministic.
:- pred categorize_unify_var_functor(mode, list(mode), list(mode), prog_var,
cons_id, list(prog_var), map(prog_var, type), unify_context,
unification, mode_info, unification, mode_info).
:- mode categorize_unify_var_functor(in, in, in, in, in, in, in, in, in,
mode_info_di, out, mode_info_uo) is det.
categorize_unify_var_functor(ModeOfX, ModeOfXArgs, ArgModes0,
X, NewConsId, ArgVars, VarTypes, UnifyContext,
Unification0, ModeInfo0, Unification, ModeInfo) :-
mode_info_get_module_info(ModeInfo0, ModuleInfo),
map__lookup(VarTypes, X, TypeOfX),
% if we are re-doing mode analysis, preserve the existing cons_id
( Unification0 = construct(_, ConsId0, _, _, _, _, _) ->
ConsId = ConsId0
; Unification0 = deconstruct(_, ConsId1, _, _, _) ->
ConsId = ConsId1
;
ConsId = NewConsId
),
mode_util__modes_to_uni_modes(ModeOfXArgs, ArgModes0,
ModuleInfo, ArgModes),
(
mode_is_output(ModuleInfo, ModeOfX)
->
% It's a construction.
ReuseVar = no,
RLExprnId = no,
Unification = construct(X, ConsId, ArgVars, ArgModes,
ReuseVar, cell_is_unique, RLExprnId),
% For existentially quantified data types,
% check that any type_info or type_class_info variables in the
% construction are ground.
check_type_info_args_are_ground(ArgVars, VarTypes,
UnifyContext, ModeInfo0, ModeInfo)
;
% It's a deconstruction.
(
% If the variable was already known to be bound
% to a single particular functor, then the
% unification either always succeeds or always
% fails. In the latter case, the final inst will
% be `not_reached' or `bound([])'. So if both
% the initial and final inst are `bound([_])',
% then the unification must be deterministic.
mode_get_insts(ModuleInfo, ModeOfX,
InitialInst0, FinalInst0),
inst_expand(ModuleInfo, InitialInst0, InitialInst),
inst_expand(ModuleInfo, FinalInst0, FinalInst),
InitialInst = bound(_, [_]),
FinalInst = bound(_, [_])
->
CanFail = cannot_fail,
ModeInfo = ModeInfo0
;
% If the type has only one constructor,
% then the unification cannot fail
type_constructors(TypeOfX, ModuleInfo, Constructors),
Constructors = [_]
->
CanFail = cannot_fail,
ModeInfo = ModeInfo0
;
% Otherwise, it can fail
CanFail = can_fail,
mode_info_get_instmap(ModeInfo0, InstMap0),
(
type_is_higher_order(TypeOfX, PredOrFunc,
_, _),
instmap__is_reachable(InstMap0)
->
set__init(WaitingVars),
mode_info_error(WaitingVars,
mode_error_unify_pred(X,
error_at_functor(ConsId, ArgVars),
TypeOfX, PredOrFunc),
ModeInfo0, ModeInfo)
;
ModeInfo = ModeInfo0
)
),
Unification = deconstruct(X, ConsId, ArgVars, ArgModes, CanFail)
).
% Check that any type_info or type_class_info variables
% in the argument list are ground.
:- pred check_type_info_args_are_ground(list(prog_var), map(prog_var, type),
unify_context, mode_info, mode_info).
:- mode check_type_info_args_are_ground(in, in, in,
mode_info_di, mode_info_uo) is det.
check_type_info_args_are_ground([], _VarTypes, _UnifyContext) --> [].
check_type_info_args_are_ground([ArgVar | ArgVars], VarTypes, UnifyContext)
-->
(
{ map__lookup(VarTypes, ArgVar, ArgType) },
{ is_introduced_type_info_type(ArgType) }
->
mode_info_set_call_context(unify(UnifyContext)),
{ InitialArgNum = 0 },
modecheck_var_has_inst_list([ArgVar], [ground(shared, no)],
InitialArgNum),
check_type_info_args_are_ground(ArgVars, VarTypes,
UnifyContext)
;
[]
).
%-----------------------------------------------------------------------------%
:- pred bind_args(inst, list(prog_var), mode_info, mode_info).
:- mode bind_args(in, in, mode_info_di, mode_info_uo) is semidet.
bind_args(not_reached, _) -->
{ instmap__init_unreachable(InstMap) },
mode_info_set_instmap(InstMap).
bind_args(ground(Uniq, no), Args) -->
ground_args(Uniq, Args).
bind_args(bound(_Uniq, List), Args) -->
( { List = [] } ->
% the code is unreachable
{ instmap__init_unreachable(InstMap) },
mode_info_set_instmap(InstMap)
;
{ List = [functor(_, InstList)] },
bind_args_2(Args, InstList)
).
:- pred bind_args_2(list(prog_var), list(inst), mode_info, mode_info).
:- mode bind_args_2(in, in, mode_info_di, mode_info_uo) is semidet.
bind_args_2([], []) --> [].
bind_args_2([Arg | Args], [Inst | Insts]) -->
modecheck_set_var_inst(Arg, Inst),
bind_args_2(Args, Insts).
:- pred ground_args(uniqueness, list(prog_var), mode_info, mode_info).
:- mode ground_args(in, in, mode_info_di, mode_info_uo) is det.
ground_args(_Uniq, []) --> [].
ground_args(Uniq, [Arg | Args]) -->
modecheck_set_var_inst(Arg, ground(Uniq, no)),
ground_args(Uniq, Args).
%-----------------------------------------------------------------------------%
% get_mode_of_args(FinalInst, InitialArgInsts, ArgModes):
% for a var-functor unification,
% given the final inst of the var
% and the initial insts of the functor arguments,
% compute the modes of the functor arguments
:- pred get_mode_of_args(inst, list(inst), list(mode)).
:- mode get_mode_of_args(in, in, out) is semidet.
get_mode_of_args(not_reached, ArgInsts, ArgModes) :-
mode_set_args(ArgInsts, not_reached, ArgModes).
get_mode_of_args(any(Uniq), ArgInsts, ArgModes) :-
mode_set_args(ArgInsts, any(Uniq), ArgModes).
get_mode_of_args(ground(Uniq, no), ArgInsts, ArgModes) :-
mode_set_args(ArgInsts, ground(Uniq, no), ArgModes).
get_mode_of_args(bound(_Uniq, List), ArgInstsA, ArgModes) :-
( List = [] ->
% the code is unreachable
mode_set_args(ArgInstsA, not_reached, ArgModes)
;
List = [functor(_Name, ArgInstsB)],
get_mode_of_args_2(ArgInstsA, ArgInstsB, ArgModes)
).
:- pred get_mode_of_args_2(list(inst), list(inst), list(mode)).
:- mode get_mode_of_args_2(in, in, out) is semidet.
get_mode_of_args_2([], [], []).
get_mode_of_args_2([InstA | InstsA], [InstB | InstsB], [Mode | Modes]) :-
Mode = (InstA -> InstB),
get_mode_of_args_2(InstsA, InstsB, Modes).
:- pred mode_set_args(list(inst), inst, list(mode)).
:- mode mode_set_args(in, in, out) is det.
mode_set_args([], _, []).
mode_set_args([Inst | Insts], FinalInst, [Mode | Modes]) :-
Mode = (Inst -> FinalInst),
mode_set_args(Insts, FinalInst, Modes).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%