For review: Make Mercury cope with impure code

Peter Schachte pets at cs.mu.oz.au
Fri Dec 5 18:43:59 AEDT 1997


Round 3 for this review.

Fergus asked that I backup my working directory after the last diff
and post the next (this) diff relative to that.  Unfortunately, my
snapshotting technology was less that perfectly reliable, and I lost
the changes I made to the tests/invalid directory, so I'm including
the full diffs, which means I'm including the whole files, since
they're all new.


Make Mercury cope with impure code

The purpose of this diff is to allow Mercury programs to contain
impure Mercury code without the compiler changing its behavior
inappropriately, while still allowing the compiler to aggressively
optimize pure code.  To do this, we require impure predicates to be so
declared, and calls to impure predicates to be flagged as such.  We
also allow predicates implemented in terms of impure predicates to be
promised to be pure; lacking such a promise, any predicate that calls
an impure predicate is assumed to be impure.

At the moment, we don't allow impure functions (only predicates),
though some of the work necessary to support them has been done.

Note that to make the operators work properly, the precedence of the
`pred' and `func' operators has been changed from 1199 to 800.

Estimated hours taken: 150

compiler/purity.m:
	New compiler pass for purity checking.
compiler/hlds_goal.m:
	Add `impure' and `semipure' to the goal_feature enum.
compiler/hlds_out.m:
compiler/typecheck.m:
compiler/special_pred.m:
	Fixed code that prints predicate name to write something more
	helpful for special (compiler-generated) predicates.  Added
	code to print new markers.  Added purity argument to
	mercury_output_pred_type.  New public predicate
	special_pred_description/2 provides an english description for
	each compiler-generated predicate.
compiler/hlds_pred.m:
	Add `impure' and `semipure' to marker enum.  Added new
	public predicates to get predicate purity and whether or not
	it's promised to be pure.
compiler/prog_data.m:
compiler/mercury_to_mercury.m:
compiler/prog_io.m:
compiler/prog_io_goal.m:
compiler/prog_io_pragma.m:
compiler/prog_io_dcg.m:
compiler/prog_util.m:
compiler/equiv_type.m:
compiler/intermod.m:
compiler/mercury_to_c.m:
compiler/module_qual.m:
	Add purity argument to pred and func items.  Add new `impure'
	and `semipure' operators.  Add promise_pure pragma.  Add
	purity/2 wrapper to goal_expr type.
compiler/make_hlds.m:
compiler/mercury_to_goedel.m:
	Added purity argument to module_add_{pred,func},
	clauses_info_add_pragma_c_code, and to pred and func items.
	Handle promise_pure pragma.  Handle purity/2 wrapper used to
	handle user-written impurity annotations on goals.
compiler/mercury_compile.m:
	Add purity checking pass between type and mode checking.
compiler/mode_errors.m:
	Distinguish mode errors caused by impure goals preventing
	goals being delayed.
compiler/modes.m:
	Don't delay impure goals, and ensure before scheduling an
	impure goal that no goals are delayed.  Actually, we go ahead
	and try to schedule goals even if impurity causes a problem,
	and then if it still doesn't mode check, then we report an
	ordinary mode error.  Only if the clause would be mode correct
	except for an impure goal do we report it as an impurity problem.
compiler/simplify.m:
	Don't optimize away non-pure duplicate calls.  We could do
	better and still optimize duplicate semipure goals without an
	intervening impure goal, but it's probably not worth the
	trouble.  Also don't eliminate impure goals on a failing branch.
compiler/notes/compiler_design.html:
	Documented purity checking pass.
doc/reference_manual.texi:
	Document purity system.
doc/transition_guide.texi:
library/nc_builtin.nl:
library/ops.m:
library/sp_builtin.nl:
	New operators and new precdence for `pred' and `func'
	operators.
tests/hard_coded/purity.m
tests/hard_coded/purity.exp
tests/hard_coded/Mmakefile:
tests/invalid/purity.m
tests/invalid/purity_nonsense.m
tests/invalid/purity.err_exp
tests/invalid/purity_nonsense.err_exp
tests/invalid/Mmakefile:
	Test cases for purity.


--- last-make_hlds.m	Fri Dec  5 18:25:21 1997
+++ make_hlds.m	Fri Dec  5 18:18:39 1997
@@ -3353,9 +3353,9 @@
 	hlds_out__write_pred_call_id(Name/Arity),
 	io__write_string("\n"),
 	prog_out__write_context(Context),
-	% XXX is `preceding' the right word here?  Can't the pred or func decl
-	%     appear afterward?  Would `corresponding' be a better word?
-	io__write_string("  without preceding `pred' or `func' declaration\n").
+	% This used to say `preceding' instead of `corresponding.'
+	% Which is more correct?
+	io__write_string("  without corresponding `pred' or `func' declaration.\n").
 
 :- pred undefined_mode_error(sym_name, int, term__context, string,
 				io__state, io__state).
--- last-modes.m	Fri Dec  5 18:25:22 1997
+++ modes.m	Fri Dec  5 10:55:30 1997
@@ -901,16 +901,11 @@
 	modecheck_goal(G0, G),
 	mode_checkpoint(exit, "some").
 
-modecheck_goal_expr(call(PredId0, _, Args0, _, Context, PredName0),
+modecheck_goal_expr(call(PredId, _, Args0, _, Context, PredName),
 		GoalInfo0, Goal) -->
-	% Resolve overloading.  This is only necessary when invoked to
-	% modecheck a lambda goal; other overloading is handled in purity.m
-        =(ModeInfo0),
-	{ resolve_pred_overloading(PredId0, Args0, PredName0, PredName,
-		ModeInfo0, PredId) },
-
 	mode_checkpoint(enter, "call"),
 	mode_info_set_call_context(call(PredId)),
+	=(ModeInfo0),
 	{ mode_info_get_instmap(ModeInfo0, InstMap0) },
 
 	modecheck_call_pred(PredId, Args0, Mode, Args, ExtraGoals),
@@ -1215,7 +1210,10 @@
 
 %  check whether there are any delayed goals (other than headvar unifications)
 %  at the point where we are about to schedule an impure goal.  If so, that is
-%  an error.
+%  an error.  Headvar unifications are allowed to be delayed because in the
+%  case of output arguments, they cannot be scheduled until the variable value
+%  is known.  If headvar unifications couldn't be delayed past impure goals,
+%  impure predicates wouldn't be able to have outputs!
 :- pred check_for_impurity_error(hlds_goal, impurity_errors, impurity_errors,
 		mode_info, mode_info).
 :- mode check_for_impurity_error(in, in, out, mode_info_di, mode_info_uo)
@@ -1678,32 +1676,5 @@
 	{ Module = Module0 }.
 
 
-%-----------------------------------------------------------------------------%
-
-:- pred resolve_pred_overloading(pred_id, list(var), sym_name, sym_name,
-				mode_info, pred_id).
-:- mode resolve_pred_overloading(in, in, in, out, mode_info_ui, out) is det.
-	%
-	% In the case of a call to an overloaded predicate, typecheck.m
-	% does not figure out the correct pred_id.  We must do that here.
-	%
-resolve_pred_overloading(PredId0, Args0, PredName0, PredName,
-			ModeInfo0, PredId) :-
-	( invalid_pred_id(PredId0) ->
-		%
-		% Find the set of candidate pred_ids for predicates which
-		% have the specified name and arity
-		%
-		mode_info_get_module_info(ModeInfo0, ModuleInfo0),
-		mode_info_get_predid(ModeInfo0, ThisPredId),
-		module_info_pred_info(ModuleInfo0, ThisPredId, PredInfo),
-		pred_info_typevarset(PredInfo, TVarSet),
-		mode_info_get_var_types(ModeInfo0, VarTypes0),
-		typecheck__resolve_pred_overloading(ModuleInfo0, Args0,
-			VarTypes0, TVarSet, PredName0, PredName, PredId)
-	;
-		PredId = PredId0,
-		PredName = PredName0
-	).
 %-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%
--- last-prog_io_dcg.m	Fri Dec  5 18:25:23 1997
+++ prog_io_dcg.m	Tue Dec  2 17:58:09 1997
@@ -284,10 +284,12 @@
 	(   Goal1 = call(Pred, Args, pure) - Context ->
 		Goal = call(Pred, Args, Purity) - Context
 	;
-		% XXX Should print a warning:  user put an `impure' or
-		%     `semipure' marker on a non-atomic goal, or else they put
-		%     multiple markers on a goal.
-		Goal = Goal1
+		% Inappropriate placement of an impurity marker, so we treat
+		% it like a predicate call.  typecheck.m prints out something
+		% descriptive for these errors.
+		Goal1 = _ - Context,
+		purity_name(Purity, PurityString),
+		Goal = call(unqualified(PurityString), [G], pure) - Context
 	).
 
 :- pred append_to_disjunct(goal, goal_expr, term__context, goal).
--- last-prog_io_goal.m	Fri Dec  5 18:25:25 1997
+++ prog_io_goal.m	Tue Dec  2 17:57:46 1997
@@ -251,10 +251,11 @@
 	(   A1 = call(Pred, Args, pure) - _ ->
 		A = call(Pred, Args, Purity)
 	;
-		% XXX Should print a warning:  user put an `impure' or
-		%     `semipure' marker on a non-atomic goal, or else they put
-		%     multiple markers on a goal.
-		A - _ = A1
+		% Inappropriate placement of an impurity marker, so we treat
+		% it like a predicate call.  typecheck.m prints out something
+		% descriptive for these errors.
+		purity_name(Purity, PurityString),
+		A = call(unqualified(PurityString), [A0], pure)
 	).
 
 
--- last-purity.m	Fri Dec  5 18:25:26 1997
+++ purity.m	Tue Dec  2 16:50:13 1997
@@ -110,11 +110,11 @@
 
 :- import_module make_hlds, hlds_data, hlds_pred, hlds_goal, prog_io_util.
 :- import_module type_util, mode_util, code_util, prog_data, unify_proc.
-:- import_module globals, options, mercury_to_mercury, hlds_out, int, set.
+:- import_module globals, options, mercury_to_mercury, hlds_out.
 :- import_module passes_aux, typecheck, module_qual, clause_to_proc.
-:- import_module modecheck_unify, modecheck_call, inst_util.
-:- import_module list, map, varset, term, prog_out, string, require, std_util.
-:- import_module assoc_list, bool.
+:- import_module modecheck_unify, modecheck_call, inst_util, prog_out.
+:- import_module list, map, varset, term, string, require, std_util.
+:- import_module assoc_list, bool, int, set.
 
 %-----------------------------------------------------------------------------%
 %				Public Predicates
@@ -255,7 +255,7 @@
 %  this is indicated by already having the appropriate goal feature.  (During
 %  the translation from term to goal, calls have their purity attached to
 %  them, and in the translation from goal to hlds_goal, the attached purity is
-%  turned into the appropriate feature in the hlds_goal_info.
+%  turned into the appropriate feature in the hlds_goal_info.)
 
 :- pred puritycheck_pred(pred_id, pred_info, pred_info, module_info, int,
 		io__state, io__state).
@@ -309,12 +309,12 @@
 	[].
 compute_purity([Clause0|Clauses0], [Clause|Clauses], PredInfo, ModuleInfo,
 		Purity0, Purity, NumErrors0, NumErrors) -->
-	{ Clause0 = clause(Ids, Body0-Info0, Context) },
+	{ Clause0 = clause(Ids, Body0 - Info0, Context) },
 	compute_expr_purity(Body0, Body, Info0, PredInfo, ModuleInfo,
 			    no, Bodypurity, NumErrors0, NumErrors1),
 	{ add_goal_info_purity_feature(Info0, Bodypurity, Info) },
 	{ worst_purity(Purity0, Bodypurity, Purity1) },
-	{ Clause = clause(Ids, Body-Info, Context) },
+	{ Clause = clause(Ids, Body - Info, Context) },
 	compute_purity(Clauses0, Clauses, PredInfo, ModuleInfo,
 		       Purity1, Purity, NumErrors1, NumErrors).
 
@@ -378,8 +378,8 @@
 	{ Unif  = unify(A,RHS,C,D,E) },
 	error_if_body_purity_indicated(GoalInfo, NumErrors0, NumErrors1,
 				       InClosure, "unification"),
-	(   { RHS0 = lambda_goal(F, G, H, I, Goal0-Info0) } ->
-		{ RHS = lambda_goal(F, G, H, I, Goal-Info0) },
+	(   { RHS0 = lambda_goal(F, G, H, I, Goal0 - Info0) } ->
+		{ RHS = lambda_goal(F, G, H, I, Goal - Info0) },
 		compute_expr_purity(Goal0, Goal, Info0, PredInfo, ModuleInfo,
 				    yes, Purity, NumErrors1, NumErrors2),
 		error_if_closure_impure(GoalInfo, Purity,
@@ -415,15 +415,20 @@
 			    InClosure, Purity3, NumErrors2, NumErrors),
 	{ worst_purity(Purity1, Purity2, Purity12) },
 	{ worst_purity(Purity12, Purity3, Purity) }.
-compute_expr_purity(Ccode, Ccode, _, _, _, _, pure, NumErrors, NumErrors) -->
-	{ Ccode = pragma_c_code(_,_,_,_,_,_,_,_) }.
+compute_expr_purity(Ccode, Ccode, _, _, ModuleInfo, _, Purity,
+		NumErrors, NumErrors) -->
+	{ Ccode = pragma_c_code(_,_,PredId,_,_,_,_,_) },
+	{ module_info_preds(ModuleInfo, Preds) },
+	{ map__lookup(Preds, PredId, PredInfo) },
+	{ pred_info_get_purity(PredInfo, Purity) }.
+	
 
 
 :- pred compute_goal_purity(hlds_goal, hlds_goal, pred_info,
 	module_info, bool, purity, int, int, io__state, io__state).
 :- mode compute_goal_purity(in, out, in, in, in, out, in, out, di, uo) is det.
 
-compute_goal_purity(Goal0-GoalInfo0, Goal-GoalInfo, PredInfo, ModuleInfo,
+compute_goal_purity(Goal0 - GoalInfo0, Goal - GoalInfo, PredInfo, ModuleInfo,
 		InClosure, Purity, NumErrors0, NumErrors) -->
 	compute_expr_purity(Goal0, Goal, GoalInfo0, PredInfo, ModuleInfo,
 			    InClosure, Purity, NumErrors0, NumErrors),
@@ -481,19 +486,19 @@
 	{ pred_info_context(PredInfo, Context) },
 	write_context_and_pred_id(ModuleInfo, PredInfo, PredId),
 	prog_out__write_context(Context),
-	report_warning("  warning: declared "),
+	report_warning("  warning: declared `"),
 	write_purity(Purity),
-	io__write_string(" but promised pure.\n"),
+	io__write_string("' but promised pure.\n"),
 	globals__io_lookup_bool_option(verbose_errors, VerboseErrors),
 	( { VerboseErrors = yes } ->
 		{ pred_info_get_is_pred_or_func(PredInfo, PredOrFunc) },
 		prog_out__write_context(Context),
 		io__write_string("  A pure "),
 		hlds_out__write_pred_or_func(PredOrFunc),
-		io__write_string(" that invokes non-pure code should be\n"),
+		io__write_string(" that invokes impure or semipure code should\n"),
 		prog_out__write_context(Context),
 		io__write_string(
-		    "  promised pure and should have no impurity declaration.\n"
+		    "  be promised pure and should have no impurity declaration.\n"
 		)
 	;
 		[]
@@ -509,9 +514,9 @@
 	{ pred_info_context(PredInfo, Context) },
 	write_context_and_pred_id(ModuleInfo, PredInfo, PredId),
 	prog_out__write_context(Context),
-	report_warning("  warning: declared "),
+	report_warning("  warning: declared `"),
 	write_purity(DeclPurity),
-	io__write_string(" but actually "),
+	io__write_string("' but actually "),
 	write_purity(AcutalPurity),
 	io__write_string(".\n").
 
@@ -531,7 +536,7 @@
 		io__write_string("  This "),
 		hlds_out__write_pred_or_func(PredOrFunc),
 		io__write_string(
-		    " does not invoke any non-pure code,\n"
+		    " does not invoke any impure or semipure code,\n"
 		),
 		prog_out__write_context(Context),
 		io__write_string(
--- last-typecheck.m	Fri Dec  5 18:25:29 1997
+++ typecheck.m	Thu Dec  4 15:00:20 1997
@@ -3912,12 +3967,14 @@
 language_builtin("\\=", 2).
 language_builtin(",", 2).
 language_builtin(";", 2).
-language_builtin("\\+", 2).
-language_builtin("not", 2).
+language_builtin("\\+", 1).
+language_builtin("not", 1).
 language_builtin("<=>", 2).
 language_builtin("=>", 2).
 language_builtin("<=", 2).
 language_builtin("call", _).
+language_builtin("impure", 1).
+language_builtin("semipure", 1).
 
 :- pred write_call_context(term__context, pred_call_id, int, unify_context,
 				io__state, io__state).
--- last-reference_manual.texi	Fri Dec  5 18:25:50 1997
+++ reference_manual.texi	Fri Dec  5 18:03:06 1997
@@ -2616,10 +2616,9 @@
 the @dfn{strict commutative} operational semantics.  This semantics
 is equivalent to the strict sequential operational semantics except
 that there is no requirement that function calls, conjunctions and disjunctions 
-be executed left-to-right; they may be executed in any order.
- at c XXX May they be interleaved?
-(The order may even be different each time a particular goal
-is entered.)
+be executed left-to-right; they may be executed in any order, and may
+even be interleaved.  Furthermore, the order may even be different each
+time a particular goal is entered.
 
 As well as providing the strict sequential operational semantics,
 Mercury implementations may optionally provide additional
@@ -3354,7 +3353,7 @@
 @node Impurity
 @section Impurity declarations
 
-In order to efficiently implement certain predicates, it is sometimes
+In order to efficiently implement certain predicates, it is occasionally
 necessary to venture outside pure logic programming.  Other predicates
 cannot be implemented at all within the paradigm of logic programming,
 for example, all solutions predicates.  Such predicates are often
@@ -3373,6 +3372,14 @@
 that are implemented in terms of impure predictes are assumed to be
 impure themselves unless they are explicitly promised to be pure.
 
+Please note that the facilities described here are needed only very
+rarely.  The main intent is for implementing language primitives such as
+the all solutions predicates.  Any use of `impure' or `semipure'
+probably indicates either a weakness in the Mercury standard library, or
+the programmer's lack of familiarity with the standard library.
+Newcomers to Mercury are hence encouraged to @strong{skip this section}.
+
+
 
 @menu
 * Purity levels::       Choosing the right level of purity
@@ -3401,7 +3408,7 @@
 is the behavior of pure predicates ever affected by the invocation of
 other predicates.
 
-Most Mercury predicates are pure.  
+The vast majority of Mercury predicates are pure.  
 
 @item semipure
 Semipure predicates are just like pure predicates, except that their
@@ -3437,13 +3444,14 @@
 
 The operational semantics of a Mercury predicate which invokes impure
 code is a modified form of the @emph{strict sequential} semantics
-(@pxref{Semantics}).  Firstly, only impure goals may not be reordered
-relative to other goals, pure and semipure goals may be reordered as
-long as they are not moved across an impure goal.  Secondly, not even
-``minimal'' reordering of impure goals is permitted; if such reordering
-is needed, this is an error.  Execution of impure goals is strict: they
-must be executed if they are reached, even if it can be determined that
-that computation cannot lead to successful termination.
+(@pxref{Semantics}).  @emph{Impure} goals may not be reordered relative
+to any other goals; not even ``minimal'' reordering as implied by the
+modes is permitted.  If any such reordering is needed, this is a mode
+error.  However, @emph{pure} and @emph{semipure} goals may be reordered
+as long as they are not moved across an impure goal.  Execution of
+impure goals is strict: they must be executed if they are reached, even
+if it can be determined that that computation cannot lead to successful
+termination.
 
 Semipure goals can be given a ``contextual'' declarative semantics.
 They cannot have any side-effects, so it is expected that, given the
@@ -3460,9 +3468,9 @@
 @node Declaring impurity
 @subsection Declaring predicate impurity
 
-A predicate is declared to be impure (semipure) by preceding the word
- at code{predicate} in its @code{pred} declaration with @code{impure}
-(@code{semipure}).  That is, a declaration of the form:
+A predicate is declared to be impure or semipure by preceding the word
+ at code{pred} in its @code{pred} declaration with @code{impure}
+or @code{semipure}, respectively.  That is, a declaration of the form:
 
 @example
 :- impure pred @var{Pred}(@var{Arguments...}).
@@ -3482,13 +3490,19 @@
 @node Impure calls
 @subsection Marking a call as impure
 
-If a predicate is impure (semipure), all calls to it must be preceded
-with the word @code{impure} (@code{semipure}).  This allows someone
+If a predicate is impure or semipure, all calls to it must be preceded
+with the word @code{impure} or @code{semipure}, respectively.  Note
+that only predicate calls need to (and are permitted to) be prefixed
+with @code{impure} or @code{semipure}, compound goals never need this.
+See @ref{Impurity Example} for an example of this.
+
+The requiremment that impure or semipure calls be marked with
+ at code{impure} or @code{semipure} allows someone 
 reading the code to tell which goals are not pure, making code which
-relies on side effects somewhat less mysterious.  See @ref{Impurity
-Example} for an example of this.  Note that only predicate calls need to
-be prefixed with @code{impure} or @code{semipure}, compound goals never
-need this.
+relies on side effects somewhat less mysterious.  Furthermore, it means
+that if a call is @emph{not} preceded by @code{impure} or
+ at code{semipure}, then the reader can rely on the call having a proper
+declarative semantics, without hidden side-effects.  
 
 
 @node Promising purity
@@ -3496,12 +3510,13 @@
 
 Some predicates which call impure or semipure predicates are themselves
 pure.  In fact, the main purpose of the Mercury impurity system is to
-allow users to write pure predicates using impure ones, while protecting
+allow programmers to write pure predicates using impure ones, while protecting
 the procedural implementation from aggressive compiler optimizations.
 Of course, the Mercury compiler cannot verify that a predicate is pure,
-so this is the user's responsibility.
+so it is the programmer's responsibility to ensure this.  If a predicate
+is promised pure and is not, the behavior of the program is undefined.
 
-The user may promise that a predicate is pure using the
+The programmer may promise that a predicate is pure using the
 @code{promise_pure} pragma:
 
 @example
@@ -3517,12 +3532,13 @@
 and so is not useful as is.  It is meant only as an example.
 
 @example
-:- pragma c_header_code("int max;").
+:- pragma c_header_code("#include <limits.h>").
+:- pragma c_header_code("Integer max;").
 
 :- impure pred init_max is det.
 :- pragma c_code(init_max,
         will_not_call_mercury,
-        "max = (int)(~(((unsigned)(~0))>>1));").
+        "max = INT_MIN;").
 
 :- impure pred set_max(int::in) is det.
 :- pragma c_code(set_max(X::in),
--- last-transition_guide.texi	Fri Dec  5 18:25:51 1997
+++ transition_guide.texi	Tue Dec  2 14:54:45 1997
@@ -152,9 +152,10 @@
 and             xfy             720
 else            xfy             1170
 end_module      fx              1199
+func            fx              800
 if              fx              1160
 import_module   fx              1199
-impure          fy              1199
+impure          fy              800
 inst            fx              1199
 is              xfx             700
 mod             xfx             300
@@ -162,9 +163,9 @@
 module          fx              1199
 not             fy              900
 or              xfy             740
-pred            fx              1180
+pred            fx              800
 rule            fx              1199
-semipure        fy              1199
+semipure        fy              800
 some            fxy             950
 then            xfx             1150
 type            fx              1180
--- last-purity.m	Fri Dec  5 18:28:43 1997
+++ purity.m	Tue Dec  2 16:06:55 1997
@@ -11,13 +11,20 @@
 main -->
 	impure test1,
 	impure test2,
-	impure test3.
+	impure test3,
+	impure test1_inline,
+	impure test2_inline,
+	impure test3_inline.
 
 
 :- impure pred test1(io__state::di, io__state::uo) is det.
 :- impure pred test2(io__state::di, io__state::uo) is det.
 :- impure pred test3(io__state::di, io__state::uo) is det.
 
+:- impure pred test1_inline(io__state::di, io__state::uo) is det.
+:- impure pred test2_inline(io__state::di, io__state::uo) is det.
+:- impure pred test3_inline(io__state::di, io__state::uo) is det.
+
 
 :- impure pred set_x(int::in) is det.
 :- pragma c_code(set_x(X::in), will_not_call_mercury, "x=X;" ).
@@ -29,6 +36,19 @@
 :- pragma c_code(get_x(X::out), will_not_call_mercury, "X=x;").
 
 
+:- impure pred set_x_inline(int::in) is det.
+:- pragma c_code(set_x_inline(X::in), will_not_call_mercury, "x=X;" ).
+:- pragma inline(set_x_inline/1).
+
+:- impure pred incr_x_inline is det.
+:- pragma c_code(incr_x_inline, will_not_call_mercury, "++x;" ).
+:- pragma inline(incr_x_inline/0).
+
+:- semipure pred get_x_inline(int::out) is det.
+:- pragma c_code(get_x_inline(X::out), will_not_call_mercury, "X=x;").
+:- pragma inline(get_x_inline/1).
+
+
 :- pragma c_header_code("int x = 0;").
 
 
@@ -54,6 +74,32 @@
 	(   { impure incr_x },
 	    { fail }
 	;   { semipure get_x(Y) },
+	    io__format("%d\n", [i(Y)])
+	).
+
+%  Now do it all again with inlining requested
+
+test1_inline -->
+	{ semipure get_x_inline(X) },
+	io__format("%d\n", [i(X)]),
+	{ impure set_x_inline(X+1) },
+	{ semipure get_x_inline(Y) },
+	io__format("%d\n", [i(Y)]).
+
+
+% tempt compiler to optimize away duplicate impure goals, or to compile away
+% det goals with no outputs.
+test2_inline -->
+	{ impure incr_x_inline },
+	{ impure incr_x_inline },
+	{ semipure get_x_inline(Y) },
+	io__format("%d\n", [i(Y)]).
+
+% tempt compiler to optimize away impure goal in branch that cannot succeed.
+test3_inline -->
+	(   { impure incr_x_inline },
+	    { fail }
+	;   { semipure get_x_inline(Y) },
 	    io__format("%d\n", [i(Y)])
 	).
 
--- last-purity.exp	Fri Dec  5 18:28:44 1997
+++ purity.exp	Tue Dec  2 16:09:08 1997
@@ -2,3 +2,7 @@
 1
 3
 4
+4
+5
+7
+8
New File: tests/invalid/purity.err_exp
===================================================================
purity.m:028: In predicate `purity:w1/0':
purity.m:028:   warning: declared `impure' but actually pure.
purity.m:032: In predicate `purity:w2/0':
purity.m:032:   warning: declared `semipure' but actually pure.
purity.m:036: In predicate `purity:w3/0':
purity.m:036:   warning: declared `impure' but actually semipure.
purity.m:040: In predicate `purity:w4/0':
purity.m:040:   warning: unnecessary `promise_pure' pragma.
purity.m:045: In predicate `purity:w5/0':
purity.m:045:   warning: declared `impure' but promised pure.
purity.m:050: In predicate `purity:w6/0':
purity.m:050:   warning: declared `semipure' but promised pure.
purity.m:059: In predicate `purity:e1/0':
purity.m:059:   error: predicate is impure.
purity.m:059:   It must be declared `impure' or promised pure.
purity.m:064: In predicate `purity:e2/0':
purity.m:064:   error: predicate is semipure.
purity.m:064:   It must be declared `semipure' or promised pure.
purity.m:068: In predicate `purity:e3/0':
purity.m:068:   error: predicate is impure.
purity.m:068:   It must be declared `impure' or promised pure.
purity.m:074: In call to impure predicate `purity:imp/0':
purity.m:074:   error: call must be preceded by `impure' indicator.
purity.m:078: In call to semipure predicate `purity:semi/0':
purity.m:078:   error: call must be preceded by `semipure' indicator.
purity.m:112: Error in closure: closure is impure.
purity.m:118: Error in closure: closure is semipure.
purity.m:093: In unification predicate for type (purity:e8):
purity.m:093:   error: predicate is impure.
purity.m:093:   It must be pure.
purity.m:101: In unification predicate for type (purity:e9):
purity.m:101:   error: predicate is semipure.
purity.m:101:   It must be pure.
purity.m:083: In clause for `e6':
purity.m:083:   in argument 1 of call to predicate `purity:in/1':
purity.m:083:   mode error: variable `X' has instantiatedness `free',
purity.m:083:   expected instantiatedness was `ground'.
purity.m:083:   The goal could not be reordered, because
purity.m:083:   it was followed by an impure goal.
purity.m:084:   This is the location of the impure goal.
purity.m:090: In clause for `e7':
purity.m:090:   in argument 1 of call to predicate `purity:imp1/1':
purity.m:090:   mode error: variable `X' has instantiatedness `free',
purity.m:090:   expected instantiatedness was `ground'.
purity.m:090:   The goal could not be reordered, because it was impure.
For more information, try recompiling with `-E'.

New File: tests/invalid/purity.m
===================================================================
:- module purity.

%----------------------------------------------------------------
%  Needed for later tests.

:- type foo ---> a ; b.

:- impure pred imp is det.
:- pragma c_code(imp, will_not_call_mercury, ";").

:- semipure pred semi is semidet.
:- pragma c_code(semi, will_not_call_mercury, "SUCCESS_INDICATOR=0;").

:- pred in(foo).
:- mode in(in) is semidet.
in(a).

:- impure pred imp1(foo).
:- mode imp1(in) is semidet.
:- pragma c_code(imp1(_X::in), will_not_call_mercury, "SUCCESS_INDICATOR=0;").



%----------------------------------------------------------------
%  Warnings


:- impure pred w1 is det.

w1.

:- semipure pred w2 is det.

w2.

:- impure pred w3 is semidet.

w3 :- semipure semi.

:- pred w4 is det.
:- pragma promise_pure(w4/0).

w4.

:- impure pred w5 is det.
:- pragma promise_pure(w5/0).

w5 :- impure imp.

:- semipure pred w6 is semidet.
:- pragma promise_pure(w6/0).

w6 :- semipure semi.


%----------------------------------------------------------------
%  Errors

:- pred e1 is det.

e1 :- impure imp.


:- pred e2 is semidet.

e2 :- semipure semi.

:- semipure pred e3 is det.

e3 :- impure imp.

:- impure pred e4 is det.

e4 :- imp.

:- semipure pred e5 is semidet.

e5 :- semi.

:- impure pred e6 is semidet.

e6 :-
	in(X),
	impure imp,
	X = a.

:- impure pred e7 is semidet.

e7 :-
	impure imp1(X),
	X = a.

:- type e8 ---> e8(foo) where equality is imp2.

:- impure pred imp2(e8, e8).
:- mode imp2(in, in) is semidet.

:- pragma c_code(imp2(_X::in, _Y::in), will_not_call_mercury,
	"SUCCESS_INDICATOR=0;").

:- type e9 ---> e9(foo) where equality is semi2.

:- semipure pred semi2(e9, e9).
:- mode semi2(in, in) is semidet.

:- pragma c_code(semi2(_X::in, _Y::in), will_not_call_mercury,
	"SUCCESS_INDICATOR=0;").

:- pred e10 is semidet.

e10 :-
	Goal1 = lambda([] is semidet, imp1(b)),
	call(Goal1).

:- pred e11 is semidet.

e11 :-
	Goal2 = lambda([] is semidet, semi),
	call(Goal2).

New File: tests/invalid/purity_nonsense.err_exp
===================================================================
purity_nonsense.m:003: Error: invalid impurity declaration: func undefined_func = foo.
purity_nonsense.m:005: Syntax error at token 'type': unexpected token at start of (sub)term.
purity_nonsense.m:006: Syntax error at token 'mode': unexpected token at start of (sub)term.
purity_nonsense.m:010: Error: `promise_pure' pragma for purity_nonsense:undefined2/0
purity_nonsense.m:010:   without corresponding `pred' or `func' declaration.
purity_nonsense.m:012: Error: clause for predicate `purity_nonsense:e12/0'
purity_nonsense.m:012:   without preceding `pred' declaration.
purity_nonsense.m:013: Error: clause for predicate `purity_nonsense:e13/0'
purity_nonsense.m:013:   without preceding `pred' declaration.
purity_nonsense.m:008: Error: no clauses for predicate `purity_nonsense:undefined/0'
purity_nonsense.m:012: In clause for predicate `purity_nonsense:e12/0':
purity_nonsense.m:012:   in argument 1 of call to predicate `impure/1':
purity_nonsense.m:012:   error: the language construct \+/1 should be
purity_nonsense.m:012:   used as a goal, not as an expression.
purity_nonsense.m:012: In clause for predicate `purity_nonsense:e12/0':
purity_nonsense.m:012:   in argument 1 of call to predicate `impure/1':
purity_nonsense.m:012:   in argument 1 of functor `\+/1':
purity_nonsense.m:012:   error: the language construct impure/1 should be
purity_nonsense.m:012:   used as a goal, not as an expression.
purity_nonsense.m:012: In clause for predicate `purity_nonsense:e12/0':
purity_nonsense.m:012:   in argument 1 of call to predicate `impure/1':
purity_nonsense.m:012:   in argument 1 of functor `\+/1':
purity_nonsense.m:012:   in argument 1 of functor `impure/1':
purity_nonsense.m:012:   error: undefined symbol `imp/0'.
purity_nonsense.m:012: In clause for predicate `purity_nonsense:e12/0':
purity_nonsense.m:012:   error: `impure' marker in an inappropriate place.
purity_nonsense.m:013: In clause for predicate `purity_nonsense:e13/0':
purity_nonsense.m:013:   in argument 1 of call to predicate `semipure/1':
purity_nonsense.m:013:   error: the language construct \+/1 should be
purity_nonsense.m:013:   used as a goal, not as an expression.
purity_nonsense.m:013: In clause for predicate `purity_nonsense:e13/0':
purity_nonsense.m:013:   in argument 1 of call to predicate `semipure/1':
purity_nonsense.m:013:   in argument 1 of functor `\+/1':
purity_nonsense.m:013:   error: the language construct semipure/1 should be
purity_nonsense.m:013:   used as a goal, not as an expression.
purity_nonsense.m:013: In clause for predicate `purity_nonsense:e13/0':
purity_nonsense.m:013:   in argument 1 of call to predicate `semipure/1':
purity_nonsense.m:013:   in argument 1 of functor `\+/1':
purity_nonsense.m:013:   in argument 1 of functor `semipure/1':
purity_nonsense.m:013:   error: undefined symbol `semi/0'.
purity_nonsense.m:013: In clause for predicate `purity_nonsense:e13/0':
purity_nonsense.m:013:   error: `semipure' marker in an inappropriate place.
For more information, try recompiling with `-E'.

New File: tests/invalid/purity_nonsense.m
===================================================================
:- module purity_nonsense.

:- impure func undefined_func = foo.		% no impure functions (yet)

:- impure type badtype ---> oops.
:- impure mode badmode :: free -> free.

:- impure pred undefined.
:- pragma promise_pure(undefined/0).
:- pragma promise_pure(undefined2/0).

e12 :- impure (\+ impure imp).
e13 :- semipure (\+ semipure semi).





More information about the developers mailing list