[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