diff: Better error reporting for illegally bound vars
Andrew Bromage
bromage at cs.mu.OZ.AU
Tue Jan 27 18:36:15 AEDT 1998
G'day all.
Fergus, could you please review this? Thanks.
While you're at it, should the locked_vars type be made abstract?
Cheers,
Andrew Bromage
Estimated hours taken: 2
Better error reporting for variables bound in illegal places.
compiler/mode_info.m:
Add a var_lock_reason, specifying the reason why a variable
is locked during mode checking.
compiler/modecheck_unify.m:
compiler/modes.m:
compiler/unique_modes.m:
Specify the reason when locking some variables.
compiler/mode_errors.m:
When reporting var locking errors, give the reason why the
var was locked.
tests/invalid/Mmakefile:
tests/invalid/bind_var_errors.err_exp:
tests/invalid/bind_var_errors.m:
Regression tests for the above changes.
Index: compiler/mode_errors.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/mode_errors.m,v
retrieving revision 1.53
diff -u -r1.53 mode_errors.m
--- mode_errors.m 1998/01/23 12:56:46 1.53
+++ mode_errors.m 1998/01/27 07:21:53
@@ -65,7 +65,7 @@
; mode_error_no_matching_mode(list(var), list(inst))
% call to a predicate with an insufficiently
% instantiated variable (for preds with >1 mode)
- ; mode_error_bind_var(var, inst, inst)
+ ; mode_error_bind_var(var_lock_reason, var, inst, inst)
% attempt to bind a non-local variable inside
% a negated context
; mode_error_unify_var_var(var, var, inst, inst)
@@ -188,8 +188,8 @@
report_mode_error_implied_mode(ModeInfo, Var, InstA, InstB).
report_mode_error(mode_error_no_mode_decl, ModeInfo) -->
report_mode_error_no_mode_decl(ModeInfo).
-report_mode_error(mode_error_bind_var(Var, InstA, InstB), ModeInfo) -->
- report_mode_error_bind_var(ModeInfo, Var, InstA, InstB).
+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_unify_var_var(VarA, VarB, InstA, InstB),
ModeInfo) -->
report_mode_error_unify_var_var(ModeInfo, VarA, VarB, InstA, InstB).
@@ -367,18 +367,29 @@
%-----------------------------------------------------------------------------%
-:- pred report_mode_error_bind_var(mode_info, var, inst, inst,
+:- pred report_mode_error_bind_var(mode_info, var_lock_reason, var, inst, inst,
io__state, io__state).
-:- mode report_mode_error_bind_var(mode_info_ui, in, in, in, di, uo) is det.
+:- mode report_mode_error_bind_var(mode_info_ui, in, in, in, in, di, uo) is det.
-report_mode_error_bind_var(ModeInfo, Var, VarInst, Inst) -->
+report_mode_error_bind_var(ModeInfo, Reason, Var, VarInst, Inst) -->
{ 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(
- " scope error: attempt to bind variable inside a negation.\n"),
+ io__write_string(" scope error: "),
+ ( { Reason = negation },
+ io__write_string("attempt to bind a variable inside a negation.\n")
+ ; { Reason = if_then_else },
+ io__write_string("attempt to bind a non-local variable inside the\n"),
+ prog_out__write_context(Context),
+ io__write_string(" condition of an if-then-else.\n")
+ ; { Reason = lambda(PredOrFunc) },
+ { hlds_out__pred_or_func_to_str(PredOrFunc, PredOrFuncS) },
+ io__write_string("attempt to bind a non-local variable inside\n"),
+ prog_out__write_context(Context),
+ io__write_strings([" a ", PredOrFuncS, " lambda goal.\n"])
+ ),
prog_out__write_context(Context),
io__write_string(" Variable `"),
mercury_output_var(Var, VarSet, no),
@@ -391,12 +402,18 @@
io__write_string("'.\n"),
globals__io_lookup_bool_option(verbose_errors, VerboseErrors),
( { VerboseErrors = yes } ->
- io__write_string("\tA negation is only allowed to bind variables which are local to the\n"),
- io__write_string("\tnegation, i.e. those which are implicitly existentially quantified\n"),
- io__write_string("\tinside the scope of the negation.\n"),
- io__write_string("\tNote that the condition of an if-then-else is implicitly\n"),
- io__write_string("\tnegated in the ""else"" part, so the condition can only bind\n"),
- io__write_string("\tvariables in the ""then"" part.\n")
+ ( { Reason = negation },
+ io__write_string("\tA negation is only allowed to bind variables which are local to the\n"),
+ io__write_string("\tnegation, i.e. those which are implicitly existentially quantified\n"),
+ io__write_string("\tinside the scope of the negation.\n")
+ ; { Reason = if_then_else },
+ io__write_string("\tThe condition of an if-then-else is only allowed\n"),
+ io__write_string("\tto bind variables which are local to the condition\n"),
+ io__write_string("\tor which occur only in the condition and the `then' part.\n")
+ ; { Reason = lambda(_) },
+ io__write_string("\tA lambda goal is only allowed to bind its arguments\n"),
+ io__write_string("\tand variables local to the lambda expression.\n")
+ )
;
[]
).
Index: compiler/mode_info.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/mode_info.m,v
retrieving revision 1.43
diff -u -r1.43 mode_info.m
--- mode_info.m 1998/01/21 06:10:59 1.43
+++ mode_info.m 1998/01/27 07:00:52
@@ -56,6 +56,13 @@
; call(pred_id)
; higher_order_call(pred_or_func).
+:- type var_lock_reason
+ ---> negation
+ ; if_then_else
+ ; lambda(pred_or_func).
+
+:- type locked_vars == assoc_list(var_lock_reason, set(var)).
+
:- type mode_info.
:- pred mode_info_init(io__state, module_info, pred_id, proc_id,
@@ -119,10 +126,10 @@
:- pred mode_info_set_instmap(instmap, mode_info, mode_info).
:- mode mode_info_set_instmap(in, mode_info_di, mode_info_uo) is det.
-:- pred mode_info_get_locked_vars(mode_info, list(set(var))).
+:- pred mode_info_get_locked_vars(mode_info, locked_vars).
:- mode mode_info_get_locked_vars(mode_info_ui, out) is det.
-:- pred mode_info_set_locked_vars(mode_info, list(set(var)), mode_info).
+:- pred mode_info_set_locked_vars(mode_info, locked_vars, mode_info).
:- mode mode_info_set_locked_vars(mode_info_di, in, mode_info_uo) is det.
:- pred mode_info_get_errors(mode_info, list(mode_error_info)).
@@ -173,17 +180,17 @@
:- pred mode_info_get_types_of_vars(mode_info, list(var), list(type)).
:- mode mode_info_get_types_of_vars(mode_info_ui, in, out) is det.
-:- pred mode_info_lock_vars(set(var), mode_info, mode_info).
-:- mode mode_info_lock_vars(in, mode_info_di, mode_info_uo) is det.
+:- pred mode_info_lock_vars(var_lock_reason, set(var), mode_info, mode_info).
+:- mode mode_info_lock_vars(in, in, mode_info_di, mode_info_uo) is det.
-:- pred mode_info_unlock_vars(set(var), mode_info, mode_info).
-:- mode mode_info_unlock_vars(in, mode_info_di, mode_info_uo) is det.
+:- pred mode_info_unlock_vars(var_lock_reason, set(var), mode_info, mode_info).
+:- mode mode_info_unlock_vars(in, in, mode_info_di, mode_info_uo) is det.
-:- pred mode_info_var_is_locked(mode_info, var).
-:- mode mode_info_var_is_locked(mode_info_ui, in) is semidet.
+:- pred mode_info_var_is_locked(mode_info, var, var_lock_reason).
+:- mode mode_info_var_is_locked(mode_info_ui, in, out) is semidet.
-:- pred mode_info_var_is_locked_2(list(set(var)), var).
-:- mode mode_info_var_is_locked_2(in, in) is semidet.
+:- pred mode_info_var_is_locked_2(locked_vars, var, var_lock_reason).
+:- mode mode_info_var_is_locked_2(in, in, out) is semidet.
:- pred mode_info_get_delay_info(mode_info, delay_info).
:- mode mode_info_get_delay_info(mode_info_no_io, out) is det.
@@ -285,7 +292,7 @@
% goal the error occurred
instmap, % The current instantiatedness
% of the variables
- list(set(var)), % The "locked" variables,
+ locked_vars, % The "locked" variables,
% i.e. variables which cannot be
% further instantiated inside a
% negated context
@@ -633,30 +640,34 @@
% push them on the stack, and to unlock a set of vars, we just
% pop them off the stack. The stack is implemented as a list.
-mode_info_lock_vars(Vars, ModeInfo0, ModeInfo) :-
+mode_info_lock_vars(Reason, Vars, ModeInfo0, ModeInfo) :-
mode_info_get_locked_vars(ModeInfo0, LockedVars),
- mode_info_set_locked_vars(ModeInfo0, [Vars | LockedVars], ModeInfo).
+ mode_info_set_locked_vars(ModeInfo0, [Reason - Vars | LockedVars],
+ ModeInfo).
-mode_info_unlock_vars(_, ModeInfo0, ModeInfo) :-
+mode_info_unlock_vars(Reason, Vars, ModeInfo0, ModeInfo) :-
mode_info_get_locked_vars(ModeInfo0, LockedVars0),
- ( LockedVars0 = [_ | LockedVars1] ->
+ (
+ LockedVars0 = [Reason - TheseVars | LockedVars1],
+ set__equal(TheseVars, Vars)
+ ->
LockedVars = LockedVars1
;
- error("mode_info_unlock_vars: stack is empty")
+ error("mode_info_unlock_vars: some kind of nesting error")
),
mode_info_set_locked_vars(ModeInfo0, LockedVars, ModeInfo).
-mode_info_var_is_locked(ModeInfo, Var) :-
+mode_info_var_is_locked(ModeInfo, Var, Reason) :-
mode_info_get_locked_vars(ModeInfo, LockedVarsList),
- mode_info_var_is_locked_2(LockedVarsList, Var).
+ mode_info_var_is_locked_2(LockedVarsList, Var, Reason).
-mode_info_var_is_locked_2([Set | Sets], Var) :-
+mode_info_var_is_locked_2([ThisReason - Set | Sets], Var, Reason) :-
(
set__member(Var, Set)
->
- true
+ Reason = ThisReason
;
- mode_info_var_is_locked_2(Sets, Var)
+ mode_info_var_is_locked_2(Sets, Var, Reason)
).
mode_info_get_delay_info(mode_info(_,_,_,_,_,_,_,_,_,_,DelayInfo,_,_,_,_,_),
Index: compiler/modecheck_unify.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/modecheck_unify.m,v
retrieving revision 1.26
diff -u -r1.26 modecheck_unify.m
--- modecheck_unify.m 1998/01/16 06:44:39 1.26
+++ modecheck_unify.m 1998/01/27 06:21:20
@@ -384,7 +384,8 @@
Goal0 = _ - GoalInfo0,
goal_info_get_nonlocals(GoalInfo0, NonLocals0),
set__delete_list(NonLocals0, Vars, NonLocals),
- mode_info_lock_vars(NonLocals, ModeInfo2, ModeInfo3),
+ 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
@@ -399,7 +400,8 @@
mode_checkpoint(exit, "lambda goal", ModeInfo6, ModeInfo7),
mode_info_remove_live_vars(LiveVars, ModeInfo7, ModeInfo8),
- mode_info_unlock_vars(NonLocals, ModeInfo8, ModeInfo9),
+ mode_info_unlock_vars(lambda(PredOrFunc), NonLocals,
+ ModeInfo8, ModeInfo9),
mode_info_set_instmap(InstMap0, ModeInfo9, ModeInfo10),
%
Index: compiler/modes.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/modes.m,v
retrieving revision 1.217
diff -u -r1.217 modes.m
--- modes.m 1998/01/24 05:44:21 1.217
+++ modes.m 1998/01/27 06:35:01
@@ -894,11 +894,11 @@
{ goal_info_get_nonlocals(GoalInfo0, NonLocals) },
{ goal_get_nonlocals(B0, B_Vars) },
mode_info_dcg_get_instmap(InstMap0),
- mode_info_lock_vars(NonLocals),
+ mode_info_lock_vars(if_then_else, NonLocals),
mode_info_add_live_vars(B_Vars),
modecheck_goal(A0, A),
mode_info_remove_live_vars(B_Vars),
- mode_info_unlock_vars(NonLocals),
+ mode_info_unlock_vars(if_then_else, NonLocals),
modecheck_goal(B0, B),
mode_info_dcg_get_instmap(InstMapB),
mode_info_set_instmap(InstMap0),
@@ -913,9 +913,9 @@
mode_checkpoint(enter, "not"),
{ goal_info_get_nonlocals(GoalInfo0, NonLocals) },
mode_info_dcg_get_instmap(InstMap0),
- mode_info_lock_vars(NonLocals),
+ mode_info_lock_vars(negation, NonLocals),
modecheck_goal(A0, A),
- mode_info_unlock_vars(NonLocals),
+ mode_info_unlock_vars(negation, NonLocals),
mode_info_set_instmap(InstMap0),
mode_checkpoint(exit, "not").
@@ -1582,12 +1582,12 @@
;
% We've bound part of the var. If the var was locked,
% then we need to report an error.
- mode_info_var_is_locked(ModeInfo1, Var0)
+ mode_info_var_is_locked(ModeInfo1, Var0, Reason0)
->
set__singleton_set(WaitingVars, Var0),
mode_info_error(WaitingVars,
- mode_error_bind_var(Var0, Inst0, Inst),
- ModeInfo1, ModeInfo
+ mode_error_bind_var(Reason0, Var0, Inst0, Inst),
+ ModeInfo1, ModeInfo
)
;
instmap__set(InstMap0, Var0, Inst, InstMap),
Index: compiler/unique_modes.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/compiler/unique_modes.m,v
retrieving revision 1.44
diff -u -r1.44 unique_modes.m
--- unique_modes.m 1998/01/13 10:13:55 1.44
+++ unique_modes.m 1998/01/27 06:35:10
@@ -294,7 +294,7 @@
{ unique_modes__goal_get_nonlocals(B0, B_Vars) },
{ unique_modes__goal_get_nonlocals(C0, C_Vars) },
mode_info_dcg_get_instmap(InstMap0),
- mode_info_lock_vars(NonLocals),
+ mode_info_lock_vars(if_then_else, NonLocals),
%
% At this point, we should set the inst of any `unique'
@@ -320,7 +320,7 @@
mode_info_add_live_vars(B_Vars),
unique_modes__check_goal(A0, A),
mode_info_remove_live_vars(B_Vars),
- mode_info_unlock_vars(NonLocals),
+ mode_info_unlock_vars(if_then_else, NonLocals),
% mode_info_dcg_get_instmap(InstMapA),
unique_modes__check_goal(B0, B),
mode_info_dcg_get_instmap(InstMapB),
@@ -340,9 +340,9 @@
=(ModeInfo),
{ select_live_vars(NonLocalsList, ModeInfo, LiveNonLocals) },
make_var_list_mostly_uniq(LiveNonLocals),
- mode_info_lock_vars(NonLocals),
+ mode_info_lock_vars(negation, NonLocals),
unique_modes__check_goal(A0, A),
- mode_info_unlock_vars(NonLocals),
+ mode_info_unlock_vars(negation, NonLocals),
mode_info_set_instmap(InstMap0),
mode_checkpoint(exit, "not").
Index: tests/invalid/Mmakefile
===================================================================
RCS file: /home/staff/zs/imp/tests/invalid/Mmakefile,v
retrieving revision 1.9
diff -u -r1.9 Mmakefile
--- Mmakefile 1998/01/12 13:30:00 1.9
+++ Mmakefile 1998/01/27 07:20:07
@@ -9,6 +9,7 @@
SOURCES= \
any_mode.m \
bigtest.m \
+ bind_var_errors.m \
circ_type.m \
constructor_warning.m \
det_errors.m \
New File: tests/invalid/bind_var_errors.err_exp
===================================================================
bind_var_errors.m:031: In clause for `bind_var_in_negation':
bind_var_errors.m:031: scope error: attempt to bind a variable inside a negation.
bind_var_errors.m:031: Variable `X' has instantiatedness `free',
bind_var_errors.m:031: expected instantiatedness was `unique(42)'.
bind_var_errors.m:037: In clause for `bind_var_in_ite_cond(in)':
bind_var_errors.m:037: scope error: attempt to bind a non-local variable inside the
bind_var_errors.m:037: condition of an if-then-else.
bind_var_errors.m:037: Variable `Y' has instantiatedness `free',
bind_var_errors.m:037: expected instantiatedness was `unique(42)'.
bind_var_errors.m:046: In clause for `bind_var_in_lambda':
bind_var_errors.m:046: scope error: attempt to bind a non-local variable inside
bind_var_errors.m:046: a pred lambda goal.
bind_var_errors.m:046: Variable `Y' has instantiatedness `free',
bind_var_errors.m:046: expected instantiatedness was `unique(42)'.
bind_var_errors.m:056: In clause for `clobber_var_in_lambda(di)':
bind_var_errors.m:056: in argument 1 of call to predicate `bind_var_errors:destroy/1':
bind_var_errors.m:056: unique-mode error: the called procedure would clobber
bind_var_errors.m:056: its argument, but variable `X' is still live.
For more information, try recompiling with `-E'.
New File: 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 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).
% The compiler currently does not pass this test. It should
% report an error but doesn't.
% share_var_in_lambda(X) :-
% call((pred) is det :- share(X)),
% destroy(X).
clobber_var_in_lambda(X) :-
call((pred) is det :- destroy(X)),
destroy(X).
More information about the developers
mailing list