diff: Fix bug in modechecking of non-local lambda vars
Andrew Bromage
bromage at cs.mu.OZ.AU
Fri Feb 6 11:50:07 AEDT 1998
G'day all.
Fergus, could you please review this? Thanks.
Note: In this diff, the regression test files are included in full
since the diff is a bit confusing.
Cheers,
Andrew Bromage
Estimated hours taken: 6
Fix a semantic hole. Previously, unique variables could be used
as non-local variables inside a lambda goal and shared within that
lambda. This fixes the problem by requiring that all non-local
vars to a lambda must be ground-shared.
compiler/modecheck_unify.m:
Changes detailed above.
compiler/mode_errors.m:
Add a new kind of mode error, mode_error_non_local_lambda_var.
One small change to find_important_errors/3 to ensure that this
error will be reported if the lambda is an implicit call argument
unification.
compiler/inst_util.m:
Export make_shared_inst_list/4.
compiler/instmap.m:
New predicate: instmap__set_vars/4, which sets multiple vars
in an instmap.
tests/invalid/bind_var_errors.m:
tests/invalid/bind_var_errors.err_exp:
Regression test.
Index: compiler/inst_util.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/inst_util.m,v
retrieving revision 1.9
diff -u -r1.9 inst_util.m
--- inst_util.m 1998/01/23 12:56:36 1.9
+++ inst_util.m 1998/02/05 05:09:37
@@ -75,6 +75,14 @@
% original inst but with all occurrences of `unique' replaced
% with `mostly_unique'.
+:- pred make_shared_inst_list(list(inst), module_info, list(inst), module_info).
+:- mode make_shared_inst_list(in, in, out, out) is det.
+
+ % Given a list of insts, return a new list of insts which is the
+ % same as the original list of insts, but with all occurrences
+ % of `unique' replaced with `shared'. It is an error if any part
+ % of the inst list is free.
+
%-----------------------------------------------------------------------------%
:- pred inst_merge(inst, inst, module_info, inst, module_info).
@@ -970,9 +978,6 @@
maybe_make_shared_inst_list([_|_], [], _, _, _) :-
error("maybe_make_shared_inst_list: length mismatch").
-:- pred make_shared_inst_list(list(inst), module_info,
- list(inst), module_info).
-:- mode make_shared_inst_list(in, in, out, out) is det.
make_shared_inst_list([], ModuleInfo, [], ModuleInfo).
make_shared_inst_list([Inst0 | Insts0], ModuleInfo0,
Index: compiler/instmap.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/instmap.m,v
retrieving revision 1.18
diff -u -r1.18 instmap.m
--- instmap.m 1998/01/23 12:56:37 1.18
+++ instmap.m 1998/02/03 12:47:18
@@ -133,6 +133,11 @@
:- pred instmap__set(instmap, var, inst, instmap).
:- mode instmap__set(in, in, in, out) is det.
+ % Set multiple entries in an instmap.
+ %
+:- pred instmap__set_vars(instmap, list(var), list(inst), instmap).
+:- mode instmap__set_vars(in, in, in, out) is det.
+
:- pred instmap_delta_set(instmap_delta, var, inst, instmap_delta).
:- mode instmap_delta_set(in, in, in, out) is det.
@@ -376,6 +381,15 @@
instmap__set(reachable(InstMapping0), Var, Inst,
reachable(InstMapping)) :-
map__set(InstMapping0, Var, Inst, InstMapping).
+
+instmap__set_vars(InstMap, [], [], InstMap).
+instmap__set_vars(InstMap0, [V | Vs], [I | Is], InstMap) :-
+ instmap__set(InstMap0, V, I, InstMap1),
+ instmap__set_vars(InstMap1, Vs, Is, InstMap).
+instmap__set_vars(_, [_ | _], [], _) :-
+ error("instmap__set_vars").
+instmap__set_vars(_, [], [_ | _], _) :-
+ error("instmap__set_vars").
instmap_delta_set(unreachable, _Var, _Inst, unreachable).
instmap_delta_set(reachable(InstMapping0), Var, Inst, Instmap) :-
Index: compiler/mode_errors.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/mode_errors.m,v
retrieving revision 1.54
diff -u -r1.54 mode_errors.m
--- mode_errors.m 1998/01/30 06:12:50 1.54
+++ mode_errors.m 1998/02/05 07:01:26
@@ -68,6 +68,9 @@
; mode_error_bind_var(var_lock_reason, var, inst, inst)
% attempt to bind a non-local variable inside
% a negated context
+ ; mode_error_non_local_lambda_var(var, inst)
+ % attempt to pass a live non-ground var as a
+ % non-local variable to a lambda goal
; mode_error_unify_var_var(var, var, inst, inst)
% attempt to unify two free variables
; mode_error_unify_var_functor(var, cons_id, list(var),
@@ -190,6 +193,8 @@
report_mode_error_no_mode_decl(ModeInfo).
report_mode_error(mode_error_bind_var(Reason, Var, InstA, InstB), ModeInfo) -->
report_mode_error_bind_var(ModeInfo, Reason, Var, InstA, InstB).
+report_mode_error(mode_error_non_local_lambda_var(Var, Inst), ModeInfo) -->
+ report_mode_error_non_local_lambda_var(ModeInfo, Var, Inst).
report_mode_error(mode_error_unify_var_var(VarA, VarB, InstA, InstB),
ModeInfo) -->
report_mode_error_unify_var_var(ModeInfo, VarA, VarB, InstA, InstB).
@@ -280,12 +285,17 @@
find_important_errors([], [], []).
find_important_errors([Error | Errors], ImportantErrors, OtherErrors) :-
- Error = delayed_goal(_, mode_error_info(_, _, _, ModeContext), _),
+ Error = delayed_goal(_, mode_error_info(_, ModeError, _, ModeContext),
+ _),
(
% an error is important unless it is a non-explicit unification,
% i.e. a head unification or a call argument unification
ModeContext = unify(unify_context(UnifyContext, _), _),
- UnifyContext \= explicit
+ UnifyContext \= explicit,
+ % except that errors in lambda goals are important even
+ % if the unification that creates the lambda goal is
+ % an implicit one
+ ModeError \= mode_error_non_local_lambda_var(_, _)
->
ImportantErrors1 = ImportantErrors,
OtherErrors = [Error | OtherErrors1]
@@ -417,6 +427,28 @@
;
[]
).
+
+%-----------------------------------------------------------------------------%
+
+:- pred report_mode_error_non_local_lambda_var(mode_info, var, inst,
+ io__state, io__state).
+:- mode report_mode_error_non_local_lambda_var(mode_info_ui, in, in,
+ di, uo) is det.
+
+report_mode_error_non_local_lambda_var(ModeInfo, Var, VarInst) -->
+ { mode_info_get_context(ModeInfo, Context) },
+ { mode_info_get_varset(ModeInfo, VarSet) },
+ { mode_info_get_instvarset(ModeInfo, InstVarSet) },
+ mode_info_write_context(ModeInfo),
+ prog_out__write_context(Context),
+ io__write_string(" mode error: variable `"),
+ mercury_output_var(Var, VarSet, no),
+ io__write_string("' has instantiatedness `"),
+ output_inst(VarInst, InstVarSet),
+ io__write_string("',\n"),
+ prog_out__write_context(Context),
+ io__write_string(" expected instantiatedness for non-local variables of lambda goals\n"),
+ io__write_string(" is `ground'.\n").
%-----------------------------------------------------------------------------%
Index: compiler/modecheck_unify.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/modecheck_unify.m,v
retrieving revision 1.27
diff -u -r1.27 modecheck_unify.m
--- modecheck_unify.m 1998/01/30 06:12:52 1.27
+++ modecheck_unify.m 1998/02/06 00:16:48
@@ -334,6 +334,7 @@
% First modecheck the lambda goal itself:
%
% initialize the initial insts of the lambda variables,
+ % check that the non-local vars are ground,
% lock the non-local vars,
% mark the non-clobbered lambda variables as live,
% modecheck the goal,
@@ -384,35 +385,94 @@
Goal0 = _ - GoalInfo0,
goal_info_get_nonlocals(GoalInfo0, NonLocals0),
set__delete_list(NonLocals0, Vars, NonLocals),
- mode_info_lock_vars(lambda(PredOrFunc), NonLocals,
- ModeInfo2, ModeInfo3),
-
- mode_checkpoint(enter, "lambda goal", ModeInfo3, ModeInfo4),
- % 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, ModeInfo4, ModeInfo5)
+ 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
+ % if 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.
+
+ inst_list_is_ground(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.
+ %
+
+ set__to_sorted_list(NonLocals, ArgVars),
+ modecheck_unify_lambda(X, PredOrFunc, ArgVars, Modes,
+ Det, Unification0, Mode, Unification,
+ ModeInfo12, ModeInfo),
+ RHS = lambda_goal(PredOrFunc, Vars, Modes, Det, Goal)
;
- modecheck_goal(Goal0, Goal, ModeInfo4, ModeInfo5)
- ),
- mode_list_get_final_insts(Modes, ModuleInfo0, FinalInsts),
- modecheck_final_insts(Vars, FinalInsts, ModeInfo5, ModeInfo6),
- mode_checkpoint(exit, "lambda goal", ModeInfo6, ModeInfo7),
-
- mode_info_remove_live_vars(LiveVars, ModeInfo7, ModeInfo8),
- mode_info_unlock_vars(lambda(PredOrFunc), NonLocals,
- ModeInfo8, ModeInfo9),
- mode_info_set_instmap(InstMap0, ModeInfo9, ModeInfo10),
-
- %
- % Now modecheck the unification of X with the lambda-expression.
- %
+ 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, Vars, Modes0, Det, Goal0),
+ Mode = (free -> free) - (free -> free),
+ Unification = Unification0
+ ).
- set__to_sorted_list(NonLocals, ArgVars),
- modecheck_unify_lambda(X, PredOrFunc, ArgVars, Modes,
- Det, Unification0, Mode, Unification,
- ModeInfo10, ModeInfo),
- RHS = lambda_goal(PredOrFunc, Vars, Modes, Det, Goal).
:- pred modecheck_unify_lambda(var, pred_or_func, list(var),
list(mode), determinism, unification,
Index: tests/invalid/bind_var_errors.m
===================================================================
:- module bind_var_errors.
:- interface.
:- import_module int.
:- pred bind_var_in_negation is semidet.
:- pred bind_var_in_ite_cond(int :: in) is semidet.
:- pred bind_var_in_lambda is semidet.
:- pred share_var_in_lambda(T :: di) is det.
:- pred share_dead_var_in_lambda(T :: di) is det.
:- pred clobber_var_in_lambda(T :: di) is det.
:- implementation.
:- pragma no_inline(consume/1).
:- pred consume(T :: in) is det.
consume(_).
:- pragma no_inline(destroy/1).
:- pred destroy(T :: di) is det.
destroy(_).
:- pragma no_inline(share/1).
:- pred share(T :: in) is det.
share(_).
bind_var_in_negation :-
\+ (X = 42),
consume(X).
bind_var_in_ite_cond(X) :-
(
X = 42,
Y = 42
->
true
;
true
),
consume(Y).
bind_var_in_lambda :-
call((pred) is det :- Y = 42),
consume(Y).
share_var_in_lambda(X) :-
call((pred) is det :- share(X)),
destroy(X).
% This one is OK since X is dead after the lambda.
share_dead_var_in_lambda(X) :-
call((pred) is det :- share(X)).
clobber_var_in_lambda(X) :-
call((pred) is det :- destroy(X)),
destroy(X).
Index: tests/invalid/bind_var_errors.err_exp
===================================================================
bind_var_errors.m:033: In clause for `bind_var_in_negation':
bind_var_errors.m:033: scope error: attempt to bind a variable inside a negation.
bind_var_errors.m:033: Variable `X' has instantiatedness `free',
bind_var_errors.m:033: expected instantiatedness was `unique(42)'.
bind_var_errors.m:039: In clause for `bind_var_in_ite_cond(in)':
bind_var_errors.m:039: scope error: attempt to bind a non-local variable inside the
bind_var_errors.m:039: condition of an if-then-else.
bind_var_errors.m:039: Variable `Y' has instantiatedness `free',
bind_var_errors.m:039: expected instantiatedness was `unique(42)'.
bind_var_errors.m:048: In clause for `bind_var_in_lambda':
bind_var_errors.m:048: in argument 1 of call to predicate `call/1':
bind_var_errors.m:048: mode error: variable `Y' has instantiatedness `free',
bind_var_errors.m:048: expected instantiatedness for non-local variables of lambda goals
is `ground'.
bind_var_errors.m:053: In clause for `share_var_in_lambda(di)':
bind_var_errors.m:053: in argument 1 of call to predicate `bind_var_errors:destroy/1':
bind_var_errors.m:053: mode error: variable `X' has instantiatedness `ground',
bind_var_errors.m:053: expected instantiatedness was `unique'.
bind_var_errors.m:060: In clause for `clobber_var_in_lambda(di)':
bind_var_errors.m:060: in argument 1 of call to predicate `bind_var_errors:destroy/1':
bind_var_errors.m:060: unique-mode error: the called procedure would clobber
bind_var_errors.m:060: its argument, but variable `X' is still live.
For more information, try recompiling with `-E'.
More information about the developers
mailing list