exception handling

Fergus Henderson fjh at cs.mu.oz.au
Fri Feb 28 02:20:21 AEDT 1997


Hi,

Obviously I have too much time on my hands, so (following my discussion
with Bart on this topic last week) I implemented exception handling.

I'm still not sure whether we want exception handling from
a language design perspective.  I think it would be a good idea to
implement destructive update properly before we make any definite
decisions on exception handling, because these two features interact
in nasty ways.

Because of the above, I don't plan to commit this change in the
near future, at least not as part of the baseline.
(Perhaps we should have a special directory in the CVS repository
for things like this?)

Still, an implementation of exception handling is a nice thing
to have lying around for a time when we might need it.

Comments/feedback welcome.

The change is very self-contained: I didn't modify any of the
existing files, I just added a new file `exception.m' which
implements the library predicates for throwing and catching exceptions.
If/when this change hits the big-time, it might also be a good idea to
change error/1 in library/require.m to throw an exception rather than
aborting.

library/exception.m:
	New file: adds support for exception handling.

tests/hard_coded/test_exceptions.m:
	New file: some tests of exception handling.

%---------------------------------------------------------------------------%
% Copyright (C) 1997 The University of Melbourne.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%---------------------------------------------------------------------------%

% File: exception.m.
% Main author: fjh.
% Status: not supported!

% This file contains experimental code for exception handling.
% Do not use it.

% XXX this won't work in non-gc grades, because it doesn't have code to
% deep_copy() the thrown exception to the solutions_heap and back;
% instead it just calls fatal_error().

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%

:- module exception.
:- interface.
:- import_module std_util, io.

%
% throw(Exception):
%	Throw the specified exception.
%
:- pred throw(T).
:- mode throw(in) is erroneous.

%
% try(Goal, Result):
%	Call Goal(R).
%	If Goal(R) fails, fail.
%	If Goal(R) succeeds, succeed with Result = succeeded(R).
%	If Goal(R) throws an exception E, succeed with Result = exception(E).
%

:- type exception_result(T)
	--->	succeeded(T)
	;	exception(univ).

/***************
Sorry, the generic try/2 is not yet implemented.
Currently you must use `try_det', `try_semidet', etc.
:- pred try(pred(T), exception_result(T)).
:- mode try(pred(out) is det,       out) is det.
:- mode try(pred(out) is semidet,   out) is semidet.
:- mode try(pred(out) is cc_multi,  out) is cc_multi.
:- mode try(pred(out) is cc_nondet, out) is cc_nondet.
:- mode try(pred(out) is multi,     out) is multi.
:- mode try(pred(out) is nondet,    out) is nondet.
***************/

:- pred try_det(pred(T), exception_result(T)).
:- mode try_det(pred(out) is det,       out) is det.

:- pred try_semidet(pred(T), exception_result(T)).
:- mode try_semidet(pred(out) is semidet,   out) is semidet.

:- pred try_cc_multi(pred(T), exception_result(T)).
:- mode try_cc_multi(pred(out) is cc_multi,  out) is cc_multi.

:- pred try_cc_nondet(pred(T), exception_result(T)).
:- mode try_cc_nondet(pred(out) is cc_nondet, out) is cc_nondet.

:- pred try_multi(pred(T), exception_result(T)).
:- mode try_multi(pred(out) is multi,     out) is multi.

:- pred try_nondet(pred(T), exception_result(T)).
:- mode try_nondet(pred(out) is nondet,    out) is nondet.

%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%

:- implementation.

throw(T) :-
	type_to_univ(T, Univ),
	builtin_throw(Univ).

/*********************
% This doesn't work, due to 
% 	bash$ mc exception.m
% 	Software error: sorry, not implemented: taking address of pred
% 	`wrap_success/2' with multiple modes.

try(Goal, Result) :-
	builtin_catch(wrap_success(Goal), wrap_exception, Result).

:- pred wrap_success(pred(T), exception_result(T)) is det.
:- mode wrap_success(pred(out) is det, out) is det.
:- mode wrap_success(pred(out) is semidet, out) is semidet.
:- mode wrap_success(pred(out) is cc_multi, out) is cc_multi.
:- mode wrap_success(pred(out) is cc_nondet, out) is cc_nondet.
:- mode wrap_success(pred(out) is multi, out) is multi.
:- mode wrap_success(pred(out) is nondet, out) is nondet.
wrap_success(Goal, succeeded(Result)) :- Goal(Result).
*********************/

try_det(Goal, Result) :-
	builtin_catch(wrap_success_det(Goal), wrap_exception, Result).
try_semidet(Goal, Result) :-
	builtin_catch(wrap_success_semidet(Goal), wrap_exception, Result).
try_cc_multi(Goal, Result) :-
	builtin_catch(wrap_success_cc_multi(Goal), wrap_exception, Result).
try_cc_nondet(Goal, Result) :-
	builtin_catch(wrap_success_cc_nondet(Goal), wrap_exception, Result).
try_multi(Goal, Result) :-
	builtin_catch(wrap_success_multi(Goal), wrap_exception, Result).
try_nondet(Goal, Result) :-
	builtin_catch(wrap_success_nondet(Goal), wrap_exception, Result).

:- pred wrap_success_det(pred(T), exception_result(T)) is det.
:- mode wrap_success_det(pred(out) is det, out) is det.
wrap_success_det(Goal, succeeded(Result)) :- Goal(Result).

:- pred wrap_success_semidet(pred(T), exception_result(T)) is det.
:- mode wrap_success_semidet(pred(out) is semidet, out) is semidet.
wrap_success_semidet(Goal, succeeded(Result)) :- Goal(Result).

:- pred wrap_success_cc_multi(pred(T), exception_result(T)) is det.
:- mode wrap_success_cc_multi(pred(out) is cc_multi, out) is cc_multi.
wrap_success_cc_multi(Goal, succeeded(Result)) :- Goal(Result).

:- pred wrap_success_cc_nondet(pred(T), exception_result(T)) is det.
:- mode wrap_success_cc_nondet(pred(out) is cc_nondet, out) is cc_nondet.
wrap_success_cc_nondet(Goal, succeeded(Result)) :- Goal(Result).

:- pred wrap_success_multi(pred(T), exception_result(T)) is det.
:- mode wrap_success_multi(pred(out) is multi, out) is multi.
wrap_success_multi(Goal, succeeded(Result)) :- Goal(Result).

:- pred wrap_success_nondet(pred(T), exception_result(T)) is det.
:- mode wrap_success_nondet(pred(out) is nondet, out) is nondet.
wrap_success_nondet(Goal, succeeded(Result)) :- Goal(Result).

:- pred wrap_exception(univ::in, exception_result(T)::out) is det.
wrap_exception(Exception, exception(Exception)).

%-----------------------------------------------------------------------------%

:- pred builtin_throw(univ).
:- mode builtin_throw(in) is erroneous.

:- type handler(T) == pred(univ, T).
:- inst handler == (pred(in, out) is det).

:- pred builtin_catch(pred(T), handler(T), T).
:- mode builtin_catch(pred(out) is det,       in(handler), out) is det.
:- mode builtin_catch(pred(out) is semidet,   in(handler), out) is semidet.
:- mode builtin_catch(pred(out) is cc_multi,  in(handler), out) is cc_multi.
:- mode builtin_catch(pred(out) is cc_nondet, in(handler), out) is cc_nondet.
:- mode builtin_catch(pred(out) is multi,     in(handler), out) is multi.
:- mode builtin_catch(pred(out) is nondet,    in(handler), out) is nondet.

% builtin_throw and builtin_catch are implemented below using
% hand-coded low-level C code.

:- external(builtin_throw/1).
:- external(builtin_catch/3).

%-----------------------------------------------------------------------------%

:- pragma c_code("

enum CodeModel { MODEL_DET, MODEL_SEMI, MODEL_NON };

Define_extern_entry(mercury__exception__builtin_catch_3_0); /* det */
Define_extern_entry(mercury__exception__builtin_catch_3_1); /* semidet */
Define_extern_entry(mercury__exception__builtin_catch_3_2); /* cc_multi */
Define_extern_entry(mercury__exception__builtin_catch_3_3); /* cc_nondet */
Define_extern_entry(mercury__exception__builtin_catch_3_4); /* multi */
Define_extern_entry(mercury__exception__builtin_catch_3_6); /* nondet */

Define_extern_entry(mercury__exception__builtin_throw_1_0);

Define_extern_entry(exception_handler_do_fail);

/* the following are defined in runtime/call.mod */
Declare_entry(do_call_det_closure);
Declare_entry(do_call_semidet_closure);
Declare_entry(do_call_nondet_closure);

Declare_label(mercury__exception__builtin_catch_3_2_i1);
Declare_label(mercury__exception__builtin_catch_3_2_i2);
Declare_label(mercury__exception__builtin_catch_3_3_i1);
Declare_label(mercury__exception__builtin_catch_3_3_i2);
Declare_label(mercury__exception__builtin_catch_3_5_i1);
Declare_label(mercury__exception__builtin_catch_3_5_i2);
#ifndef COMPACT_ARGS
Declare_label(mercury__exception__builtin_throw_1_0_i1);
Declare_label(mercury__exception__builtin_throw_1_0_i2);
Declare_label(mercury__exception__builtin_throw_1_0_i3);
#endif

BEGIN_MODULE(exceptions_module)
	init_entry(mercury__exception__builtin_catch_3_0);
	init_entry(mercury__exception__builtin_catch_3_1);
	init_entry(mercury__exception__builtin_catch_3_2);
	init_entry(mercury__exception__builtin_catch_3_3);
	init_entry(mercury__exception__builtin_catch_3_4);
	init_entry(mercury__exception__builtin_catch_3_5);
	init_label(mercury__exception__builtin_catch_3_2_i1);
	init_label(mercury__exception__builtin_catch_3_2_i2);
	init_label(mercury__exception__builtin_catch_3_3_i1);
	init_label(mercury__exception__builtin_catch_3_3_i2);
	init_label(mercury__exception__builtin_catch_3_5_i1);
	init_label(mercury__exception__builtin_catch_3_5_i2);
	init_label(mercury__exception__builtin_throw_1_0);
#ifndef COMPACT_ARGS
	init_label(mercury__exception__builtin_throw_1_0_i1);
	init_label(mercury__exception__builtin_throw_1_0_i2);
	init_label(mercury__exception__builtin_throw_1_0_i3);
#endif
	init_entry(exception_handler_do_fail);
BEGIN_CODE

/*
** builtin_catch(Goal, Handler, Result)
**	call Goal(R).
**	if succeeds, set Result = R.
**	if throws an exception, call Handler(Result).
**
** This is the model_det version.
** On entry, we have a type_info (which we don't use) in r1,
** the Goal to execute in r2 and the Handler in r3.
** On exit, we should put Result in r1 (with COMPACT_ARGS) or r4.
*/
Define_entry(mercury__exception__builtin_catch_3_0); /* det */
Define_entry(mercury__exception__builtin_catch_3_2); /* cc_multi */
	/*
	** Create a handler on the stack with the special redoip
	** of `exception_handler_do_fail' (we'll look for this redoip
	** when unwinding the nondet stack in builtin_throw/1),
	** and save the stuff we will need if an exception is thrown.
	*/
	mkframe(""builtin_catch/3 [model_det]"", 4,
		ENTRY(exception_handler_do_fail));
	framevar(0) = MODEL_DET;
	framevar(1) = r3;		/* save the Handler closure */
	framevar(2) = (Word) sp;	/* save the det stack pointer */
	mark_hp(framevar(3));		/* save the heap pointer (if any) */

	/*
	** Now we need to create another frame.
	** This is so that we can be sure that no-one will hijack
	** the redoip of the special frame we created above.
	** (The compiler sometimes generates ``hijacking'' code that saves
	** the topmost redoip on the stack, and temporarily replaces it
	** with a new redoip that will do some processing on failure
	** before restoring the original redoip.  This would cause
	** problems when doing stack unwinding in builtin_throw/1,
	** because we wouldn't be able to find the special redoip.
	** But code will only ever hijack the topmost frame, so we
	** can avoid this by creating a second frame above the special
	** frame.)
	*/
	succip = LABEL(mercury__exception__builtin_catch_3_2_i1);
	mkframe(""builtin_catch_2/1 [model_det]"", 0, ENTRY(do_fail));

	/*
	** Now call `Goal(Result)'.
	*/
	r1 = r2;	/* The Goal to call */
	r2 = 0;		/* Zero additional input arguments */
	r3 = 1;		/* One output argument */
	call(ENTRY(do_call_det_closure), 
		LABEL(mercury__exception__builtin_catch_3_2_i2),
		LABEL(mercury__exception__builtin_catch_3_2));
		
Define_label(mercury__exception__builtin_catch_3_2_i2);
	update_prof_current_proc(LABEL(mercury__exception__catch_3_2));
	/*
	** On exit from do_call_det_closure, Result is in r1
	*/
#ifndef COMPACT_ARGS
	r4 = r1;
#endif
	succeed_discard();

Define_label(mercury__exception__builtin_catch_3_2_i1);
	succeed_discard();

/*
** builtin_catch(Goal, Handler, Result)
**	call Goal(R).
**	if succeeds, set Result = R.
**	if fails, fail.
**	if throws an exception, call Handler(Result).
**
** This is the model_semi version.
** With COMPACT_ARGS,
** on entry, we have a type_info (which we don't use) in r1,
** the Goal to execute in r2 and the Handler in r3,
** and on exit, we should put Result in r2.
** Without COMPACT_ARGS,
** on entry, we have a type_info (which we don't use) in r2,
** the Goal to execute in r3 and the Handler in r4,
** and on exit, we should put Result in r5.
*/
Define_entry(mercury__exception__builtin_catch_3_1); /* semidet */
Define_entry(mercury__exception__builtin_catch_3_3); /* cc_nondet */
	/*
	** Create a handler on the stack with the special redoip
	** of `exception_handler_do_fail' (we'll look for this redoip
	** when unwinding the nondet stack in builtin_throw/1),
	** and save the stuff we will need if an exception is thrown.
	*/
	mkframe(""builtin_catch/3 [model_semi]"", 4,
		ENTRY(exception_handler_do_fail));
	framevar(0) = MODEL_SEMI;
#ifdef COMPACT_ARGS
	framevar(1) = r3;		/* save the Handler closure */
#else
	framevar(1) = r4;		/* save the Handler closure */
#endif
	framevar(2) = (Word) sp;	/* save the det stack pointer */
	mark_hp(framevar(3));		/* save the heap pointer (if any) */

	/*
	** Now we need to create another frame.
	** This is so that we can be sure that no-one will hijack
	** the redoip of the special frame we created above.
	** (The compiler sometimes generates ``hijacking'' code that saves
	** the topmost redoip on the stack, and temporarily replaces it
	** with a new redoip that will do some processing on failure
	** before restoring the original redoip.  This would cause
	** problems when doing stack unwinding in builtin_throw/1,
	** because we wouldn't be able to find the special redoip.
	** But code will only ever hijacks the topmost frame, so we
	** can avoid this by creating a second frame above the special
	** frame.)
	*/
	succip = LABEL(mercury__exception__builtin_catch_3_3_i1);
	mkframe(""builtin_catch_2/1 [model_semi]"", 0, ENTRY(do_fail));

	/*
	** Now call `Goal(Result)'.
	*/
#ifdef COMPACT_ARGS
	r1 = r2;	/* The Goal to call */
#else
	r1 = r3;	/* The Goal to call */
#endif
	r2 = 0;		/* Zero additional input arguments */
	r3 = 1;		/* One output argument */
	call(ENTRY(do_call_semidet_closure), 
		LABEL(mercury__exception__builtin_catch_3_3_i2),
		LABEL(mercury__exception__builtin_catch_3_3));
		
Define_label(mercury__exception__builtin_catch_3_3_i2);
	update_prof_current_proc(LABEL(mercury__exception__catch_3_3));
	/*
	** On exit from do_call_semidet_closure, the success/failure
	** indicator is in r1, and Result is in r2.
	** Note that we call succeed_discard() to exit regardless
	** of whether r1 is true or false.  We just return the r1 value
	** back to our caller.
	*/
#ifndef COMPACT_ARGS
	r5 = r2;
#endif
	succeed_discard();

Define_label(mercury__exception__builtin_catch_3_3_i1);
	succeed_discard();

/*
** builtin_catch(Goal, Handler, Result)
**	call Goal(R).
**	if succeeds, set Result = R.
**	if fails, fail.
**	if throws an exception, call Handler(Result).
**
** This is the model_non version.
** On entry, we have a type_info (which we don't use) in r1,
** the Goal to execute in r2 and the Handler in r3.
** On exit, we should put Result in r1 (with COMPACT_ARGS) or r3.
*/
Define_entry(mercury__exception__builtin_catch_3_4); /* multi */
Define_entry(mercury__exception__builtin_catch_3_5); /* nondet */
	/*
	** Create a handler on the stack with the special redoip
	** of `exception_handler_do_fail' (we'll look for this redoip
	** when unwinding the nondet stack in builtin_throw/1),
	** and save the stuff we will need if an exception is thrown.
	*/
	mkframe(""builtin_catch/3 [nondet]"", 4,
		ENTRY(exception_handler_do_fail));
	framevar(0) = MODEL_NON;
	framevar(1) = r3;		/* save the Handler closure */
	framevar(2) = (Word) sp;	/* save the det stack pointer */
	mark_hp(framevar(3));		/* save the heap pointer (if any) */

	/*
	** Now we need to create another frame.
	** This is so that we can be sure that no-one will hijack
	** the redoip of the special frame we created above.
	** (The compiler sometimes generates ``hijacking'' code that saves
	** the topmost redoip on the stack, and temporarily replaces it
	** with a new redoip that will do some processing on failure
	** before restoring the original redoip.  This would cause
	** problems when doing stack unwinding in builtin_throw/1,
	** because we wouldn't be able to find the special redoip.
	** But code will only ever hijacks the topmost frame, so we
	** can avoid this by creating a second frame above the special
	** frame.)
	*/
	succip = LABEL(mercury__exception__builtin_catch_3_5_i1);
	mkframe(""builtin_catch_2/1 [nondet]"", 0, ENTRY(do_fail));

	/*
	** Now call `Goal(Result)'.
	*/
	r1 = r2;	/* the Goal to call */
	r2 = 0;		/* Zero additional input arguments */
	r3 = 1;		/* One output argument */
	call(ENTRY(do_call_nondet_closure), 
		LABEL(mercury__exception__builtin_catch_3_5_i2),
		LABEL(mercury__exception__builtin_catch_3_5));
		
Define_label(mercury__exception__builtin_catch_3_5_i2);
	update_prof_current_proc(LABEL(mercury__exception__catch_3_5));
	/*
	** On exit from do_call_nondet_closure, Result is in r1
	*/
#ifndef COMPACT_ARGS
	r3 = r1;
#endif
	succeed();

Define_label(mercury__exception__builtin_catch_3_5_i1);
	succeed();

/*
** builtin_throw(Exception):
**	Throw the specified exception.
**	That means unwinding the nondet stack until we find a handler, and then
**	calling Handler(Result).
**
** On entry, we have Exception in r1.
*/
Define_entry(mercury__exception__builtin_throw_1_0);
{
	Word exception = r1;
	Word handler;
	enum CodeModel catch_code_model;

	/*
	** Search the nondet stack for an exception handler,
	** i.e. a frame whose redoip is `exception_handler_do_fail'
	** (one created by `builtin_catch').
	** N.B.  We search down the `succfr' chain, not the `prevfr' chain;
	** this ensures that we only find handlers installed by our callers,
	** not handlers installed by procedures that we called but which
	** are still on the nondet stack because they choice points behind.
	*/
	do {
		curfr = cursuccfr;
		if (curfr < nondetstack_zone->min) {
			fatal_error(""builtin_throw/1: uncaught exception"");
		}
	} while (curredoip != ENTRY(exception_handler_do_fail));

	/*
	** Save the handler we found, reset the det stack to and heap,
	** pop the final exception handler frame off the nondet stack,
	** and reset the nondet stack top.
	*/
	succip = cursuccip;
	catch_code_model = framevar(0);
	handler = framevar(1);	
	sp = (Word) framevar(2);	/* reset the det stack pointer */
	restore_hp(framevar(3));	/* reset the heap pointer (if any) */
	maxfr = curprevfr;
	curfr = maxfr;

#ifndef CONSERVATIVE_GC
	/* XXX we need to do deep_copy() on the exception object */
	fatal_error(""throw/1 not implemented for non-gc grades"");
#endif

	/*
	** Now invoke the handler that we found, as `Handler(Result)',
	*/

	r1 = handler;		/* get the Handler closure */
	r2 = 1;			/* One additional input argument */
	r3 = 1;			/* One output argument */
	r4 = exception;		/* This is our one input argument */

#ifdef COMPACT_ARGS
	/*
	** If the catch was semidet, we need to set the success indicator
	** r1 to TRUE and return the result in r2; otherwise, we return
	** the result in r1, which is where do_call_det_closure puts it,
	** so we can to a tailcall.
	*/
	if (catch_code_model != MODEL_SEMI) {
		tailcall(ENTRY(do_call_det_closure), 
			LABEL(mercury__exception__builtin_throw_1_0));
	}
	push(succip);
	call(ENTRY(do_call_det_closure), 
		LABEL(mercury__exception__builtin_throw_1_0_i1),
		LABEL(mercury__exception__builtin_throw_1_0));
}
Define_label(mercury__exception__builtin_throw_1_0_i1);
	r2 = r1;
	r1 = TRUE;
	succip = (Code *) pop();
	proceed();

#else /* not COMPACT_ARGS */

	push(succip);
	push(catch_code_model);
	call(ENTRY(do_call_det_closure), 
		LABEL(mercury__exception__builtin_throw_1_0_i1)),
		LABEL(mercury__exception__builtin_throw_1_0));
}
Define_label(mercury__exception__builtin_throw_1_0_i1);
	/* we've just returned from do_call_det_closure */
	catch_code_model = pop();
	if (catch_code_model == MODEL_SEMI) {
		r5 = r1;
	else {
		r4 = r1;
	}
	succip = (Code *) pop();
	proceed();

#endif /* not COMPACT_ARGS */

Define_entry(exception_handler_do_fail);
	/*
	** `exception_handler_do_fail' is the same as `do_fail':
	** it just invokes fail().  The reason we don't just use
	** `do_fail' for this is that when unwinding the stack we
	** check for a redoip of `exception_handler_do_fail' and
	** handle it specially.
	*/
	fail();

END_MODULE

/* suppress gcc -Wmissing-decls warning */
void mercury_sys_init_exceptions(void);

void mercury_sys_init_exceptions(void) {
	exceptions_module();
}

").

%-----------------------------------------------------------------------------%


%-----------------------------------------------------------------------------%
% Copyright (C) 1997 The University of Melbourne.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%-----------------------------------------------------------------------------%

% File: test_exceptions.m.
% Main author: fjh.

% Test cases for exception handling.

% XXX we should test nested exception handlers.

%-----------------------------------------------------------------------------%

:- module test_exceptions.
:- interface.
:- import_module io.

:- pred main(io__state::di, io__state::uo) is cc_multi.

:- implementation.
:- import_module std_util.
:- import_module exception.

main --> 
	{ try_det(det_throw, DetThrowResult) },
	write("det_throw: "), write(DetThrowResult), nl,
	{ try_det(det_succeed, DetSucceedResult) },
	write("det_succeed: "), write(DetSucceedResult), nl,

	(if { try_semidet(semidet_throw, SemidetThrowResult) } then
		write("semidet_throw: "), write(SemidetThrowResult), nl
	else
		write("semidet_throw failed"), nl
	),
	(if { try_semidet(semidet_succeed, SemidetSucceedResult) } then
		write("semidet_succeed: "), write(SemidetSucceedResult), nl
	else
		write("semidet_succeed failed"), nl
	),
	(if { try_semidet(semidet_fail, SemidetFailResult) } then
		write("semidet_fail: "), write(SemidetFailResult), nl
	else
		write("semidet_fail failed"), nl
	),

	{ try_cc_multi(cc_multi_throw, CCMultiThrowResult) },
	write("cc_multi_throw: "), write(CCMultiThrowResult), nl,
	{ try_cc_multi(cc_multi_succeed, CCMultiSucceedResult) },
	write("cc_multi_succeed: "), write(CCMultiSucceedResult), nl,

	(if { try_cc_nondet(cc_nondet_throw, CCNondetThrowResult) } then
		write("cc_nondet_throw: "), write(CCNondetThrowResult), nl
	else
		write("cc_nondet_throw failed"), nl
	),
	(if { try_cc_nondet(cc_nondet_succeed, CCNondetSucceedResult) } then
		write("cc_nondet_succeed: "), write(CCNondetSucceedResult), nl
	else
		write("cc_nondet_succeed failed"), nl
	),
	(if { try_cc_nondet(cc_nondet_fail, CCNondetFailResult) } then
		write("cc_nondet_fail: "), write(CCNondetFailResult), nl
	else
		write("cc_nondet_fail failed"), nl
	),

	{ solutions(try_multi(multi_throw), MultiThrowResult) },
	write("multi_throw: "), write(MultiThrowResult), nl,
	{ solutions(try_multi(multi_succeed), MultiSucceedResult) },
	write("multi_succeed: "), write(MultiSucceedResult), nl,
	{ solutions(try_multi(multi_succeed_then_throw),
		MultiSucceedThenThrowResult) },
	write("multi_succeed_then_throw: "),
	write(MultiSucceedThenThrowResult), nl,

	{ solutions(try_nondet(nondet_throw), NondetThrowResult) },
	write("nondet_throw: "), write(NondetThrowResult), nl,
	{ solutions(try_nondet(nondet_succeed), NondetSucceedResult) },
	write("nondet_succeed: "), write(NondetSucceedResult), nl,
	{ solutions(try_nondet(nondet_fail), NondetFailResult) },
	write("nondet_fail: "), write(NondetFailResult), nl,
	{ solutions(try_nondet(nondet_succeed_then_throw),
		NondetSucceedThenThrowResult) },
	write("nondet_succeed_then_throw: "),
	write(NondetSucceedThenThrowResult), nl.

:- pred det_throw(string::out) is det.
det_throw(_) :- throw("det_throw").

:- pred semidet_throw(string::out) is semidet.
semidet_throw(_) :- throw("semidet_throw").

:- pred nondet_throw(string::out) is nondet.
nondet_throw(_) :- throw("nondet_throw").

:- pred multi_throw(string::out) is multi.
multi_throw(_) :- throw("multi_throw").

:- pred cc_nondet_throw(string::out) is cc_nondet.
cc_nondet_throw(_) :- throw("cc_nondet_throw").

:- pred cc_multi_throw(string::out) is cc_multi.
cc_multi_throw(_) :- throw("cc_multi_throw").


:- pred det_succeed(string::out) is det.
det_succeed("det_succeed").

:- pred semidet_succeed(string::out) is semidet.
semidet_succeed("semidet_succeed").

:- pred nondet_succeed(string::out) is nondet.
nondet_succeed("nondet_succeed 1").
nondet_succeed("nondet_succeed 2").

:- pred multi_succeed(string::out) is multi.
multi_succeed("multi_succeed 1").
multi_succeed("multi_succeed 2").

:- pred cc_nondet_succeed(string::out) is cc_nondet.
cc_nondet_succeed("cc_nondet_succeed").
cc_nondet_succeed("cc_nondet_succeed 2").

:- pred cc_multi_succeed(string::out) is cc_multi.
cc_multi_succeed("cc_multi_succeed").
cc_multi_succeed("cc_multi_succeed 2").


:- pred semidet_fail(string::out) is semidet.
semidet_fail("semidet_fail") :- fail.

:- pred nondet_fail(string::out) is nondet.
nondet_fail("nondet_fail 1") :- fail.
nondet_fail("nondet_fail 2") :- fail.

:- pred cc_nondet_fail(string::out) is cc_nondet.
cc_nondet_fail("cc_nondet_fail") :- fail.
cc_nondet_fail("cc_nondet_succeed 2") :- fail.


:- pred nondet_succeed_then_throw(string::out) is nondet.
nondet_succeed_then_throw("nondet_succeed_then_throw 1").
nondet_succeed_then_throw("nondet_succeed_then_throw 2").
nondet_succeed_then_throw(_) :- throw("nondet_succeed_then_throw 3").
nondet_succeed_then_throw("nondet_succeed_then_throw 4").

:- pred multi_succeed_then_throw(string::out) is multi.
multi_succeed_then_throw("multi_succeed_then_throw 1").
multi_succeed_then_throw("multi_succeed_then_throw 2").
multi_succeed_then_throw(_) :- throw("multi_succeed_then_throw 3").
multi_succeed_then_throw("multi_succeed_then_throw 4").


-- 
Fergus Henderson <fjh at cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3         |     -- the last words of T. S. Garp.



More information about the developers mailing list