[m-rev.] For review: change the way we handle inst any non-locals in negated contexts (again)
Ralph Becket
rafe at cs.mu.OZ.AU
Tue Dec 13 19:11:09 AEDT 2005
Estimated hours taken: 16
Branches: main
Undo my recent changes to purity error checking in the context of inst any
non-locals in negated contexts.
Implement a better way of handling the problem, as discussed on the mailing
list. The new solution is to require that any goals featuring inst any
non-locals in a negated context must appear in a
promise_{pure,semipure,impure} context. This is something of a compromise:
on the one hand it does require that the condition be explicitly recognised
by the programmer; on the other, it does not require that the "offending" goals
be individually identified (this is partly for pragmatic reasons: the earlier
approach required a plethora of awkward impurity declarations on goals that
would otherwise be considered completely pure).
compiler/mode_errors.m:
Remove purity_error_should_be_impure and purity_error_wrongly_impure
data constructors; add purity_error_should_be_in_promise_purity_scope.
compiler/mode_info.m:
Replace the in_negated_context field with the in_promise_purity_scope
field.
compiler/modecheck_call.m:
compiler/modecheck_unify.m:
Back out my previous change.
compiler/modes.m:
Record a purity error if a non-local inst any variable appears
in a negation or the condition of an if-then-else goal.
compiler/purity.m:
compiler/unique_modes.m:
Back out my previous change.
doc/reference_manual.texi:
Document the new purity rules for inst any non-locals in negated
contexts.
tests/debugger/solver_test.m:
tests/hard_coded/any_free_unify.m:
tests/invalid/any_passed_as_ground.m:
tests/invalid/any_to_ground_in_ite_cond.m:
tests/invalid/anys_in_negated_contexts.err_exp:
tests/invalid/anys_in_negated_contexts.m:
tests/invalid/purity/impure_func_t7.err_exp:
tests/invalid/purity/impure_func_t7.m:
Fix up error cases to use the new syntax.
Index: compiler/mode_errors.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/mode_errors.m,v
retrieving revision 1.96
diff -u -r1.96 mode_errors.m
--- compiler/mode_errors.m 28 Nov 2005 04:11:48 -0000 1.96
+++ compiler/mode_errors.m 13 Dec 2005 01:23:09 -0000
@@ -128,13 +128,11 @@
% One of the head variables did not have the expected final inst
% on exit from the proc.
- ; purity_error_should_be_impure(list(prog_var))
- % A goal in a negated context contains vars with inst any was
- % not marked impure, as it should be.
-
- ; purity_error_wrongly_impure(purity)
- % A goal in a negated context was erroneously marked as impure.
- % The purity argument specifies what purity the goal has.
+ ; purity_error_should_be_in_promise_purity_scope(
+ negated_context_desc, prog_var)
+ % The condition of an if-then-else or the body of a negation
+ % contained an inst any non-local, but was not inside a
+ % promise_purity scope.
; purity_error_lambda_should_be_impure(list(prog_var)).
% A lambda term containing inst any non-locals should have been
@@ -142,6 +140,10 @@
% further constrain the inst any variables, thereby violating
% referential transparency).
+:- type negated_context_desc
+ ---> if_then_else
+ ; negation.
+
:- type schedule_culprit
---> goal_itself_was_impure
; goals_followed_by_impure_goal(hlds_goal)
@@ -344,11 +346,10 @@
Specs = mode_error_final_inst_to_specs(ModeInfo, ArgNum, Var, VarInst,
Inst, Reason)
;
- ModeError = purity_error_should_be_impure(Vars),
- Specs = purity_error_should_be_impure_to_specs(ModeInfo, Vars)
- ;
- ModeError = purity_error_wrongly_impure(Purity),
- Specs = purity_error_wrongly_impure_to_specs(ModeInfo, Purity)
+ ModeError = purity_error_should_be_in_promise_purity_scope(NegCtxt,
+ Var),
+ Specs = purity_error_should_be_in_promise_purity_scope_to_specs(
+ NegCtxt, ModeInfo, Var)
;
ModeError = purity_error_lambda_should_be_impure(Vars),
Specs = purity_error_lambda_should_be_impure_to_specs(ModeInfo, Vars)
@@ -1092,35 +1093,33 @@
%-----------------------------------------------------------------------------%
-:- func purity_error_should_be_impure_to_specs(mode_info::in,
- list(prog_var)::in) = (list(error_msg_spec)::out(error_msg_specs))
- is det.
-
-purity_error_should_be_impure_to_specs(ModeInfo, Vars) = Specs :-
- mode_info_get_context(ModeInfo, Context),
- mode_info_get_varset(ModeInfo, VarSet),
- Pieces = [
- words("purity error: goal should be impure because it appears"),
- words("in a negated context, but involves the following variables"),
- words("whose insts contain `any':"),
- words(mercury_vars_to_string(Vars, VarSet, no))
- ],
- Specs = [
- mode_info_context_to_spec(ModeInfo),
- error_msg_spec(no, Context, 0, Pieces)
- ].
-
-%-----------------------------------------------------------------------------%
-
-:- func purity_error_wrongly_impure_to_specs(mode_info::in, purity::in) =
+:- func purity_error_should_be_in_promise_purity_scope_to_specs(
+ negated_context_desc::in, mode_info::in, prog_var::in) =
(list(error_msg_spec)::out(error_msg_specs)) is det.
-purity_error_wrongly_impure_to_specs(ModeInfo, Purity) = Specs :-
+purity_error_should_be_in_promise_purity_scope_to_specs(NegCtxtDesc,
+ ModeInfo, Var) = Specs :-
mode_info_get_context(ModeInfo, Context),
- Pieces = [
- words("purity error: goal is marked as impure, but is actually"),
- words(if Purity = purity_pure then "pure" else "semipure")
- ],
+ mode_info_get_varset(ModeInfo, VarSet),
+ (
+ NegCtxtDesc = if_then_else,
+ Pieces = [
+ words("purity error: if-then-else"),
+ words("should be inside a promise_purity"),
+ words("scope because non-local variable"),
+ words(mercury_var_to_string(Var, VarSet, no)),
+ words("has inst any and appears in the condition.")
+ ]
+ ;
+ NegCtxtDesc = negation,
+ Pieces = [
+ words("purity error: negation"),
+ words("should be inside a promise_purity"),
+ words("scope because non-local variable"),
+ words(mercury_var_to_string(Var, VarSet, no)),
+ words("has inst any and appears in the body.")
+ ]
+ ),
Specs = [
mode_info_context_to_spec(ModeInfo),
error_msg_spec(no, Context, 0, Pieces)
@@ -1139,7 +1138,8 @@
words("purity error: lambda should be impure because it"),
words("contains the following non-local variables"),
words("whose insts contain `any':"),
- words(mercury_vars_to_string(Vars, VarSet, no))
+ words(mercury_vars_to_string(Vars, VarSet, no)),
+ suffix("."), nl
],
Specs = [
mode_info_context_to_spec(ModeInfo),
Index: compiler/mode_info.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/mode_info.m,v
retrieving revision 1.79
diff -u -r1.79 mode_info.m
--- compiler/mode_info.m 28 Nov 2005 04:11:49 -0000 1.79
+++ compiler/mode_info.m 9 Dec 2005 00:04:57 -0000
@@ -120,7 +120,7 @@
:- pred mode_info_get_warnings(mode_info::in, list(mode_warning_info)::out)
is det.
:- pred mode_info_get_need_to_requantify(mode_info::in, bool::out) is det.
-:- pred mode_info_get_in_negated_context(mode_info::in, bool::out) is det.
+:- pred mode_info_get_in_promise_purity_scope(mode_info::in, bool::out) is det.
:- pred mode_info_get_num_errors(mode_info::in, int::out) is det.
:- pred mode_info_get_liveness(mode_info::in, set(prog_var)::out) is det.
:- pred mode_info_get_varset(mode_info::in, prog_varset::out) is det.
@@ -169,7 +169,7 @@
mode_info::in, mode_info::out) is det.
:- pred mode_info_set_need_to_requantify(bool::in,
mode_info::in, mode_info::out) is det.
-:- pred mode_info_set_in_negated_context(bool::in,
+:- pred mode_info_set_in_promise_purity_scope(bool::in,
mode_info::in, mode_info::out) is det.
:- pred mode_info_add_live_vars(set(prog_var)::in,
mode_info::in, mode_info::out) is det.
@@ -320,8 +320,11 @@
% after mode analysis finishes.
need_to_requantify :: bool,
- % Set to `yes' if we are in a negated context.
- in_negated_context :: bool
+ % Set to `yes' if we are in a promise_<purity> scope. This
+ % information is needed to check that potentially impure
+ % uses of inst any non-locals in negated contexts are
+ % properly acknowledged by the programmer.
+ in_promise_purity_scope :: bool
).
:- type mode_info
@@ -473,7 +476,8 @@
mode_info_get_errors(MI, MI ^ errors).
mode_info_get_warnings(MI, MI ^ mode_sub_info ^ warnings).
mode_info_get_need_to_requantify(MI, MI ^ mode_sub_info ^ need_to_requantify).
-mode_info_get_in_negated_context(MI, MI ^ mode_sub_info ^ in_negated_context).
+mode_info_get_in_promise_purity_scope(MI,
+ MI ^ mode_sub_info ^ in_promise_purity_scope).
mode_info_get_delay_info(MI, MI ^ delay_info).
mode_info_get_live_vars(MI, MI ^ live_vars).
mode_info_get_nondet_live_vars(MI, MI ^ nondet_live_vars).
@@ -498,8 +502,8 @@
MI ^ mode_sub_info ^ warnings := Warnings).
mode_info_set_need_to_requantify(NTRQ, MI,
MI ^ mode_sub_info ^ need_to_requantify := NTRQ).
-mode_info_set_in_negated_context(INC, MI,
- MI ^ mode_sub_info ^ in_negated_context := INC).
+mode_info_set_in_promise_purity_scope(INC, MI,
+ MI ^ mode_sub_info ^ in_promise_purity_scope := INC).
mode_info_set_delay_info(DelayInfo, MI, MI ^ delay_info := DelayInfo).
mode_info_set_live_vars(LiveVarsList, MI, MI ^ live_vars := LiveVarsList).
mode_info_set_nondet_live_vars(NondetLiveVars, MI,
Index: compiler/modecheck_call.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/modecheck_call.m,v
retrieving revision 1.64
diff -u -r1.64 modecheck_call.m
--- compiler/modecheck_call.m 28 Nov 2005 04:11:49 -0000 1.64
+++ compiler/modecheck_call.m 9 Dec 2005 02:21:14 -0000
@@ -105,7 +105,7 @@
%-----------------------------------------------------------------------------%
modecheck_call_pred(PredId, DeterminismKnown, ProcId0, TheProcId,
- ArgVars0, ArgVars, GoalInfo, ExtraGoals, !ModeInfo) :-
+ ArgVars0, ArgVars, _GoalInfo, ExtraGoals, !ModeInfo) :-
mode_info_get_may_change_called_proc(!.ModeInfo, MayChangeCalledProc),
mode_info_get_preds(!.ModeInfo, Preds),
mode_info_get_module_info(!.ModeInfo, ModuleInfo),
@@ -125,42 +125,8 @@
compute_arg_offset(PredInfo, ArgOffset),
pred_info_get_markers(PredInfo, Markers),
-
mode_info_get_instmap(!.ModeInfo, InstMap),
- AnyVars =
- list__filter(var_inst_contains_any(ModuleInfo, InstMap), ArgVars0),
-
(
- % If we are in a negated context and one or more vars have
- % inst any then this call is required to be marked impure
- % since it may violate referential transparency.
- %
- mode_info_get_in_negated_context(!.ModeInfo, yes),
- \+ goal_info_is_impure(GoalInfo),
- AnyVars \= []
- ->
- set__init(WaitingVars),
- mode_info_error(WaitingVars, purity_error_should_be_impure(AnyVars),
- !ModeInfo),
- TheProcId = invalid_proc_id,
- ArgVars = ArgVars0,
- ExtraGoals = no_extra_goals
- ;
- % Report a purity error if a pred call is erroneously marked as impure
- % in a negated context.
- %
- mode_info_get_in_negated_context(!.ModeInfo, yes),
- goal_info_is_impure(GoalInfo),
- AnyVars = [],
- Purity \= purity_impure
- ->
- set__init(WaitingVars),
- mode_info_error(WaitingVars, purity_error_wrongly_impure(Purity),
- !ModeInfo),
- TheProcId = invalid_proc_id,
- ArgVars = ArgVars0,
- ExtraGoals = no_extra_goals
- ;
% In order to give better diagnostics, we handle the cases where there
% are zero or one modes for the called predicate specially.
%
Index: compiler/modecheck_unify.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/modecheck_unify.m,v
retrieving revision 1.94
diff -u -r1.94 modecheck_unify.m
--- compiler/modecheck_unify.m 28 Nov 2005 04:11:49 -0000 1.94
+++ compiler/modecheck_unify.m 9 Dec 2005 01:39:00 -0000
@@ -98,58 +98,6 @@
modecheck_unification(X, RHS, Unification0, UnifyContext, UnifyGoalInfo0,
Unify, !ModeInfo, !IO) :-
(
- mode_info_get_in_negated_context(!.ModeInfo, yes),
- (
- RHS = var(Y),
- Vars = [Y]
- ;
- RHS = functor(_, _, Vars)
- ),
- mode_info_get_module_info(!.ModeInfo, ModuleInfo),
- mode_info_get_instmap(!.ModeInfo, InstMap),
- AnyVars =
- list__filter(var_inst_contains_any(ModuleInfo, InstMap), Vars)
- % If this unification is in a negated context, but doesn't
- % have an `impure' annotation, then none of the variables
- % involved are allowed to have inst any.
- %
- ->
- (
- AnyVars = [_ | _],
- (
- goal_info_is_impure(UnifyGoalInfo0)
- ->
- modecheck_unification_2(X, RHS, Unification0, UnifyContext,
- UnifyGoalInfo0, Unify, !ModeInfo, !IO)
- ;
- set__init(WaitingVars),
- mode_info_error(WaitingVars,
- purity_error_should_be_impure(AnyVars), !ModeInfo),
- Unify = conj([])
- )
- ;
- AnyVars = [],
- (
- goal_info_is_pure(UnifyGoalInfo0)
- ->
- modecheck_unification_2(X, RHS, Unification0, UnifyContext,
- UnifyGoalInfo0, Unify, !ModeInfo, !IO)
- ;
- set__init(WaitingVars),
- mode_info_error(WaitingVars,
- purity_error_wrongly_impure(purity_pure), !ModeInfo),
- Unify = conj([])
- )
- )
- ;
- mode_info_get_in_negated_context(!.ModeInfo, no),
- \+ goal_info_is_pure(UnifyGoalInfo0)
- ->
- set__init(WaitingVars),
- mode_info_error(WaitingVars,
- purity_error_wrongly_impure(purity_pure), !ModeInfo),
- Unify = conj([])
- ;
% If this is a lambda unification containing some inst any
% nonlocals, then the lambda should be marked as impure.
%
Index: compiler/modes.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/modes.m,v
retrieving revision 1.322
diff -u -r1.322 modes.m
--- compiler/modes.m 28 Nov 2005 04:11:49 -0000 1.322
+++ compiler/modes.m 13 Dec 2005 01:03:14 -0000
@@ -1356,10 +1356,7 @@
%
mode_info_lock_vars(if_then_else, NonLocals, !ModeInfo),
mode_info_add_live_vars(ThenVars, !ModeInfo),
- mode_info_get_in_negated_context(!.ModeInfo, InNegatedContext0),
- mode_info_set_in_negated_context(yes, !ModeInfo),
modecheck_goal(Cond0, Cond, !ModeInfo, !IO),
- mode_info_set_in_negated_context(InNegatedContext0, !ModeInfo),
mode_info_get_instmap(!.ModeInfo, InstMapCond),
mode_info_remove_live_vars(ThenVars, !ModeInfo),
mode_info_unlock_vars(if_then_else, NonLocals, !ModeInfo),
@@ -1384,6 +1381,18 @@
instmap__merge(NonLocals, [InstMapThen, InstMapElse], if_then_else,
!ModeInfo),
Goal = if_then_else(Vars, Cond, Then, Else),
+ mode_info_get_instmap(!.ModeInfo, InstMap),
+ (
+ mode_info_get_in_promise_purity_scope(!.ModeInfo, no)
+ ->
+ goal_get_nonlocals(Cond, CondNonLocals0),
+ CondNonLocals =
+ set__to_sorted_list(CondNonLocals0 `intersect` NonLocals),
+ check_no_inst_any_vars(if_then_else, CondNonLocals,
+ InstMap0, InstMap, !ModeInfo)
+ ;
+ true
+ ),
mode_checkpoint(exit, "if-then-else", !ModeInfo, !IO).
modecheck_goal_expr(not(SubGoal0), GoalInfo0, not(SubGoal), !ModeInfo, !IO) :-
@@ -1407,13 +1416,20 @@
% that the negation does not bind them.
%
mode_info_lock_vars(negation, NonLocals, !ModeInfo),
- mode_info_get_in_negated_context(!.ModeInfo, InNegatedContext0),
- mode_info_set_in_negated_context(yes, !ModeInfo),
modecheck_goal(SubGoal0, SubGoal, !ModeInfo, !IO),
- mode_info_set_in_negated_context(InNegatedContext0, !ModeInfo),
mode_info_set_live_vars(LiveVars0, !ModeInfo),
mode_info_unlock_vars(negation, NonLocals, !ModeInfo),
mode_info_set_instmap(InstMap0, !ModeInfo),
+ (
+ mode_info_get_in_promise_purity_scope(!.ModeInfo, no)
+ ->
+ goal_info_get_nonlocals(GoalInfo0, NegNonLocals),
+ instmap__init_unreachable(Unreachable),
+ check_no_inst_any_vars(negation, set__to_sorted_list(NegNonLocals),
+ InstMap0, Unreachable, !ModeInfo)
+ ;
+ true
+ ),
mode_checkpoint(exit, "not", !ModeInfo, !IO).
modecheck_goal_expr(scope(Reason, SubGoal0), _GoalInfo, GoalExpr,
@@ -1459,6 +1475,14 @@
mode_checkpoint(exit, "scope", !ModeInfo, !IO),
SubGoal = GoalExpr - _
)
+ ; Reason = promise_purity(_Implicit, _Purity) ->
+ mode_info_get_in_promise_purity_scope(!.ModeInfo, InPPScope),
+ mode_info_set_in_promise_purity_scope(yes, !ModeInfo),
+ mode_checkpoint(enter, "scope", !ModeInfo, !IO),
+ modecheck_goal(SubGoal0, SubGoal, !ModeInfo, !IO),
+ mode_checkpoint(exit, "scope", !ModeInfo, !IO),
+ GoalExpr = scope(Reason, SubGoal),
+ mode_info_set_in_promise_purity_scope(InPPScope, !ModeInfo)
;
mode_checkpoint(enter, "scope", !ModeInfo, !IO),
modecheck_goal(SubGoal0, SubGoal, !ModeInfo, !IO),
@@ -1624,6 +1648,35 @@
% these should have been expanded out by now
unexpected(this_file, "modecheck_goal_expr: unexpected shorthand").
+
+ % If the condition of a negation or if-then-else contains any inst any
+ % non-locals (a potential referential transparency violation)
+ % we need to check that the programmer has recognised the
+ % possibility and placed the if-then-else in a promise_<purity>
+ % scope.
+ %
+:- pred check_no_inst_any_vars(negated_context_desc::in, prog_vars::in,
+ instmap::in, instmap::in, mode_info::in, mode_info::out) is det.
+
+check_no_inst_any_vars(_, [], _, _, !ModeInfo).
+
+check_no_inst_any_vars(NegCtxtDesc, [NonLocal | NonLocals], InstMap0, InstMap,
+ !ModeInfo) :-
+ (
+ ( instmap__lookup_var(InstMap0, NonLocal, Inst)
+ ; instmap__lookup_var(InstMap, NonLocal, Inst)
+ ),
+ mode_info_get_module_info(!.ModeInfo, ModuleInfo),
+ inst_contains_any(ModuleInfo, Inst)
+ ->
+ mode_info_error(make_singleton_set(NonLocal),
+ purity_error_should_be_in_promise_purity_scope(NegCtxtDesc,
+ NonLocal), !ModeInfo)
+ ;
+ check_no_inst_any_vars(NegCtxtDesc, NonLocals, InstMap0, InstMap,
+ !ModeInfo)
+ ).
+
append_extra_goals(no_extra_goals, ExtraGoals, ExtraGoals).
append_extra_goals(extra_goals(BeforeGoals, AfterGoals),
no_extra_goals, extra_goals(BeforeGoals, AfterGoals)).
Index: compiler/purity.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/purity.m,v
retrieving revision 1.86
diff -u -r1.86 purity.m
--- compiler/purity.m 18 Nov 2005 06:13:37 -0000 1.86
+++ compiler/purity.m 13 Dec 2005 07:42:30 -0000
@@ -5,34 +5,18 @@
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
-
-% File: purity.m.
+%
+% File: purity.m
% Authors: scachte (Peter Schachte, main author and designer of purity system)
% trd (modifications for impure functions)
-% rafe (modifications for solver goals in negated contexts)
-
% Purpose: handle `impure' and `promise_pure' declarations;
% finish off type checking.
-
-% The main purpose of this module is check the consistency of the `impure' and
-% `promise_pure' (etc.) declarations, and to thus report error messages if the
-% program is not "purity-correct". This includes treating procedures with
-% different clauses for different modes as impure, unless promised pure.
-%
-% There is a special case to do with solver type goals in negated contexts.
-% A pure goal taking solver type arguments with inst any may be used to
-% break referential transparency if it appears in a negated context. The
-% canonical example is `X < Y, not(X >= Y)' vs `not(X >= Y), X < Y' where
-% (<) and (>=) impose inequality constraints on solver variables. If X
-% and Y are initially free then the first goal ordering will succeed, but the
-% second ordering will fail. To get around this problem we require solver
-% goals in negated contexts with inst any arguments to be marked as `impure'.
-% This means that this module should not report purity warnings for calls
-% marked as impure in negated contexts when those calls are to non-impure
-% predicates or functions. Instead this aspect of purity checking must be
-% deferred until mode analysis which is when information about the insts of
-% variables is available. (Note: the above also applies to lambdas containing
-% non-local inst any variables.)
+%
+% The main purpose of this module is check the consistency of the
+% `impure' and `promise_pure' (etc.) declarations, and to thus report
+% error messages if the program is not "purity-correct".
+% This includes treating procedures with different clauses for
+% different modes as impure, unless promised pure.
%
% This module also calls post_typecheck.m to perform the final parts of
% type analysis, including resolution of predicate and function overloading
@@ -135,20 +119,20 @@
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
+:- import_module parse_tree.prog_data.
:- import_module bool.
:- import_module io.
-%-----------------------------------------------------------------------------%
-
- % Purity check a whole module. Also do the post-typecheck stuff described
- % above, and eliminate double negations and calls to
- % `private_builtin.unsafe_type_cast/2'. The first argument specifies
- % whether there were any type errors (if so, we suppress some diagnostics
- % in post_typecheck.m because they are usually spurious). The third
- % argument specifies whether post_typecheck.m detected any errors that
- % would cause problems for later passes (if so, we stop compilation after
- % this pass).
+ % Purity check a whole module. Also do the post-typecheck stuff
+ % described above, and eliminate double negations and calls
+ % to `private_builtin.unsafe_type_cast/2'.
+ % The first argument specifies whether there were any type
+ % errors (if so, we suppress some diagnostics in post_typecheck.m
+ % because they are usually spurious).
+ % The third argument specifies whether post_typecheck.m detected
+ % any errors that would cause problems for later passes
+ % (if so, we stop compilation after this pass).
%
:- pred puritycheck(bool::in, bool::out, module_info::in, module_info::out,
io::di, io::uo) is det.
@@ -163,6 +147,12 @@
:- pred repuritycheck_proc(module_info::in, pred_proc_id::in, pred_info::in,
pred_info::out) is det.
+ % Give an error message for unifications marked impure/semipure
+ % that are not function calls (e.g. impure X = 4)
+ %
+:- pred impure_unification_expr_error(prog_context::in, purity::in,
+ io::di, io::uo) is det.
+
:- implementation.
:- import_module check_hlds.clause_to_proc.
@@ -196,6 +186,7 @@
:- import_module int.
:- import_module list.
:- import_module map.
+:- import_module require.
:- import_module set.
:- import_module std_util.
:- import_module string.
@@ -205,7 +196,6 @@
%-----------------------------------------------------------------------------%
%
% Public Predicates
-%
puritycheck(FoundTypeError, PostTypecheckError, !HLDS, !IO) :-
globals__io_lookup_bool_option(statistics, Statistics, !IO),
@@ -290,7 +280,7 @@
module_info::in, int::out, io::di, io::uo) is det.
puritycheck_pred(PredId, !PredInfo, ModuleInfo, NumErrors, !IO) :-
- pred_info_get_purity(!.PredInfo, DeclPurity),
+ pred_info_get_purity(!.PredInfo, DeclPurity) ,
pred_info_get_promised_purity(!.PredInfo, PromisedPurity),
some [!ClausesInfo] (
pred_info_clauses_info(!.PredInfo, !:ClausesInfo),
@@ -300,13 +290,12 @@
clauses_info_varset(!.ClausesInfo, VarSet0),
RunPostTypecheck = yes,
PurityInfo0 = purity_info(ModuleInfo, RunPostTypecheck,
- !.PredInfo, VarTypes0, VarSet0, [], dont_make_implicit_promises,
- no),
+ !.PredInfo, VarTypes0, VarSet0, [], dont_make_implicit_promises),
pred_info_get_goal_type(!.PredInfo, GoalType),
compute_purity(GoalType, Clauses0, Clauses, ProcIds,
purity_pure, Purity, PurityInfo0, PurityInfo),
PurityInfo = purity_info(_, _, !:PredInfo,
- VarTypes, VarSet, RevMessages, _, _),
+ VarTypes, VarSet, RevMessages, _),
clauses_info_set_vartypes(VarTypes, !ClausesInfo),
clauses_info_set_varset(VarSet, !ClausesInfo),
Messages = list__reverse(RevMessages),
@@ -351,9 +340,9 @@
proc_info_varset(ProcInfo0, VarSet0),
RunPostTypeCheck = no,
PurityInfo0 = purity_info(ModuleInfo, RunPostTypeCheck,
- !.PredInfo, VarTypes0, VarSet0, [], dont_make_implicit_promises, no),
+ !.PredInfo, VarTypes0, VarSet0, [], dont_make_implicit_promises),
compute_goal_purity(Goal0, Goal, Bodypurity, PurityInfo0, PurityInfo),
- PurityInfo = purity_info(_, _, !:PredInfo, VarTypes, VarSet, _, _, _),
+ PurityInfo = purity_info(_, _, !:PredInfo, VarTypes, VarSet, _, _),
proc_info_set_goal(Goal, ProcInfo0, ProcInfo1),
proc_info_set_vartypes(VarTypes, ProcInfo1, ProcInfo2),
proc_info_set_varset(VarSet, ProcInfo2, ProcInfo),
@@ -417,8 +406,7 @@
compute_purity(GoalType, [Clause0 | Clauses0], [Clause | Clauses], ProcIds,
!Purity, !Info) :-
Clause0 = clause(Ids, Body0 - Info0, Lang, Context),
- compute_expr_purity(Body0, Body, Info0, Bodypurity, !Info),
- add_goal_info_purity_feature(Bodypurity, Info0, Info),
+ compute_expr_purity(Body0, Body, Info0, Bodypurity0, !Info),
% If this clause doesn't apply to all modes of this procedure,
% i.e. the procedure has different clauses for different modes,
% then we must treat it as impure.
@@ -434,7 +422,9 @@
;
Clausepurity = purity_impure
),
- !:Purity = worst_purity(!.Purity, worst_purity(Bodypurity, Clausepurity)),
+ worst_purity(Bodypurity0, Clausepurity) = Bodypurity,
+ add_goal_info_purity_feature(Bodypurity, Info0, Info),
+ !:Purity = worst_purity(!.Purity, Bodypurity),
Clause = clause(Ids, Body - Info, Lang, Context),
compute_purity(GoalType, Clauses0, Clauses, ProcIds, !Purity, !Info).
@@ -454,20 +444,11 @@
:- pred compute_expr_purity(hlds_goal_expr::in, hlds_goal_expr::out,
hlds_goal_info::in, purity::out, purity_info::in, purity_info::out) is det.
-compute_expr_purity(GoalExpr0, GoalExpr, GoalInfo0, ActualPurity, !Info) :-
- compute_expr_purity_2(GoalExpr0, GoalExpr, GoalInfo0, InferredPurity,
- !Info),
- infer_goal_info_purity(GoalInfo0, DeclPurity),
- ActualPurity = worst_purity(DeclPurity, InferredPurity).
-
-:- pred compute_expr_purity_2(hlds_goal_expr::in, hlds_goal_expr::out,
- hlds_goal_info::in, purity::out, purity_info::in, purity_info::out) is det.
-
-compute_expr_purity_2(conj(Goals0), conj(Goals), _, Purity, !Info) :-
+compute_expr_purity(conj(Goals0), conj(Goals), _, Purity, !Info) :-
compute_goals_purity(Goals0, Goals, purity_pure, Purity, !Info).
-compute_expr_purity_2(par_conj(Goals0), par_conj(Goals), _, Purity, !Info) :-
+compute_expr_purity(par_conj(Goals0), par_conj(Goals), _, Purity, !Info) :-
compute_goals_purity(Goals0, Goals, purity_pure, Purity, !Info).
-compute_expr_purity_2(Goal0, Goal, GoalInfo, ActualPurity, !Info) :-
+compute_expr_purity(Goal0, Goal, GoalInfo, ActualPurity, !Info) :-
Goal0 = call(PredId0, ProcId, Vars, BIState, UContext, Name0),
RunPostTypecheck = !.Info ^ run_post_typecheck,
PredInfo = !.Info ^ pred_info,
@@ -497,7 +478,7 @@
goal_info_get_context(GoalInfo, CallContext),
perform_goal_purity_checks(CallContext, PredId,
DeclaredPurity, ActualPurity, !Info).
-compute_expr_purity_2(generic_call(GenericCall0, Args, Modes0, Det),
+compute_expr_purity(generic_call(GenericCall0, Args, Modes0, Det),
GoalExpr, GoalInfo, Purity, !Info) :-
(
GenericCall0 = higher_order(_, Purity, _, _),
@@ -537,10 +518,10 @@
),
GoalExpr = generic_call(GenericCall, Args, Modes, Det)
).
-compute_expr_purity_2(switch(Var, Canfail, Cases0),
+compute_expr_purity(switch(Var, Canfail, Cases0),
switch(Var, Canfail, Cases), _, Purity, !Info) :-
compute_cases_purity(Cases0, Cases, purity_pure, Purity, !Info).
-compute_expr_purity_2(Unif0, GoalExpr, GoalInfo, ActualPurity, !Info) :-
+compute_expr_purity(Unif0, GoalExpr, GoalInfo, ActualPurity, !Info) :-
Unif0 = unify(Var, RHS0, Mode, Unification, UnifyContext),
(
RHS0 = lambda_goal(LambdaPurity, F, EvalMethod,
@@ -559,7 +540,7 @@
FixModes = modes_need_fixing,
(
EvalMethod = lambda_normal,
- unexpected(this_file, "compute_expr_purity_2: modes need " ++
+ error("compute_expr_purity: modes need " ++
"fixing for normal lambda_goal")
;
EvalMethod = lambda_aditi_bottom_up,
@@ -575,6 +556,16 @@
GoalExpr = unify(Var, RHS, Mode, Unification, UnifyContext),
% the unification itself is always pure,
% even if the lambda expression body is impure
+ infer_goal_info_purity(GoalInfo, DeclaredPurity),
+ (
+ DeclaredPurity \= purity_pure
+ ->
+ goal_info_get_context(GoalInfo, Context),
+ Message = impure_unification_expr_error(Context, DeclaredPurity),
+ purity_info_add_message(error(Message), !Info)
+ ;
+ true
+ ),
ActualPurity = purity_pure
;
RHS0 = functor(ConsId, _, Args)
@@ -609,22 +600,19 @@
GoalExpr = Unif0,
ActualPurity = purity_pure
).
-compute_expr_purity_2(disj(Goals0), disj(Goals), _, Purity, !Info) :-
+compute_expr_purity(disj(Goals0), disj(Goals), _, Purity, !Info) :-
compute_goals_purity(Goals0, Goals, purity_pure, Purity, !Info).
-compute_expr_purity_2(not(Goal0), NotGoal, GoalInfo0, Purity, !Info) :-
+compute_expr_purity(not(Goal0), NotGoal, GoalInfo0, Purity, !Info) :-
% Eliminate double negation.
negate_goal(Goal0, GoalInfo0, NotGoal0),
( NotGoal0 = not(Goal1) - _GoalInfo1 ->
- InNegatedContext0 = !.Info ^ in_negated_context,
- !:Info = !.Info ^ in_negated_context := yes,
compute_goal_purity(Goal1, Goal, Purity, !Info),
- !:Info = !.Info ^ in_negated_context := InNegatedContext0,
NotGoal = not(Goal)
;
compute_goal_purity(NotGoal0, NotGoal1, Purity, !Info),
NotGoal1 = NotGoal - _
).
-compute_expr_purity_2(scope(Reason, Goal0), scope(Reason, Goal),
+compute_expr_purity(scope(Reason, Goal0), scope(Reason, Goal),
_, Purity, !Info) :-
(
Reason = exist_quant(_),
@@ -654,21 +642,14 @@
Reason = from_ground_term(_),
compute_goal_purity(Goal0, Goal, Purity, !Info)
).
-compute_expr_purity_2(if_then_else(Vars, Cond0, Then0, Else0),
+compute_expr_purity(if_then_else(Vars, Cond0, Then0, Else0),
if_then_else(Vars, Cond, Then, Else), _, Purity, !Info) :-
- % The condition is in a negated context.
- %
- InNegatedContext0 = !.Info ^ in_negated_context,
- !:Info = !.Info ^ in_negated_context := yes,
compute_goal_purity(Cond0, Cond, Purity1, !Info),
- !:Info = !.Info ^ in_negated_context := InNegatedContext0,
- % The Then and Else goals are not.
- %
compute_goal_purity(Then0, Then, Purity2, !Info),
compute_goal_purity(Else0, Else, Purity3, !Info),
worst_purity(Purity1, Purity2) = Purity12,
worst_purity(Purity12, Purity3) = Purity.
-compute_expr_purity_2(ForeignProc0, ForeignProc, _, Purity, !Info) :-
+compute_expr_purity(ForeignProc0, ForeignProc, _, Purity, !Info) :-
ForeignProc0 = foreign_proc(_, _, _, _, _, _),
Attributes = ForeignProc0 ^ foreign_attr,
PredId = ForeignProc0 ^ foreign_pred_id,
@@ -686,9 +667,9 @@
Purity = purity(Attributes)
).
-compute_expr_purity_2(shorthand(_), _, _, _, !Info) :-
+compute_expr_purity(shorthand(_), _, _, _, !Info) :-
% These should have been expanded out by now.
- unexpected(this_file, "compute_expr_purity_2: unexpected shorthand").
+ error("compute_expr_purity: unexpected shorthand").
:- pred check_higher_order_purity(hlds_goal_info::in, cons_id::in,
prog_var::in, list(prog_var)::in, purity::out,
@@ -728,9 +709,22 @@
true
),
- % We take the annotated purity (if any) as the actual purity.
- % This is checked for validity in modecheck_unify.m
- infer_goal_info_purity(GoalInfo, ActualPurity).
+ % The unification itself is always pure,
+ % even if it is a unification with an impure higher-order term.
+ ActualPurity = purity_pure,
+
+ % Check for a bogus purity annotation on the unification.
+ infer_goal_info_purity(GoalInfo, DeclaredPurity),
+ (
+ DeclaredPurity \= purity_pure,
+ !.Info ^ implicit_purity = dont_make_implicit_promises
+ ->
+ goal_info_get_context(GoalInfo, Context),
+ Message = impure_unification_expr_error(Context, DeclaredPurity),
+ purity_info_add_message(error(Message), !Info)
+ ;
+ true
+ ).
% The possible results of a purity check.
:- type purity_check_result
@@ -820,11 +814,7 @@
).
% Peform purity checking of the actual and declared purity,
- % and check that promises are consistent. There is one
- % exception to this rule: an otherwise pure atomic goal must
- % be marked as `impure' if it has an inst any argument and
- % occurs in a negated context (see the comments at the top
- % of this file).
+ % and check that promises are consistent.
%
% ActualPurity: The inferred purity of the goal
% DeclaredPurity: The declared purity of the goal
@@ -852,14 +842,6 @@
->
true
;
- % If DeclaredPurity is impure and this is in a negated context
- % then we have to defer this purity check until mode checking
- % when we know whether the vars have inst any or not.
- DeclaredPurity = purity_impure,
- !.Info ^ in_negated_context = yes
- ->
- true
- ;
% Don't require purity annotations on calls in
% compiler-generated code.
is_unify_or_compare_pred(PredInfo)
@@ -895,14 +877,7 @@
compute_goal_purity(Goal0 - GoalInfo0, Goal - GoalInfo, Purity, !Info) :-
compute_expr_purity(Goal0, Goal, GoalInfo0, Purity, !Info),
- (
- !.Info ^ in_negated_context = yes,
- infer_goal_info_purity(GoalInfo0, purity_impure)
- ->
- GoalInfo = GoalInfo0
- ;
- add_goal_info_purity_feature(Purity, GoalInfo0, GoalInfo)
- ).
+ add_goal_info_purity_feature(Purity, GoalInfo0, GoalInfo).
% Compute the purity of a list of hlds_goals. Since the purity of a
% disjunction is computed the same way as the purity of a conjunction,
@@ -935,9 +910,9 @@
fix_aditi_state_modes(_, _, [], [], []).
fix_aditi_state_modes(_, _, [_|_], [], []) :-
- unexpected(this_file, "fix_aditi_state_modes: mismatched lists").
+ error("purity:fix_aditi_state_modes").
fix_aditi_state_modes(_, _, [], [_|_], []) :-
- unexpected(this_file, "fix_aditi_state_modes: mismatched lists").
+ error("purity:fix_aditi_state_modes").
fix_aditi_state_modes(SeenState0, AditiStateMode, [Type | Types],
[ArgMode0 | Modes0], [ArgMode | Modes]) :-
( type_is_aditi_state(Type) ->
@@ -1028,8 +1003,7 @@
CodeStr = "impure"
;
PromisedPurity = purity_impure,
- unexpected(this_file,
- "warn_unnecessary_promise_pure: promise_impure?")
+ error("purity__warn_unnecessary_promise_pure: promise_impure?")
),
Pieces1 = [words("warning: unnecessary `" ++ Pragma ++ "' pragma."), nl],
globals__io_lookup_bool_option(verbose_errors, VerboseErrors, !IO),
@@ -1077,7 +1051,6 @@
% Errors and warnings reported by purity.m and post_typecheck.m
% for problems within a goal.
- %
:- type post_typecheck_message
---> error(post_typecheck_error)
; warning(post_typecheck_warning).
@@ -1088,6 +1061,7 @@
---> missing_body_impurity_error(prog_context, pred_id)
; closure_purity_error(prog_context, purity, purity)
% closure_purity_error(Context, DeclaredPurity, ActualPurity)
+ ; impure_unification_expr_error(prog_context, purity)
; aditi_builtin_error(aditi_builtin_error).
:- type post_typecheck_warning
@@ -1106,6 +1080,9 @@
Message = closure_purity_error(Context, DeclaredPurity, ActualPurity),
report_error_closure_purity(Context, DeclaredPurity, ActualPurity, !IO)
;
+ Message = impure_unification_expr_error(Context, Purity),
+ impure_unification_expr_error(Context, Purity, !IO)
+ ;
Message = aditi_builtin_error(AditiError),
report_aditi_builtin_error(AditiError, !IO)
).
@@ -1195,8 +1172,9 @@
purity_info_add_message(error(closure_purity_error(Context,
DeclaredPurity, ActualPurity)), !IO)
;
- % We don't bother to warn if the DeclaredPurity is less pure than the
- % ActualPurity; that would lead to too many spurious warnings.
+ % we don't bother to warn if the DeclaredPurity is less
+ % pure than the ActualPurity; that would lead to too many
+ % spurious warnings.
true
).
@@ -1211,6 +1189,13 @@
fixed(ActualPurityName ++ ".")],
write_error_pieces(Context, 0, Pieces, !IO).
+impure_unification_expr_error(Context, Purity, !IO) :-
+ purity_name(Purity, PurityName),
+ Pieces = [words("Purity error: unification with expression"),
+ words("was declared"), fixed(PurityName ++ ","),
+ words("but expression was not a function call.")],
+ write_error_pieces(Context, 0, Pieces, !IO).
+
%-----------------------------------------------------------------------------%
:- type purity_info
@@ -1224,15 +1209,11 @@
vartypes :: vartypes,
varset :: prog_varset,
messages :: post_typecheck_messages,
- implicit_purity :: implicit_purity_promise,
+ implicit_purity :: implicit_purity_promise
% If this is make_implicit_promises then
% purity annotations are optional in the
% current scope and purity warnings/errors
% should not be generated.
- in_negated_context :: bool
- % This is `yes' iff the current goal
- % is in a negation or the condition
- % of an if-then-else.
).
:- pred purity_info_add_message(post_typecheck_message::in,
@@ -1242,11 +1223,3 @@
Info ^ messages := [Message | Info ^ messages]).
%-----------------------------------------------------------------------------%
-
-:- func this_file = string.
-
-this_file = "purity.m".
-
-%-----------------------------------------------------------------------------%
-:- end_module purity.
-%-----------------------------------------------------------------------------%
Index: compiler/unique_modes.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/unique_modes.m,v
retrieving revision 1.102
diff -u -r1.102 unique_modes.m
--- compiler/unique_modes.m 28 Nov 2005 04:11:59 -0000 1.102
+++ compiler/unique_modes.m 9 Dec 2005 02:25:42 -0000
@@ -359,10 +359,7 @@
mode_info_remove_live_vars(Else_Vars, !ModeInfo),
mode_info_add_live_vars(Then_Vars, !ModeInfo),
- mode_info_get_in_negated_context(!.ModeInfo, InNegatedContext0),
- mode_info_set_in_negated_context(yes, !ModeInfo),
check_goal(Cond0, Cond, !ModeInfo, !IO),
- mode_info_set_in_negated_context(InNegatedContext0, !ModeInfo),
mode_info_remove_live_vars(Then_Vars, !ModeInfo),
mode_info_unlock_vars(if_then_else, NonLocals, !ModeInfo),
mode_info_get_instmap(!.ModeInfo, InstMapCond),
Index: doc/reference_manual.texi
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/reference_manual.texi,v
retrieving revision 1.340
diff -u -r1.340 reference_manual.texi
--- doc/reference_manual.texi 23 Nov 2005 05:11:41 -0000 1.340
+++ doc/reference_manual.texi 9 Dec 2005 03:44:42 -0000
@@ -2381,10 +2381,10 @@
Referential transparency can be violated if a non-local solver type variable
with inst @code{any} is used in a negated context (i.e., the body of a negated
goal or the condition of an if-then-else) or in a lambda expression. Programs
-are therefore required to place @code{impure} annotations on such goals or
-lambda expressions, even if they would normally be considered pure. This
-impurity propagates through the enclosing goal structure as specified in
- at ref{Impurity}.
+are therefore required to place such goals inside a @code{promise_pure},
+ at code{promise_semipure}, or @code{promise_impure} scope, and to declare such
+lambda expressions to be @code{impure}, even if they would normally be
+considered pure.
@node Polymorphic solver types
@subsection Polymorphic solver types
Index: tests/debugger/solver_test.m
===================================================================
RCS file: /home/mercury1/repository/tests/debugger/solver_test.m,v
retrieving revision 1.1
diff -u -r1.1 solver_test.m
--- tests/debugger/solver_test.m 9 Dec 2004 01:03:19 -0000 1.1
+++ tests/debugger/solver_test.m 12 Dec 2005 00:14:31 -0000
@@ -42,6 +42,7 @@
% unification in the condition.
test_any_free_unify(List, Result) :-
+ promise_pure
( List = [_ | _] ->
Result = no
;
Index: tests/hard_coded/any_free_unify.m
===================================================================
RCS file: /home/mercury1/repository/tests/hard_coded/any_free_unify.m,v
retrieving revision 1.4
diff -u -r1.4 any_free_unify.m
--- tests/hard_coded/any_free_unify.m 21 Sep 2004 07:40:22 -0000 1.4
+++ tests/hard_coded/any_free_unify.m 12 Dec 2005 00:09:52 -0000
@@ -38,7 +38,7 @@
% unification in the condition.
test_any_free_unify(List, Result) :-
- ( List = [_ | _] ->
+ promise_pure ( List = [_ | _] ->
Result = no
;
Result = yes
Index: tests/invalid/any_passed_as_ground.m
===================================================================
RCS file: /home/mercury1/repository/tests/invalid/any_passed_as_ground.m,v
retrieving revision 1.2
diff -u -r1.2 any_passed_as_ground.m
--- tests/invalid/any_passed_as_ground.m 8 Nov 2005 06:42:02 -0000 1.2
+++ tests/invalid/any_passed_as_ground.m 13 Dec 2005 06:26:34 -0000
@@ -33,7 +33,7 @@
%
main(!IO) :-
p(Xs),
- promise_pure ( if impure member((X - _), Xs) then Y = X else Y = 0 ),
+ promise_pure ( if member((X - _), Xs) then Y = X else Y = 0 ),
io.write_int(Y, !IO).
:- pred i(st::oa) is det.
Index: tests/invalid/any_to_ground_in_ite_cond.m
===================================================================
RCS file: /home/mercury1/repository/tests/invalid/any_to_ground_in_ite_cond.m,v
retrieving revision 1.2
diff -u -r1.2 any_to_ground_in_ite_cond.m
--- tests/invalid/any_to_ground_in_ite_cond.m 8 Nov 2005 06:42:02 -0000 1.2
+++ tests/invalid/any_to_ground_in_ite_cond.m 12 Dec 2005 05:15:30 -0000
@@ -33,7 +33,7 @@
main(!IO) :-
i(X),
promise_pure (
- if impure p(X) then
+ if p(X) then
io.write_string("aye\n", !IO)
else
io.write_string("nay\n", !IO)
Index: tests/invalid/anys_in_negated_contexts.err_exp
===================================================================
RCS file: /home/mercury1/repository/tests/invalid/anys_in_negated_contexts.err_exp,v
retrieving revision 1.1
diff -u -r1.1 anys_in_negated_contexts.err_exp
--- tests/invalid/anys_in_negated_contexts.err_exp 7 Nov 2005 07:47:11 -0000 1.1
+++ tests/invalid/anys_in_negated_contexts.err_exp 13 Dec 2005 02:16:17 -0000
@@ -1,28 +1,14 @@
-anys_in_negated_contexts.m:035: In clause for `bad_if_then_else1(ia, out)':
-anys_in_negated_contexts.m:035: in call to predicate
-anys_in_negated_contexts.m:035: `anys_in_negated_contexts.ia/1':
-anys_in_negated_contexts.m:035: purity error: goal should be impure because
-anys_in_negated_contexts.m:035: it appears in a negated context, but involves
-anys_in_negated_contexts.m:035: the following variables whose insts contain
-anys_in_negated_contexts.m:035: `any': X
-anys_in_negated_contexts.m:043: In clause for `bad_if_then_else2(ia, out)':
-anys_in_negated_contexts.m:043: in call to predicate
-anys_in_negated_contexts.m:043: `anys_in_negated_contexts.ig/1':
-anys_in_negated_contexts.m:043: purity error: goal is marked as impure, but
-anys_in_negated_contexts.m:043: is actually pure
-anys_in_negated_contexts.m:056: In clause for `bad_negation1(ia)':
-anys_in_negated_contexts.m:056: in call to predicate
-anys_in_negated_contexts.m:056: `anys_in_negated_contexts.ia/1':
-anys_in_negated_contexts.m:056: purity error: goal should be impure because
-anys_in_negated_contexts.m:056: it appears in a negated context, but involves
-anys_in_negated_contexts.m:056: the following variables whose insts contain
-anys_in_negated_contexts.m:056: `any': X
-anys_in_negated_contexts.m:061: In clause for `bad_negation2(ia)':
-anys_in_negated_contexts.m:061: in call to predicate
-anys_in_negated_contexts.m:061: `anys_in_negated_contexts.ig/1':
-anys_in_negated_contexts.m:061: purity error: goal is marked as impure, but
-anys_in_negated_contexts.m:061: is actually pure
-anys_in_negated_contexts.m:082: In clause for `bad_lambda(in)':
-anys_in_negated_contexts.m:082: purity error: lambda should be impure because
-anys_in_negated_contexts.m:082: it contains the following non-local variables
-anys_in_negated_contexts.m:082: whose insts contain `any': Y
+anys_in_negated_contexts.m:038: In clause for `bad_if_then_else1(ia, out)':
+anys_in_negated_contexts.m:038: purity error: if-then-else should be inside a
+anys_in_negated_contexts.m:038: promise_purity scope because non-local
+anys_in_negated_contexts.m:038: variable X has inst any and appears in the
+anys_in_negated_contexts.m:038: condition.
+anys_in_negated_contexts.m:049: In clause for `bad_negation1(ia)':
+anys_in_negated_contexts.m:049: purity error: negation should be inside a
+anys_in_negated_contexts.m:049: promise_purity scope because non-local
+anys_in_negated_contexts.m:049: variable X has inst any and appears in the
+anys_in_negated_contexts.m:049: body.
+anys_in_negated_contexts.m:070: In clause for `bad_lambda(in)':
+anys_in_negated_contexts.m:070: purity error: lambda should be impure because
+anys_in_negated_contexts.m:070: it contains the following non-local variables
+anys_in_negated_contexts.m:070: whose insts contain `any': Y.
Index: tests/invalid/anys_in_negated_contexts.m
===================================================================
RCS file: /home/mercury1/repository/tests/invalid/anys_in_negated_contexts.m,v
retrieving revision 1.2
diff -u -r1.2 anys_in_negated_contexts.m
--- tests/invalid/anys_in_negated_contexts.m 16 Nov 2005 06:28:02 -0000 1.2
+++ tests/invalid/anys_in_negated_contexts.m 12 Dec 2005 05:26:39 -0000
@@ -24,7 +24,8 @@
:- pred good_if_then_else(T::ia, int::out) is det.
good_if_then_else(X, Y) :-
- ( if promise_pure (impure ia(X)), ig(3)
+ promise_pure
+ ( if ia(X), ig(3)
then Y = 1
else Y = 2
).
@@ -37,29 +38,16 @@
else Y = 2
).
-:- pred bad_if_then_else2(T::ia, int::out) is det.
-
-bad_if_then_else2(X, Y) :-
- ( if promise_pure (impure ia(X), impure ig(3))
- then Y = 1
- else Y = 2
- ).
-
:- pred good_negation(T::ia) is semidet.
good_negation(X) :-
- not (promise_pure ((impure ia(X), ig(3)))).
+ promise_pure not (ia(X), ig(3)).
:- pred bad_negation1(T::ia) is semidet.
bad_negation1(X) :-
not (ia(X), ig(3)).
-:- pred bad_negation2(T::ia) is semidet.
-
-bad_negation2(X) :-
- not (promise_pure ((impure ia(X), impure ig(3)))).
-
:- pred pure_pred_mode_specific_clauses(int).
:- mode pure_pred_mode_specific_clauses(in) is semidet.
:- mode pure_pred_mode_specific_clauses(out) is det.
Index: tests/invalid/purity/impure_func_t7.err_exp
===================================================================
RCS file: /home/mercury1/repository/tests/invalid/purity/impure_func_t7.err_exp,v
retrieving revision 1.4
diff -u -r1.4 impure_func_t7.err_exp
--- tests/invalid/purity/impure_func_t7.err_exp 18 Nov 2005 06:13:39 -0000 1.4
+++ tests/invalid/purity/impure_func_t7.err_exp 13 Dec 2005 07:43:45 -0000
@@ -1,12 +1,10 @@
-impure_func_t7.m:029: In clause for `bad_impure_if_then_else_expr(di, uo)':
-impure_func_t7.m:029: purity error: goal is marked as impure, but is actually
-impure_func_t7.m:029: pure
-impure_func_t7.m:037: In clause for `bad_impure_lambda_unification(di, uo)':
-impure_func_t7.m:037: purity error: goal is marked as impure, but is actually
-impure_func_t7.m:037: pure
-impure_func_t7.m:046: In clause for `bad_impure_field_access(di, uo)':
-impure_func_t7.m:046: purity error: goal is marked as impure, but is actually
-impure_func_t7.m:046: pure
-impure_func_t7.m:054: In clause for `bad_impure_assignment(di, uo)':
-impure_func_t7.m:054: purity error: goal is marked as impure, but is actually
-impure_func_t7.m:054: pure
+impure_func_t7.m:029: Purity error: unification with expression was declared
+impure_func_t7.m:029: impure, but expression was not a function call.
+impure_func_t7.m:029: Purity error: unification with expression was declared
+impure_func_t7.m:029: impure, but expression was not a function call.
+impure_func_t7.m:037: Purity error: unification with expression was declared
+impure_func_t7.m:037: impure, but expression was not a function call.
+impure_func_t7.m:046: Purity error: unification with expression was declared
+impure_func_t7.m:046: impure, but expression was not a function call.
+impure_func_t7.m:054: Purity error: unification with expression was declared
+impure_func_t7.m:054: impure, but expression was not a function call.
Index: tests/invalid/purity/impure_func_t7.m
===================================================================
RCS file: /home/mercury1/repository/tests/invalid/purity/impure_func_t7.m,v
retrieving revision 1.3
diff -u -r1.3 impure_func_t7.m
--- tests/invalid/purity/impure_func_t7.m 18 Nov 2005 06:13:39 -0000 1.3
+++ tests/invalid/purity/impure_func_t7.m 13 Dec 2005 07:43:21 -0000
@@ -16,9 +16,9 @@
main(!IO) :-
impure bad_impure_if_then_else_expr(!IO),
- impure bad_impure_lambda_unification(!IO),
- impure bad_impure_field_access(!IO),
- impure bad_impure_assignment(!IO).
+ bad_impure_lambda_unification(!IO),
+ bad_impure_field_access(!IO),
+ bad_impure_assignment(!IO).
:- impure pred bad_impure_if_then_else_expr(io::di, io::uo) is det.
@@ -31,7 +31,7 @@
print(Z, !IO),
nl(!IO).
-:- impure pred bad_impure_lambda_unification(io::di, io::uo) is det.
+:- pred bad_impure_lambda_unification(io::di, io::uo) is det.
bad_impure_lambda_unification(!IO) :-
impure L = (pred(X5::out) is det :- X5 = 4),
@@ -40,7 +40,7 @@
print(P, !IO),
nl(!IO).
-:- impure pred bad_impure_field_access(io::di, io::uo) is det.
+:- pred bad_impure_field_access(io::di, io::uo) is det.
bad_impure_field_access(!IO) :-
impure M = blah(7) ^ foo,
@@ -48,7 +48,7 @@
print(M, !IO),
nl(!IO).
-:- impure pred bad_impure_assignment(io::di, io::uo) is det.
+:- pred bad_impure_assignment(io::di, io::uo) is det.
bad_impure_assignment(!IO) :-
impure B = 4,
--------------------------------------------------------------------------
mercury-reviews mailing list
post: mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------
More information about the reviews
mailing list