[m-dev.] for review: move exception handling into the standard library

Fergus Henderson fjh at cs.mu.OZ.AU
Tue Aug 31 17:17:31 AEST 1999


Hi all,

Does anyone want to review this?

Mission Critical have been asking for changes along these lines
for some time.

--------------------

Estimated hours taken: 4

Move exception handling into the standard library,
and change error/1 so that it throws an exception.

This has two advantages: one is that user code can now catch
exceptions caused by calls to error/1, the other is that the
debugger handles calls to error/1 more gracefully (by virtue
of its existing support for exception handling).

extras/exceptions/exception.m:
library/exception.m:
	Move exception.m from `extras' into the standard library.
	Change the code for handling uncaught exceptions so that
	it prints out the same diagnostics that error/1 used to print out
	(i.e. a stack trace and the last debugger event number).

extras/exceptions/Mmakefile:
extras/exceptions/test_exceptions.exp:
extras/exceptions/test_exceptions.m:
extras/exceptions/test_exceptions_func.exp:
extras/exceptions/test_exceptions_func.m:
extras/exceptions/test_uncaught_exception.exp:
extras/exceptions/test_uncaught_exception.m
tests/hard_coded/exceptions/Mmakefile:
tests/hard_coded/exceptions/test_exceptions.exp:
tests/hard_coded/exceptions/test_exceptions.m:
tests/hard_coded/exceptions/test_exceptions_func.exp:
tests/hard_coded/exceptions/test_exceptions_func.m:
tests/hard_coded/exceptions/test_uncaught_exception.exp:
tests/hard_coded/exceptions/test_uncaught_exception.exp2:
tests/hard_coded/exceptions/test_uncaught_exception.m
	Move the exception test cases from extras/exceptions to
	a new directory tests/hard_coded/exceptions.
	Also, change the expected output for the `test_uncaught_exception'
	test case, since it now prints out more information.

tests/hard_coded/Mmakefile:
	Add `exceptions' to the list of subdirectories.

extras/exceptions/README:
	Delete this file, since the files that it refers to have now
	all been moved elsewhere.

library/require.m:
	Change error/1 so that it throws an exception.

library/library.m:
	Add exceptions.m to the list of modules in the standard library.

Workspace: /usr/hg/fjh/mercury
cvs diff: extras/exceptions/Mmakefile was removed, no comparison available
cvs diff: extras/exceptions/README was removed, no comparison available
cvs diff: extras/exceptions/exception.m was removed, no comparison available
cvs diff: extras/exceptions/test_exceptions.exp was removed, no comparison available
cvs diff: extras/exceptions/test_exceptions.m was removed, no comparison available
cvs diff: extras/exceptions/test_exceptions_func.exp was removed, no comparison available
cvs diff: extras/exceptions/test_exceptions_func.m was removed, no comparison available
cvs diff: extras/exceptions/test_uncaught_exception.exp was removed, no comparison available
cvs diff: library/exception.m is a new entry, no comparison available
Index: library/library.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/library.m,v
retrieving revision 1.43
diff -u -r1.43 library.m
--- library/library.m	1998/09/29 05:10:45	1.43
+++ library/library.m	1999/08/30 13:48:02
@@ -33,6 +33,7 @@
 :- import_module store, rbtree, parser, lexer, ops.
 :- import_module prolog.
 :- import_module integer, rational.
+:- import_module exception.
 
 % library__version must be implemented using pragma c_code,
 % so we can get at the MR_VERSION and MR_FULLARCH configuration
Index: library/require.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/require.m,v
retrieving revision 1.27
diff -u -r1.27 require.m
--- library/require.m	1999/03/07 23:41:04	1.27
+++ library/require.m	1999/08/31 06:09:42
@@ -14,18 +14,21 @@
 %-----------------------------------------------------------------------------%
 :- interface.
 
+:- type software_error ---> software_error(string).
+
 :- pred error(string).
 :- mode error(in) is erroneous.
 
 %	error(Message).
-%		Abort with error message.
-
+%		Throw a `software_error(Message)' exception.
+%		This will normally cause execution to abort with an error
+%		message.
 
 :- pred	require(pred, string).
 :- mode	require((pred) is semidet, in) is det.
 
 %	require(Goal, Message).
-%		Call goal, and abort with error message if Goal fails.
+%		Call goal, and call error(Message) if Goal fails.
 %		This is not as useful as you might imagine, since it requires
 %		that the goal not produce any output variables.  In
 %		most circumstances you should use an explicit if-then-else
@@ -44,7 +47,7 @@
 
 :- implementation.
 
-:- import_module string, list, std_util.
+:- import_module string, list, std_util, exception.
 
 require(Goal, Message) :-
 	( call(Goal) ->
@@ -54,6 +57,8 @@
 		fail
 	).
 
+%-----------------------------------------------------------------------------%
+
 report_lookup_error(Msg, K, V) :-
 	KeyType = type_name(type_of(K)),
 	ValueType = type_name(type_of(V)),
@@ -78,64 +83,11 @@
 
 %-----------------------------------------------------------------------------%
 
-/* error/1, from require.m */
-
-:- pragma c_header_code("
-#include <stdio.h>
-#include ""mercury_trace_base.h""
-#include ""mercury_stack_trace.h""
-").
-
 % Hopefully error/1 won't be called often (!), so no point inlining it.
 :- pragma no_inline(error/1). 
 
 error(Message) :- 
-	error_internal(Message).
-
-:- pred error_internal(string::in) is erroneous.
-
-% We define error using handwritten code in error_internal because we
-% need complete control over it if we want to call MR_dump_stack.  In
-% particular we don't want to have to explicitly tell MR_dump_stack whether
-% a stack frame was generated by its caller.  The easiest way to do
-% this is to make sure it wasn't.
-
-:- external(error_internal/1).
-
-:- pragma c_code("
-
-Define_extern_entry(mercury__require__error_internal_1_0);
-
-BEGIN_MODULE(require_internal_module)
-        init_entry(mercury__require__error_internal_1_0);
-BEGIN_CODE
-
-/* code for predicate 'error_internal'/1 in mode 0 */
-Define_entry(mercury__require__error_internal_1_0);
-{
-	String  Message;
-	Message = (String) r1;
-
-	fflush(stdout);
-	fprintf(stderr, ""Software error: %s\\n"", Message);
-	MR_trace_report(stderr);
-	MR_dump_stack(MR_succip, MR_sp, MR_curfr, FALSE);
-	exit(1);
-}
-END_MODULE
-
-/*
-** Ensure that the initialization function for the above module gets run.
-*/
-/*
-INIT sys_init_require_internal_module
-*/
-extern ModuleFunc require_internal_module;
-void sys_init_require_internal_module(void);
-void sys_init_require_internal_module(void) {
-	require_internal_module();
-}
-").
+	throw(software_error(Message)).
 
 :- end_module require.
 
Index: tests/hard_coded/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/hard_coded/Mmakefile,v
retrieving revision 1.63
diff -u -r1.63 Mmakefile
--- tests/hard_coded/Mmakefile	1999/08/18 02:57:52	1.63
+++ tests/hard_coded/Mmakefile	1999/08/31 05:42:42
@@ -151,7 +151,7 @@
 
 #-----------------------------------------------------------------------------#
 
-SUBDIRS = typeclasses sub-modules
+SUBDIRS = typeclasses sub-modules exceptions
 
 dep_subdirs:
 	for dir in $(SUBDIRS); do \
cvs diff: tests/hard_coded/exceptions/Mmakefile is a new entry, no comparison available
cvs diff: tests/hard_coded/exceptions/test_exceptions.exp is a new entry, no comparison available
cvs diff: tests/hard_coded/exceptions/test_exceptions.m is a new entry, no comparison available
cvs diff: tests/hard_coded/exceptions/test_exceptions_func.exp is a new entry, no comparison available
cvs diff: tests/hard_coded/exceptions/test_exceptions_func.m is a new entry, no comparison available
cvs diff: tests/hard_coded/exceptions/test_uncaught_exception.exp is a new entry, no comparison available

********************************************************************************
The files that have been moved are mostly unchanged.
Here are diffs for the ones that have changed.
********************************************************************************

diff -u extras/exceptions/Mmakefile tests/hard_coded/exceptions/Mmakefile
--- extras/exceptions/Mmakefile			Tue Aug 31 16:15:52 1999
+++ tests/hard_coded/exceptions/Mmakefile	Tue Aug 31 15:41:47 1999
@@ -1,5 +1,5 @@
 #-----------------------------------------------------------------------------#
-# Copyright (C) 1997-1998 The University of Melbourne. 
+# Copyright (C) 1997-1999 The University of Melbourne. 
 # This file may only be copied under the terms of the GNU General
 # Public Licence - see the file COPYING in the Mercury distribution.
 #-----------------------------------------------------------------------------#
@@ -9,6 +9,8 @@
 
 MAIN_TARGET = check
 
+include ../../Mmake.common
+
 PROGS = test_exceptions.m test_uncaught_exception.m test_exceptions_func.m
 
 depend: $(PROGS:.m=.depend)
@@ -16,30 +18,6 @@
 check: $(PROGS:.m=.res)
 
 #-----------------------------------------------------------------------------#
-#
-# the following lines are required for exception.m
-# (see the comment at the top of exception.m)
-#
-
-RM_C=:
-C2INITFLAGS=--extra-inits
-$(PROGS:.m=_init.c): $(PROGS:.m=.c) exception.c
-
-#-----------------------------------------------------------------------------#
-#
-# some rules for running test cases
-#
-
-%.out: %
-	./$< > $@
-
-%.res: %.out %.exp
-	diff -c $*.out $*.exp > $@
-
-clean:
-	rm -f *.out
-realclean:
-	rm -f *.res
 
 # test_uncaught_exception is *supposed* to return an error exit status
 test_uncaught_exception.out: test_uncaught_exception

diff -u extras/exceptions/exception.m library/exception.m
--- extras/exceptions/exception.m Tue Aug 31 00:41:27 1999
+++ library/exception.m	Tue Aug 31 16:51:18 1999
@@ -6,9 +6,9 @@
 
 % File: exception.m.
 % Main author: fjh.
-% Stability: low
+% Stability: medium
 
-% This file contains experimental code for exception handling.
+% This file defines the Mercury interface for exception handling.
 
 % Note that throwing an exception across the C interface won't work.
 % That is, if a Mercury procedure that is exported to C using `pragma export'
@@ -16,19 +16,6 @@
 % you will get undefined behaviour.
 
 %-----------------------------------------------------------------------------%
-
-% To compile this module you need the following two lines in your Mmakefile:
-%
-%	RM_C=:
-%	C2INITFLAGS=--extra-inits
-%
-% You also need to add dependencies to ensure that Mmake knows that
-% the *_init.c files depend on the *.c files.
-%
-% This ensures that the module initialization code for this module will be run.
-% (Actually these steps are needed only in certain grades, e.g. for profiling.)
-
-%-----------------------------------------------------------------------------%
 %-----------------------------------------------------------------------------%
 :- module exception.
 :- interface.
@@ -161,7 +148,7 @@
 %-----------------------------------------------------------------------------%
 
 :- implementation.
-:- import_module require.
+:- import_module string, require.
 
 :- pred try(determinism,      	  pred(T),		  exception_result(T)).
 :- mode try(in(bound(det)),	  pred(out) is det,       out(cannot_fail))
@@ -189,7 +176,7 @@
 				    	     out(try_all_nondet)) is cc_multi.
 
 % The functors in this type must be in the same order as the
-% enumeration constants in the C enum `ME_Determinism' defined below.
+% enumeration constants in the C enum `ML_Determinism' defined below.
 :- type determinism
 	--->	det
 	;	semidet
@@ -224,66 +211,66 @@
 % functors in the Mercury type `determinism' defined above.
 :- pragma c_header_code("
 	typedef enum {
-		ME_DET,
-		ME_SEMIDET,
-		ME_CC_MULTI,
-		ME_CC_NONDET,
-		ME_MULTI,
-		ME_NONDET,
-		ME_ERRONEOUS,
-		ME_FAILURE
-	} ME_Determinism;
+		ML_DET,
+		ML_SEMIDET,
+		ML_CC_MULTI,
+		ML_CC_NONDET,
+		ML_MULTI,
+		ML_NONDET,
+		ML_ERRONEOUS,
+		ML_FAILURE
+	} ML_Determinism;
 ").
 
 :- pragma c_code(
 	get_determinism(_Pred::pred(out) is det,
 			Det::out(bound(det))),
 	will_not_call_mercury,
-	"Det = ME_DET"
+	"Det = ML_DET"
 ).
 :- pragma c_code(
 	get_determinism(_Pred::pred(out) is semidet,
 			Det::out(bound(semidet))),
 	will_not_call_mercury,
-	"Det = ME_SEMIDET"
+	"Det = ML_SEMIDET"
 ).
 :- pragma c_code(
 	get_determinism(_Pred::pred(out) is cc_multi,
 			Det::out(bound(cc_multi))),
 	will_not_call_mercury,
-	"Det = ME_CC_MULTI"
+	"Det = ML_CC_MULTI"
 ).
 :- pragma c_code(
 	get_determinism(_Pred::pred(out) is cc_nondet,
 			Det::out(bound(cc_nondet))),
 	will_not_call_mercury,
-	"Det = ME_CC_NONDET"
+	"Det = ML_CC_NONDET"
 ).
 :- pragma c_code(
 	get_determinism(_Pred::pred(out) is multi,
 			Det::out(bound(multi))),
 	will_not_call_mercury,
-	"Det = ME_MULTI"
+	"Det = ML_MULTI"
 ).
 :- pragma c_code(
 	get_determinism(_Pred::pred(out) is nondet,
 			Det::out(bound(nondet))),
 	will_not_call_mercury,
-	"Det = ME_NONDET"
+	"Det = ML_NONDET"
 ).
 
 :- pragma c_code(
 	get_determinism_2(_Pred::pred(out, di, uo) is det,
 			Det::out(bound(det))),
 	will_not_call_mercury,
-	"Det = ME_DET"
+	"Det = ML_DET"
 ).
 
 :- pragma c_code(
 	get_determinism_2(_Pred::pred(out, di, uo) is cc_multi,
 			Det::out(bound(cc_multi))),
 	will_not_call_mercury,
-	"Det = ME_CC_MULTI"
+	"Det = ML_CC_MULTI"
 ).
 
 throw(Exception) :-
@@ -464,7 +451,10 @@
 
 :- pragma c_header_code("
 	#include <assert.h>
+	#include <stdio.h>
 	#include ""mercury_deep_copy.h""
+	#include ""mercury_trace_base.h""
+	#include ""mercury_stack_trace.h""
 
 	MR_DECLARE_TYPE_CTOR_INFO_STRUCT( \
 			mercury_data_std_util__type_ctor_info_univ_0);
@@ -951,6 +941,7 @@
 	Word exception = r1;
 	Word handler;
 	enum CodeModel catch_code_model;
+	Word *orig_curfr = MR_curfr;
 
 	/*
 	** let the debugger trace exception throwing
@@ -976,9 +967,20 @@
 	while (MR_redoip_slot(MR_curfr) != ENTRY(exception_handler_do_fail)) {
 		MR_curfr = MR_succfr_slot(MR_curfr);
 		if (MR_curfr < MR_CONTEXT(nondetstack_zone)->min) {
-			fatal_error(""builtin_throw/1: uncaught exception"");
+			/*
+			** There was no exception handler.
+			** 
+			** We restore the original value of MR_curfr,
+			** print out some diagnostics,
+			** and then terminate execution.
+			*/
+			MR_curfr = orig_curfr;
+			fflush(stdout);
+			ML_report_uncaught_exception(exception);
+			MR_trace_report(stderr);
+			MR_dump_stack(MR_succip, MR_sp, MR_curfr, FALSE);
+			exit(1);
 		}
-
 	}
 
 	/*
@@ -1125,9 +1127,9 @@
 	** handle it specially.
 	*/
 	fail();
-
 END_MODULE
 
+
 /* Ensure that the initialization code for the above module gets run. */
 /*
 INIT mercury_sys_init_exceptions
@@ -1144,6 +1146,33 @@
 
 %-----------------------------------------------------------------------------%
 
+:- pragma export(report_uncaught_exception(in, di, uo),
+	"ML_report_uncaught_exception").
+
+:- pred report_uncaught_exception(univ, io__state, io__state).
+:- mode report_uncaught_exception(in, di, uo) is cc_multi.
+
+report_uncaught_exception(Exception) -->
+	try_io(report_uncaught_exception_2(Exception), Result),
+	(	{ Result = succeeded(_) }
+	;	{ Result = exception(_) }
+		% if we got a further exception while trying to report
+		% the uncaught exception, just ignore it
+	).
+
+:- pred report_uncaught_exception_2(univ, unit, io__state, io__state).
+:- mode report_uncaught_exception_2(in, out, di, uo) is det.
+
+report_uncaught_exception_2(Exception, unit) -->
+	io__stderr_stream(StdErr),
+	io__write_string(StdErr, "Uncaught exception:\n"),
+	( { univ_to_type(Exception, software_error(Message)) } ->
+		io__format(StdErr, "Software Error: %s\n", [s(Message)])
+	;
+		io__write(StdErr, univ_value(Exception)),
+		io__nl(StdErr)
+	).
+
 /*
 ** unsafe_perform_io/2 is the same as unsafe_perform_io/1
 ** (see extras/trailed_update/unsafe.m)
@@ -1157,13 +1186,13 @@
 unsafe_perform_io(P::(pred(out, di, uo) is det), X::out),
 	may_call_mercury,
 "{
-	ME_exception_call_io_pred_det(TypeInfo_for_T, P, &X);
+	ML_exception_call_io_pred_det(TypeInfo_for_T, P, &X);
 }").
 :- pragma c_code(
 unsafe_perform_io(P::(pred(out, di, uo) is cc_multi), X::out),
 	may_call_mercury,
 "{
-	ME_exception_call_io_pred_cc_multi(TypeInfo_for_T, P, &X);
+	ML_exception_call_io_pred_cc_multi(TypeInfo_for_T, P, &X);
 }").
 
 :- pred call_io_pred(pred(T, io__state, io__state), T, io__state, io__state).
@@ -1171,9 +1200,9 @@
 :- mode call_io_pred(pred(out, di, uo) is cc_multi, out, di, uo) is cc_multi.
 
 :- pragma export(call_io_pred(pred(out, di, uo) is det, out, di, uo),
-		"ME_exception_call_io_pred_det").
+		"ML_exception_call_io_pred_det").
 :- pragma export(call_io_pred(pred(out, di, uo) is cc_multi, out, di, uo),
-		"ME_exception_call_io_pred_cc_multi").
+		"ML_exception_call_io_pred_cc_multi").
 
 call_io_pred(P, X) --> P(X).
 
diff -u extras/exceptions/test_uncaught_exception.exp tests/hard_coded/exceptions/test_uncaught_exception.exp
--- extras/exceptions/test_uncaught_exception.exp	Thu Jul 23 00:32:02 1998
+++ tests/hard_coded/exceptions/test_uncaught_exception.exp	Tue Aug 31 16:54:50 1999
@@ -1 +1,3 @@
-Mercury runtime: builtin_throw/1: uncaught exception
+Uncaught exception:
+"<exception thrown from main>"
+Stack dump not available in this grade.
-- 
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.
--------------------------------------------------------------------------
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