mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 06:47:17 +00:00
Estimated hours taken: 6 Rearrange the ordering of the different phases in the compiler. Moved lambda elimination (lambda.m) after unique_modes.m, because mode analysis must have been fully completed before lambda elimination, so that we get the right mode on the introduced predicates. Also moved inlining.m and common.m after unique modes, since they are optimization passes, not semantic checking passes. The cse_detection.m, switch_detection.m, and determinism.m passes now need to recursively traverse lambda goals. (Previously they assumed that lambda goals had already been eliminated.) mercury_compile.pp: Move the inlining.m and common.m passes from semantic_phases to middle_phases. polymorphism.m: Remove the code which called lambda.m. switch_detection.m: Recursively traverse lambda goals. cse_detection.m: Recursively traverse lambda goals. Also, when redoing mode analysis and switch detection, we shouldn't reinvoke lambda.m. det_analysis.m, det_report.m: Recursively traverse lambda goals, check for determinism errors in them, and report them. Also, print the calling predicate name & mode in the error message for calls to predicates with cc_* determinism in all-solutions contexts. modes.m: Add an extra argument to modecheck_unification that specifies how we should recursively process lambda goals, so that we can do the right thing when called from unique_modes.m. The right thing in this case is to call unique_modes__check_goal instead of modecheck_goal, and to then invoke lambda__transform_lambda on the result. unique_modes.m: Make sure we don't clobber the predicate table, since we now indirectly call lambda__transform_lambda, which inserts new predicates into the table. Also, simplify the code a little and add a sanity check. lambda.m: Make some changes that were needed because lambda.m now comes directly after (unique_)modes.m not after polymorphism.m.
343 lines
12 KiB
Mathematica
343 lines
12 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1995 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: lambda.m
|
|
% main author: fjh
|
|
|
|
% This module is a pass over the HLDS to deal with lambda expressions.
|
|
%
|
|
% Lambda expressions are converted into separate predicates, so for
|
|
% example we translate
|
|
%
|
|
% :- pred p(int::in) is det.
|
|
% p(X) :-
|
|
% V__1 = lambda([Y::out] is nondet, q(Y, X))),
|
|
% solutions(V__1, List),
|
|
% ...
|
|
% :- pred q(int::out, int::in) is nondet.
|
|
%
|
|
% into
|
|
%
|
|
% p(X) :-
|
|
% V__1 = '__LambdaGoal__1'(X)
|
|
% solutions(V__1, List),
|
|
% ...
|
|
%
|
|
% :- pred '__LambdaGoal__1'(int::in, int::out) is nondet.
|
|
% '__LambdaGoal__1'(X, Y) :- q(Y, X).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module (lambda).
|
|
:- interface.
|
|
:- import_module hlds.
|
|
:- import_module list, set, map.
|
|
:- import_module varset, term.
|
|
:- import_module prog_io.
|
|
|
|
:- pred lambda__process_pred(pred_id, module_info, module_info).
|
|
:- mode lambda__process_pred(in, in, out) is det.
|
|
|
|
:- pred lambda__transform_lambda(list(var), list(mode), determinism,
|
|
hlds__goal, unification, varset,
|
|
map(var, type), tvarset, module_info,
|
|
unify_rhs, unification, module_info).
|
|
:- mode lambda__transform_lambda(in, in, in, in, in, in, in, in, in,
|
|
out, out, out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
:- import_module string, std_util, require.
|
|
:- import_module make_hlds.
|
|
|
|
:- type lambda_info --->
|
|
lambda_info(
|
|
varset, % from the proc_info
|
|
map(var, type), % from the proc_info
|
|
tvarset, % from the proc_info
|
|
module_info
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% This whole section just traverses the module structure.
|
|
|
|
lambda__process_pred(PredId, ModuleInfo0, ModuleInfo) :-
|
|
module_info_pred_info(ModuleInfo0, PredId, PredInfo),
|
|
pred_info_procids(PredInfo, ProcIds),
|
|
lambda__process_procs(PredId, ProcIds, ModuleInfo0, ModuleInfo).
|
|
|
|
:- pred lambda__process_procs(pred_id, list(proc_id), module_info, module_info).
|
|
:- mode lambda__process_procs(in, in, in, out) is det.
|
|
|
|
lambda__process_procs(_PredId, [], ModuleInfo, ModuleInfo).
|
|
lambda__process_procs(PredId, [ProcId | ProcIds], ModuleInfo0, ModuleInfo) :-
|
|
lambda__process_proc(PredId, ProcId, ModuleInfo0, ModuleInfo1),
|
|
lambda__process_procs(PredId, ProcIds, ModuleInfo1, ModuleInfo).
|
|
|
|
:- pred lambda__process_proc(pred_id, proc_id, module_info, module_info).
|
|
:- mode lambda__process_proc(in, in, in, out) is det.
|
|
|
|
lambda__process_proc(PredId, ProcId, ModuleInfo0, ModuleInfo) :-
|
|
module_info_preds(ModuleInfo0, PredTable0),
|
|
map__lookup(PredTable0, PredId, PredInfo0),
|
|
pred_info_procedures(PredInfo0, ProcTable0),
|
|
map__lookup(ProcTable0, ProcId, ProcInfo0),
|
|
|
|
lambda__process_proc_2(ProcInfo0, PredInfo0, ModuleInfo0,
|
|
ProcInfo, PredInfo1, ModuleInfo1),
|
|
|
|
pred_info_procedures(PredInfo1, ProcTable1),
|
|
map__set(ProcTable1, ProcId, ProcInfo, ProcTable),
|
|
pred_info_set_procedures(PredInfo1, ProcTable, PredInfo),
|
|
module_info_preds(ModuleInfo1, PredTable1),
|
|
map__set(PredTable1, PredId, PredInfo, PredTable),
|
|
module_info_set_preds(ModuleInfo1, PredTable, ModuleInfo).
|
|
|
|
:- pred lambda__process_proc_2(proc_info, pred_info, module_info,
|
|
proc_info, pred_info, module_info).
|
|
:- mode lambda__process_proc_2(in, in, in, out, out, out) is det.
|
|
|
|
lambda__process_proc_2(ProcInfo0, PredInfo0, ModuleInfo0,
|
|
ProcInfo, PredInfo, ModuleInfo) :-
|
|
% grab the appropriate fields from the pred_info and proc_info
|
|
pred_info_typevarset(PredInfo0, TypeVarSet0),
|
|
proc_info_variables(ProcInfo0, VarSet0),
|
|
proc_info_vartypes(ProcInfo0, VarTypes0),
|
|
proc_info_goal(ProcInfo0, Goal0),
|
|
|
|
% process the goal
|
|
Info0 = lambda_info(VarSet0, VarTypes0, TypeVarSet0, ModuleInfo0),
|
|
lambda__process_goal(Goal0, Goal, Info0, Info),
|
|
Info = lambda_info(VarSet, VarTypes, TypeVarSet, ModuleInfo),
|
|
|
|
% set the new values of the fields in proc_info and pred_info
|
|
proc_info_set_goal(ProcInfo0, Goal, ProcInfo1),
|
|
proc_info_set_variables(ProcInfo1, VarSet, ProcInfo2),
|
|
proc_info_set_vartypes(ProcInfo2, VarTypes, ProcInfo),
|
|
pred_info_set_typevarset(PredInfo0, TypeVarSet, PredInfo).
|
|
|
|
:- pred lambda__process_goal(hlds__goal, hlds__goal,
|
|
lambda_info, lambda_info).
|
|
:- mode lambda__process_goal(in, out, in, out) is det.
|
|
|
|
lambda__process_goal(Goal0 - GoalInfo0, Goal) -->
|
|
lambda__process_goal_2(Goal0, GoalInfo0, Goal).
|
|
|
|
:- pred lambda__process_goal_2(hlds__goal_expr, hlds__goal_info,
|
|
hlds__goal, lambda_info, lambda_info).
|
|
:- mode lambda__process_goal_2(in, in, out, in, out) is det.
|
|
|
|
lambda__process_goal_2(unify(XVar, Y, Mode, Unification, Context), GoalInfo,
|
|
Unify - GoalInfo) -->
|
|
( { Y = lambda_goal(Vars, Modes, Det, LambdaGoal0) } ->
|
|
% for lambda expressions, we must convert the lambda expression
|
|
% into a new predicate
|
|
lambda__process_lambda(Vars, Modes, Det,
|
|
LambdaGoal0, Unification, Y1, Unification1),
|
|
{ Unify = unify(XVar, Y1, Mode, Unification1, Context) }
|
|
;
|
|
% ordinary unifications are left unchanged
|
|
{ Unify = unify(XVar, Y, Mode, Unification, Context) }
|
|
).
|
|
|
|
% the rest of the clauses just process goals recursively
|
|
|
|
lambda__process_goal_2(conj(Goals0), GoalInfo, conj(Goals) - GoalInfo) -->
|
|
lambda__process_goal_list(Goals0, Goals).
|
|
lambda__process_goal_2(disj(Goals0), GoalInfo, disj(Goals) - GoalInfo) -->
|
|
lambda__process_goal_list(Goals0, Goals).
|
|
lambda__process_goal_2(not(Goal0), GoalInfo, not(Goal) - GoalInfo) -->
|
|
lambda__process_goal(Goal0, Goal).
|
|
lambda__process_goal_2(switch(Var, CanFail, Cases0), GoalInfo,
|
|
switch(Var, CanFail, Cases) - GoalInfo) -->
|
|
lambda__process_cases(Cases0, Cases).
|
|
lambda__process_goal_2(some(Vars, Goal0), GoalInfo,
|
|
some(Vars, Goal) - GoalInfo) -->
|
|
lambda__process_goal(Goal0, Goal).
|
|
lambda__process_goal_2(if_then_else(Vars, A0, B0, C0), GoalInfo,
|
|
if_then_else(Vars, A, B, C) - GoalInfo) -->
|
|
lambda__process_goal(A0, A),
|
|
lambda__process_goal(B0, B),
|
|
lambda__process_goal(C0, C).
|
|
lambda__process_goal_2(call(A,B,C,D,E,F,G), GoalInfo,
|
|
call(A,B,C,D,E,F,G) - GoalInfo) -->
|
|
[].
|
|
lambda__process_goal_2(pragma_c_code(A,B,C,D,E), GoalInfo,
|
|
pragma_c_code(A,B,C,D,E) - GoalInfo) -->
|
|
[].
|
|
|
|
:- pred lambda__process_goal_list(list(hlds__goal), list(hlds__goal),
|
|
lambda_info, lambda_info).
|
|
:- mode lambda__process_goal_list(in, out, in, out) is det.
|
|
|
|
lambda__process_goal_list([], []) --> [].
|
|
lambda__process_goal_list([Goal0 | Goals0], [Goal | Goals]) -->
|
|
lambda__process_goal(Goal0, Goal),
|
|
lambda__process_goal_list(Goals0, Goals).
|
|
|
|
:- pred lambda__process_cases(list(case), list(case),
|
|
lambda_info, lambda_info).
|
|
:- mode lambda__process_cases(in, out, in, out) is det.
|
|
|
|
lambda__process_cases([], []) --> [].
|
|
lambda__process_cases([case(ConsId, Goal0) | Cases0],
|
|
[case(ConsId, Goal) | Cases]) -->
|
|
lambda__process_goal(Goal0, Goal),
|
|
lambda__process_cases(Cases0, Cases).
|
|
|
|
:- pred lambda__process_lambda(list(var), list(mode), determinism,
|
|
hlds__goal, unification, unify_rhs, unification,
|
|
lambda_info, lambda_info).
|
|
:- mode lambda__process_lambda(in, in, in, in, in, out, out,
|
|
in, out) is det.
|
|
|
|
lambda__process_lambda(Vars, Modes, Det, LambdaGoal,
|
|
Unification0, Functor, Unification, LambdaInfo0, LambdaInfo) :-
|
|
LambdaInfo0 = lambda_info(VarSet, VarTypes, TVarSet, ModuleInfo0),
|
|
lambda__transform_lambda(Vars, Modes, Det, LambdaGoal,
|
|
Unification0, VarSet, VarTypes, TVarSet, ModuleInfo0,
|
|
Functor, Unification, ModuleInfo),
|
|
LambdaInfo = lambda_info(VarSet, VarTypes, TVarSet, ModuleInfo).
|
|
|
|
lambda__transform_lambda(Vars, Modes, Det, LambdaGoal,
|
|
Unification0, VarSet, VarTypes, TVarSet, ModuleInfo0,
|
|
Functor, Unification, ModuleInfo) :-
|
|
|
|
%
|
|
% Optimize a special case: replace
|
|
% `lambda([Y1, Y2, ...] is Det, p(X1, X2, ..., Y1, Y2, ...))'
|
|
% where `p' has determinism `Det' with
|
|
% `p(X1, X2, ...)'
|
|
%
|
|
% XXX This optimization is only valid if the modes of the Xi are
|
|
% input, since only input arguments can be curried.
|
|
% Until this check is added, this optimization is incorrect,
|
|
% so I have disabled it - fjh.
|
|
|
|
LambdaGoal = _ - LambdaGoalInfo,
|
|
goal_info_get_nonlocals(LambdaGoalInfo, NonLocals0),
|
|
set__delete_list(NonLocals0, Vars, NonLocals),
|
|
set__to_sorted_list(NonLocals, ArgVars1),
|
|
(
|
|
/****************
|
|
XXX this optimization temporarily disabled, see comment above
|
|
|
|
LambdaGoal = call(PredId0, ModeId0, CallVars, _, _, PredName, _)
|
|
- _,
|
|
list__remove_suffix(CallVars, Vars, InitialVars),
|
|
|
|
module_info_pred_proc_info(ModuleInfo0, PredId0, ModeId0, _,
|
|
Call_ProcInfo),
|
|
proc_info_interface_code_model(Call_ProcInfo, Call_CodeModel),
|
|
determinism_to_code_model(Det, CodeModel),
|
|
% Check that the code models are compatible.
|
|
% Note that det is not compatible with semidet,
|
|
% and semidet is not compatible with nondet,
|
|
% since the arguments go in different registers.
|
|
% But det is compatible with nondet.
|
|
( CodeModel = Call_CodeModel
|
|
; CodeModel = model_non, Call_CodeModel = model_det
|
|
)
|
|
->
|
|
ArgVars = InitialVars,
|
|
PredId = PredId0,
|
|
ModeId = ModeId0,
|
|
unqualify_name(PredName, PName),
|
|
ModuleInfo = ModuleInfo0
|
|
;
|
|
****************/
|
|
% Prepare to create a new predicate for the lambda
|
|
% expression: work out the arguments, module name, predicate
|
|
% name, arity, arg types, determinism,
|
|
% context, status, etc. for the new predicate
|
|
|
|
ArgVars = ArgVars1,
|
|
list__append(ArgVars, Vars, AllArgVars),
|
|
|
|
module_info_name(ModuleInfo0, ModuleName),
|
|
module_info_next_lambda_count(ModuleInfo0, LambdaCount,
|
|
ModuleInfo1),
|
|
string__int_to_string(LambdaCount, LambdaCountStr),
|
|
string__append("__LambdaGoal__", LambdaCountStr, PName0),
|
|
string__append(ModuleName, PName0, PName),
|
|
PredName = unqualified(PName),
|
|
list__length(AllArgVars, Arity),
|
|
map__apply_to_list(AllArgVars, VarTypes, ArgTypes),
|
|
Cond = true,
|
|
goal_info_context(LambdaGoalInfo, LambdaContext),
|
|
Status = local,
|
|
MaybeDet = yes(Det),
|
|
% the TVarSet is a superset of what it really ought be,
|
|
% but that shouldn't matter
|
|
|
|
% For the non-local vars, we use the modes from `UniModes'.
|
|
% For the lambda var arguments at the end,
|
|
% we use the mode in the lambda expression.
|
|
(
|
|
Unification0 = construct(_, _, _, UniModes0)
|
|
->
|
|
UniModes = UniModes0
|
|
;
|
|
error("polymorphism__transform_lambda: wierd unification")
|
|
),
|
|
lambda__uni_modes_to_modes(UniModes, OrigArgModes),
|
|
list__append(OrigArgModes, Modes, AllArgModes),
|
|
|
|
%
|
|
% Now construct the pred_info for the new predicate, using
|
|
% the information computed above
|
|
%
|
|
clauses_info_init(Arity, ClausesInfo),
|
|
pred_info_init(ModuleName, PredName, Arity, TVarSet,
|
|
ArgTypes, Cond, LambdaContext, ClausesInfo, Status,
|
|
no, none, PredInfo0),
|
|
|
|
%
|
|
% Create a single mode for the new predicate, and insert
|
|
% the lambda goal as the body of that procedure.
|
|
%
|
|
pred_info_procedures(PredInfo0, Procs0),
|
|
next_mode_id(Procs0, MaybeDet, ModeId),
|
|
proc_info_init(Arity, AllArgModes, MaybeDet, LambdaContext,
|
|
ProcInfo0),
|
|
proc_info_set_body(ProcInfo0, VarSet, VarTypes, AllArgVars,
|
|
LambdaGoal, ProcInfo),
|
|
map__set(Procs0, ModeId, ProcInfo, Procs),
|
|
pred_info_set_procedures(PredInfo0, Procs, PredInfo),
|
|
|
|
%
|
|
% save the new predicate in the predicate table
|
|
%
|
|
module_info_get_predicate_table(ModuleInfo1, PredicateTable0),
|
|
predicate_table_insert(PredicateTable0, PredInfo,
|
|
PredId, PredicateTable),
|
|
module_info_set_predicate_table(ModuleInfo1, PredicateTable,
|
|
ModuleInfo)
|
|
),
|
|
Functor = functor(term_atom(PName), ArgVars),
|
|
ConsId = pred_const(PredId, ModeId),
|
|
Unification0 = construct(Var, _, _, ArgModes),
|
|
Unification = construct(Var, ConsId, ArgVars, ArgModes).
|
|
|
|
:- pred lambda__uni_modes_to_modes(list(uni_mode), list(mode)).
|
|
:- mode lambda__uni_modes_to_modes(in, out) is det.
|
|
|
|
% This predicate works out the modes of the original non-local
|
|
% variables of a lambda expression based on the list of uni_mode
|
|
% in the unify_info for the lambda unification.
|
|
|
|
lambda__uni_modes_to_modes([], []).
|
|
lambda__uni_modes_to_modes([UniMode | UniModes], [Mode | Modes]) :-
|
|
UniMode = ((_Initial0 - Initial1) -> (_Final0 - _Final1)),
|
|
Mode = (Initial1 -> Initial1),
|
|
lambda__uni_modes_to_modes(UniModes, Modes).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|