[m-dev.] for review: impure functions

David Glen JEFFERY dgj at cs.mu.OZ.AU
Wed Apr 5 17:37:50 AEST 2000


On 04-Apr-2000, Tyson Dowd <trd at cs.mu.OZ.AU> wrote:
> 
> Add impure functions to Mercury, clean up the purity module somewhat,
> fix some bugs in purity, update and expand the purity documentation, 
> and re-organize the purity checks.

> compiler/notes/authors.html:
> 	Add Peter Schachte to the authors list.

As an aside, there seem to be a few other people missing from the authors list
too...

> doc/reference_manual.texi:
> 	Document impure functions.
> 	Expand more on what impure predicates/functions can do.
> 	Explain the concept of worst purity, and use it to explain the
> 	"inferred purity"/"declared purity" concepts.
> 	Make it more explicit that only impure goals obey
> 	strict-sequential like semantics.
> 
> tests/invalid/type_inf_loop.err_exp2:
> 	Fix this test case to reflect the new error message new that we 
> 	check the purity of this code correctly (or rather, we correctly
> 	fail to be able to purity check this code).
> 
> tests/hard_coded/Mmakefile:
> tests/hard_coded/purity.exp:
> tests/hard_coded/purity.m:
> tests/hard_coded/purity/Mmakefile:
> tests/hard_coded/purity/impure_func_t1.m:
> tests/hard_coded/purity/purity.m:
> tests/hard_coded/purity/runtests:
> 	Remove purity tests from the hard_coded directory, give it a
> 	sub-directory of its own.
> 
> tests/invalid/Mmakefile:
> tests/invalid/purity.err_exp:
> tests/invalid/purity.m:
> tests/invalid/purity_nonsense.err_exp:
> tests/invalid/purity_nonsense.m:
> tests/invalid/purity/Mmakefile:
> tests/invalid/purity/impure_func_t2.err_exp:
> tests/invalid/purity/impure_func_t2.m:
> tests/invalid/purity/impure_func_t3.err_exp:
> tests/invalid/purity/impure_func_t3.m:
> tests/invalid/purity/impure_func_t4.err_exp:
> tests/invalid/purity/impure_func_t4.m:
> tests/invalid/purity/impure_func_t5.err_exp:
> tests/invalid/purity/impure_func_t5.m:
> tests/invalid/purity/impure_pred_t1.err_exp:
> tests/invalid/purity/impure_pred_t1.m:
> tests/invalid/purity/impure_pred_t2.err_exp:
> tests/invalid/purity/impure_pred_t2.m:
> tests/invalid/purity/purity.err_exp:
> tests/invalid/purity/purity.m:
> tests/invalid/purity/purity_nonsense.err_exp:
> tests/invalid/purity/purity_nonsense.m:
> tests/invalid/purity/runtests:
> 	Remove purity tests from the invalid directory, give it a
> 	sub-directory of its own.

> Index: compiler/prog_io_dcg.m
> ===================================================================
> RCS file: /home/mercury1/repository/mercury/compiler/prog_io_dcg.m,v
> retrieving revision 1.14
> diff -u -r1.14 prog_io_dcg.m
> --- compiler/prog_io_dcg.m	2000/01/13 06:16:56	1.14
> +++ compiler/prog_io_dcg.m	2000/03/09 12:26:24

>  parse_dcg_goal_2("=", [A0], Context, VarSet, N, Var, Goal, VarSet, N, Var) :-
>  	term__coerce(A0, A),
> -	Goal = unify(A, term__variable(Var)) - Context.
> +	Goal = unify(A, term__variable(Var), pure) - Context.
>  
>  	% Call to ':='/1 - unify argument with DCG output arg.
>  parse_dcg_goal_2(":=", [A0], Context, VarSet0, N0, _Var0,
>  		Goal, VarSet, N, Var) :-
>  	new_dcg_var(VarSet0, N0, VarSet, N, Var),
>  	term__coerce(A0, A),
> -	Goal = unify(A, term__variable(Var)) - Context.
> +	Goal = unify(A, term__variable(Var), pure) - Context.

It would be nice to eventually be able to have dcg goals of the form
	...
	impure =(an_impure_function(...))
	...
or
	...
	impure :=(an_impure_function(...))
	...

> ===================================================================
> RCS file: /home/mercury1/repository/mercury/compiler/purity.m,v
> retrieving revision 1.23
> diff -u -r1.23 purity.m
> --- compiler/purity.m	2000/03/27 05:07:49	1.23
> +++ compiler/purity.m	2000/04/04 08:19:13

> +	; { PurityCheckResult = no_impure_in_closure },
> +			% We catch this error at the creation of the closure
> +			% It might also make sense to flag missing
> +			% impurity declarations inside closures, but we
> +			% don't do so currently.
> +		{ NumErrors = NumErrors0 }

Do you mean that we don't give an error for a call to an impure pred without
an `impure' annotation inside a closure? If so, you should mark that with
an XXX at least.

> +:- pred check_higher_order_purity(module_info, pred_info,
> +	hlds_goal_info, cons_id, prog_var, list(prog_var),
> +	int, int, purity, io__state, io__state).
> +:- mode check_higher_order_purity(in, in, in, in, in, in, in, out, out, 
> +	di, uo) is det.
> +check_higher_order_purity(ModuleInfo, PredInfo, GoalInfo, ConsId, Var, Args,
> +	NumErrors0, NumErrors, ActualPurity) -->
> +	{ pred_info_clauses_info(PredInfo, ClausesInfo) },
> +	{ clauses_info_vartypes(ClausesInfo, VarTypes) },
> +	{ map__lookup(VarTypes, Var, TypeOfVar) },
> +	( 
> +		{ ConsId = cons(PName, _) },
> +		{ type_is_higher_order(TypeOfVar, PredOrFunc,
> +			_EvalMethod, VarArgTypes) }
> +	->
> +		{ pred_info_typevarset(PredInfo, TVarSet) },
> +		{ map__apply_to_list(Args, VarTypes, ArgTypes0) },
> +		{ list__append(ArgTypes0, VarArgTypes, PredArgTypes) },
> +		( 
> +			{ get_pred_id(PName, PredOrFunc, TVarSet, PredArgTypes,
> +				ModuleInfo, CalleePredId) }
> +		->
> +			{ module_info_pred_info(ModuleInfo,
> +				CalleePredId, CalleePredInfo) },
> +			{ pred_info_get_purity(CalleePredInfo, Purity) },
> +			( { Purity = pure } ->
> +				{ NumErrors = NumErrors0 }
> +			;
> +				{ goal_info_get_context(GoalInfo, CallContext) },
> +				error_missing_body_impurity_decl(ModuleInfo,
> +					CalleePredInfo, CalleePredId,
> +					CallContext, Purity),
> +				{ NumErrors is NumErrors0 + 1 }
> +			)
> +		;
> +			{ goal_info_get_context(GoalInfo, CallContext) },
> +			error_unknown_predicate(CallContext),
> +			{ Purity = pure },
> +			{ NumErrors is NumErrors0 + 1 }

Doesn't this else case correspond to unresolved overloading, and therefore
wouldn't this error be reported elsewhere?

> +:- pred error_unknown_predicate(prog_context, io__state, io__state).
> +:- mode error_unknown_predicate(in, di, uo) is det.
> +
> +error_unknown_predicate(Context) -->
> +	prog_out__write_context(Context),
> +	io__write_string("Unable to purity check this unification.\n"),
> +	prog_out__write_context(Context),
> +	io__write_string("  The purity of the higher order term is unknown.\n"),
> +	prog_out__write_context(Context),
> +	io__write_string("  This can occur if type inference fails.\n"),
> +	prog_out__write_context(Context),
> +	io__write_string("  You should declare the types explicitly.\n").






> @@ -5372,13 +5379,15 @@
>  input/output) without taking an io__state (@pxref{Types}) as input and
>  returning one as output, and do not make any changes to any data
>  structure that will not be undone on backtracking (unless the data
> -structure would be unreachable on backtracking).  The behaviour of other
> -predicates is never affected by the invocation of pure predicates, nor
> -is the behaviour of pure predicates ever affected by the invocation of
> -other predicates.
> +structure would be unreachable on backtracking).  The behaviour of pure
> +predicates is never affected by the invocation of pure predicates.
> +It is possible for the invocation of pure predicate to affect the
> +behaviour of non-pure predicates and vice versa.

The last sentence sounds a little fishy to me. Surely the invocation of
something pure can't affect the behaviour of anything.

There's also a typo: s/of pure predicate/of pure predicates/

> +The compiler should issue a warning if the declared purity of a goal is
> +less pure than its inferred purity.

I'm not sure that the language reference manual should talk about what warnings
the compiler should be expected to produce. (Unless there is a precedent
elsewhere in the manual that I wasn't aware of).

> @@ -5476,11 +5525,23 @@
>  
>  @node Promising purity
>  @section Promising that a predicate is pure
> +
> +Predicates that are implemented in terms of impure or semipure predicates are
> +assumed to have the @emph{worst impurity} of the goals in their body.
> +The declared purity of a predicate must not be more pure
> +than the inferred purity; if it is, the compiler must generate an error.
> +If the declared purity is less pure than the inferred purity, the
> +compiler should issue a warning (this is similar to the above case for
> +goals).

Ditto.

> Index: tests/invalid/purity/impure_pred_t2.m
> ===================================================================
> RCS file: impure_pred_t2.m
> diff -N impure_pred_t2.m
> --- /dev/null	Thu Mar  4 04:20:11 1999
> +++ impure_pred_t2.m	Sun Mar 26 17:55:34 2000
> @@ -0,0 +1,31 @@
> +
> +% Subverting the Mercury purity system.
> +
> +% This should not be possible.
> +
> +:- module impure_pred_t2.
> +
> +:- interface.
> +
> +:- import_module io.
> +
> +:- pred main(state::di, state::uo) is det.
> +
> +:- implementation.
> +:- import_module int, require, list.
> +
> +:- type foo ---> foo(pred(int,int)).

That type decl is unused.

> +
> +main -->
> +	{ Y = get_counter },
> +	{ Y(4, Z) },
> +	print("X = "), 
> +	print(Z), 
> +	nl.
> +
> +:- impure pred get_counter(int::in, int::out) is det.
> +
> +:- pragma c_header_code("extern Integer counter;").
> +:- pragma c_code("Integer counter = 0;").
> +:- pragma c_code(get_counter(Y::in, X::out), will_not_call_mercury, "X = counter + Y;").
> +

> ===================================================================
> RCS file: purity_nonsense.err_exp
> diff -N purity_nonsense.err_exp
> --- /dev/null	Thu Mar  4 04:20:11 1999
> +++ purity_nonsense.err_exp	Wed Nov 10 09:40:13 1999
> @@ -0,0 +1,47 @@

This test case no longer looks right...

> +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:018: Warning: unnecessary `impure' marker.
> +purity_nonsense.m:018:   Higher-order goals are always pure.
> +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.
> +purity_nonsense.m:015: In predicate `purity_nonsense:e14/1':
> +purity_nonsense.m:015:   warning: declared `impure' but actually pure.
> +For more information, try recompiling with `-E'.
> Index: tests/invalid/purity/purity_nonsense.m
> ===================================================================
> RCS file: purity_nonsense.m
> diff -N purity_nonsense.m
> --- /dev/null	Thu Mar  4 04:20:11 1999
> +++ purity_nonsense.m	Tue Jan  6 17:31:42 1998
> @@ -0,0 +1,18 @@
> +:- 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).
> +
> +:- impure pred e14(pred).
> +:- mode e14(((pred) is semidet)) is semidet.
> +
> +e14(P) :- impure call(P).


dgj
-- 
David Jeffery (dgj at cs.mu.oz.au) | If your thesis is utterly vacuous
PhD student,                    | Use first-order predicate calculus.
Dept. of Comp. Sci. & Soft. Eng.|     With sufficient formality
The University of Melbourne     |     The sheerist banality
Australia                       | Will be hailed by the critics: "Miraculous!"
                                |     -- Anon.
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list