[m-rev.] for review: implement det exceptions on the .NET backend.
Tyson Dowd
trd at miscrit.be
Fri Aug 24 02:42:03 AEST 2001
Hi,
===================================================================
Estimated hours taken: 16
Branches: main
Implement exceptions in the .NET backend.
(sorry, model_det exceptions only at the moment).
compiler/mlds_to_il.m:
Wrap two exception handlers around the Mercury code -- one that
prints out any uncaught user exceptions using
ML_report_uncaught_exception, and one that catches any system
exceptions and prints them using the system exception printing
mechanism.
library/exception.m:
Implement exceptions in C#.
try and catch call try_impl and catch_impl, which by default
call builtin_trd and builtin_catch, but we provide a
foreign_proc definition that implements them in C# on the .NET
backend.
We also implement ML_call_goal in Mercury, renaming the
hand-coded versions appropriately.
library/math.m:
Throw mercury.runtime.SystemException for domain errors.
runtime/mercury_mcpp.cpp:
Fix the implementation of mercury.runtime.Exception -- now we
store the Mercury exception so you can retrieve it later.
Create a new class for Mercury runtime system exceptions, which
are generated by SORRY and fatal_error. We can't expect to
print these exceptions using ML_report_uncaught_exception (at
the moment ML_report_uncaught_exception generates such
exceptions!) so we print them as normal .NET exceptions instead.
Index: compiler/mlds_to_il.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/mlds_to_il.m,v
retrieving revision 1.77
diff -u -r1.77 mlds_to_il.m
--- compiler/mlds_to_il.m 23 Aug 2001 09:28:03 -0000 1.77
+++ compiler/mlds_to_il.m 23 Aug 2001 16:37:50 -0000
@@ -990,7 +990,10 @@
il_info_add_init_instructions(runtime_initialization_instrs),
^ has_main := yes,
- il_info_get_next_block_id(TryBlockId),
+ il_info_get_next_block_id(InnerTryBlockId),
+ il_info_get_next_block_id(OuterTryBlockId),
+ il_info_get_next_block_id(InnerCatchBlockId),
+ il_info_get_next_block_id(OuterCatchBlockId),
il_info_make_next_label(DoneLabel),
% Replace all the returns with leave instructions;
@@ -1006,43 +1009,123 @@
I
)
)},
+
+ { UnivMercuryType = term__functor(term__atom("univ"), [],
+ context("", 0)) },
+ { UnivMLDSType = mercury_type(UnivMercuryType, user_type) },
+ { UnivType = mlds_type_to_ilds_type(DataRep, UnivMLDSType) },
+
{ RenameNode = (func(N) = list__map(RenameRets, N)) },
- { ExceptionClassName = structured_name(assembly("mscorlib"),
+ { MercuryExceptionClassName = structured_name(
+ assembly("mercury"),
+ ["mercury", "runtime", "Exception"], []) },
+
+ { ExceptionClassName = structured_name(il_system_assembly_name,
["System", "Exception"], []) },
+ { FieldRef = make_fieldref(UnivType, MercuryExceptionClassName,
+ "mercury_exception") },
+
{ ConsoleWriteName = class_member_name(
structured_name(il_system_assembly_name,
["System", "Console"], []),
id("Write")) },
+
+ { UncaughtExceptionName = class_member_name(
+ structured_name(assembly("mercury"),
+ ["mercury", "exception",
+ "mercury_code"], []),
+ id("ML_report_uncaught_exception")) },
+
{ WriteString = methoddef(call_conv(no, default),
void, ConsoleWriteName,
[il_string_type]) },
+ { WriteUncaughtException = methoddef(call_conv(no, default),
+ void, UncaughtExceptionName,
+ [UnivType]) },
{ WriteObject = methoddef(call_conv(no, default),
void, ConsoleWriteName,
[il_generic_type]) },
+
+ % A code block to catch any exception at all.
+
+ { CatchAnyException = tree__list([
+ instr_node(start_block(
+ catch(ExceptionClassName),
+ OuterCatchBlockId)),
+ instr_node(ldstr("\nUncaught system exception: \n")),
+ instr_node(call(WriteString)),
+ instr_node(call(WriteObject)),
+ instr_node(leave(label_target(DoneLabel))),
+ instr_node(end_block(catch(ExceptionClassName),
+ OuterCatchBlockId))
+ ])
+ },
+
+ % Code to catch Mercury exceptions.
+ { CatchUserException = tree__list([
+ instr_node(start_block(
+ catch(MercuryExceptionClassName),
+ InnerCatchBlockId)),
+ instr_node(ldfld(FieldRef)),
+
+ instr_node(call(WriteUncaughtException)),
+
+ instr_node(leave(label_target(DoneLabel))),
+ instr_node(end_block(
+ catch(MercuryExceptionClassName),
+ InnerCatchBlockId))
+ ])
+ },
+
% Wrap an exception handler around the main
% code. This allows us to debug programs
% remotely without a window popping up asking
% how you wish to debug. Pressing the cancel
% button on this window is a bit difficult
% remotely.
+ %
+ % Inside this exception handler, we catch any user
+ % exceptions and pass them
+ %
+ % We nest the Mercury exception handler so that any
+ % exceptions thrown in ML_report_uncaught_exception
+ % will be caught by the outer (more general) exception
+ % handler.
+ %
+ % try {
+ % try {
+ % ... main instructions ...
+ % }
+ % catch (mercury.runtime.Exception me) {
+ % ML_report_uncaught_exception(me);
+ % }
+ % }
+ % catch (System.Exception e) {
+ % System.Console.Write(e);
+ % }
+
{ InstrsTree = tree__list([
- instr_node(start_block(try, TryBlockId)),
+
+ % outer try block
+ instr_node(start_block(try, OuterTryBlockId)),
+
+ % inner try block
+ instr_node(start_block(try, InnerTryBlockId)),
tree__map(RenameNode, InstrsTree2),
instr_node(leave(label_target(DoneLabel))),
- instr_node(end_block(try, TryBlockId)),
+ instr_node(end_block(try, InnerTryBlockId)),
+
+ % inner catch block
+ CatchUserException,
- instr_node(start_block(
- catch(ExceptionClassName),
- TryBlockId)),
- instr_node(ldstr("\nException Caught: \n")),
- instr_node(call(WriteString)),
- instr_node(call(WriteObject)),
instr_node(leave(label_target(DoneLabel))),
- instr_node(end_block(catch(ExceptionClassName),
- TryBlockId)),
+ instr_node(end_block(try, OuterTryBlockId)),
+
+ % outer catch block
+ CatchAnyException,
instr_node(label(DoneLabel)),
instr_node(ret)
Index: library/exception.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/exception.m,v
retrieving revision 1.49
diff -u -r1.49 exception.m
--- library/exception.m 3 Jul 2001 08:16:25 -0000 1.49
+++ library/exception.m 23 Aug 2001 16:37:51 -0000
@@ -388,7 +388,7 @@
throw(Exception) :-
type_to_univ(Exception, Univ),
- builtin_throw(Univ).
+ throw_impl(Univ).
throw(Exception) = _ :-
throw(Exception).
@@ -434,24 +434,24 @@
try(Detism, Goal, Result).
try(det, Goal, Result) :-
- builtin_catch((pred(R::out) is det :-
+ catch_impl((pred(R::out) is det :-
wrap_success_or_failure(Goal, R)),
wrap_exception, Result0),
cc_multi_equal(Result0, Result).
try(semidet, Goal, Result) :-
- builtin_catch((pred(R::out) is det :-
+ catch_impl((pred(R::out) is det :-
wrap_success_or_failure(Goal, R)),
wrap_exception, Result0),
cc_multi_equal(Result0, Result).
try(cc_multi, Goal, Result) :-
- builtin_catch(
+ catch_impl(
(pred(R::out) is cc_multi :-
wrap_success_or_failure(Goal, R)
),
wrap_exception, Result).
try(cc_nondet, Goal, Result) :-
- builtin_catch((pred(R::out) is cc_multi :-
+ catch_impl((pred(R::out) is cc_multi :-
wrap_success_or_failure(Goal, R)),
wrap_exception, Result).
@@ -600,33 +600,70 @@
throw_string(Msg) :-
throw(Msg).
-:- pred builtin_throw(univ).
-:- mode builtin_throw(in) is erroneous.
+:- pred throw_impl(univ).
+:- mode throw_impl(in) is erroneous.
:- type handler(T) == pred(univ, T).
:- inst handler == (pred(in, out) is det).
%
-% builtin_catch/3 is actually impure. But we don't declare it as impure,
+% catch_impl/3 is actually impure. But we don't declare it as impure,
% because the code for try_all/3 takes its address (to pass to
% unsorted_solutions/2), and Mercury does not (yet?) support
% impure higher-order pred terms.
%
+:- pragma promise_pure(catch_impl/3).
:- /* impure */
- 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.
+ pred catch_impl(pred(T), handler(T), T).
+:- mode catch_impl(pred(out) is det, in(handler), out) is det.
+:- mode catch_impl(pred(out) is semidet, in(handler), out) is semidet.
+:- mode catch_impl(pred(out) is cc_multi, in(handler), out) is cc_multi.
+:- mode catch_impl(pred(out) is cc_nondet, in(handler), out) is cc_nondet.
+:- mode catch_impl(pred(out) is multi, in(handler), out) is multi.
+:- mode catch_impl(pred(out) is nondet, in(handler), out) is nondet.
+
+% by default we call the external implementation, but specific backends
+% can provide their own definition using foreign_proc.
+
+throw_impl(Univ::in) :-
+ builtin_throw(Univ).
+
+
+catch_impl(Pred::(pred(out) is det), Handler::in(handler), T::out) :-
+ builtin_catch(Pred, Handler, T).
+catch_impl(Pred::(pred(out) is semidet), Handler::in(handler), T::out) :-
+ builtin_catch(Pred, Handler, T).
+catch_impl(Pred::(pred(out) is cc_multi), Handler::in(handler), T::out) :-
+ builtin_catch(Pred, Handler, T).
+catch_impl(Pred::(pred(out) is cc_nondet), Handler::in(handler), T::out) :-
+ builtin_catch(Pred, Handler, T).
+catch_impl(Pred::(pred(out) is multi), Handler::in(handler), T::out) :-
+ builtin_catch(Pred, Handler, T).
+catch_impl(Pred::(pred(out) is nondet), Handler::in(handler), T::out) :-
+ builtin_catch(Pred, Handler, T).
% builtin_throw and builtin_catch are implemented below using
% hand-coded low-level C code.
+%
+:- pred builtin_throw(univ).
+:- mode builtin_throw(in) is erroneous.
+
+
+:- /* impure */
+ 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.
+
+
:- external(builtin_throw/1).
:- external(builtin_catch/3).
+
%-----------------------------------------------------------------------------%
%
% The --high-level-code implementation
@@ -803,7 +840,7 @@
/*---------------------------------------------------------------------------*/
static void
-ML_call_goal_det(MR_Mercury_Type_Info type_info,
+ML_call_goal_det_handcoded(MR_Mercury_Type_Info type_info,
MR_Pred closure, MR_Box *result)
{
typedef void MR_CALL DetFuncType(void *, MR_Box *);
@@ -813,7 +850,7 @@
}
static bool
-ML_call_goal_semi(MR_Mercury_Type_Info type_info,
+ML_call_goal_semi_handcoded(MR_Mercury_Type_Info type_info,
MR_Pred closure, MR_Box *result)
{
typedef bool MR_CALL SemidetFuncType(void *, MR_Box *);
@@ -823,7 +860,7 @@
}
static void
-ML_call_goal_non(MR_Mercury_Type_Info type_info,
+ML_call_goal_non_handcoded(MR_Mercury_Type_Info type_info,
MR_Pred closure, MR_Box *result, MR_CONT_PARAMS)
{
typedef void MR_CALL NondetFuncType(void *, MR_Box *,
@@ -888,7 +925,7 @@
#endif
if (setjmp(this_handler.handler) == 0) {
- ML_call_goal_det(type_info, pred, output);
+ ML_call_goal_det_handcoded(type_info, pred, output);
ML_exception_handler = this_handler.prev;
} else {
#ifdef MR_DEBUG_JMPBUFS
@@ -916,7 +953,8 @@
#endif
if (setjmp(this_handler.handler) == 0) {
- bool result = ML_call_goal_semi(type_info, pred, output);
+ bool result = ML_call_goal_semi_handcoded(type_info, pred,
+ output);
ML_exception_handler = this_handler.prev;
return result;
} else {
@@ -969,7 +1007,8 @@
#endif
if (setjmp(this_handler.handler) == 0) {
- ML_call_goal_non(type_info, pred, output, success_cont);
+ ML_call_goal_non_handcoded(type_info, pred, output,
+ success_cont);
ML_exception_handler = this_handler.prev;
} else {
#ifdef MR_DEBUG_JMPBUFS
@@ -1031,7 +1070,7 @@
#endif
if (setjmp(locals.this_handler.handler) == 0) {
- ML_call_goal_non(type_info, pred, output,
+ ML_call_goal_non_handcoded(type_info, pred, output,
ML_catch_success_cont, &locals);
/*
** If we reach here, it means that
@@ -1067,75 +1106,84 @@
#endif /* MR_HIGHLEVEL_CODE */
").
-/*
-
-XXX :- external stops us from using this
-
-:- pragma foreign_proc("MC++", builtin_throw(_T::in), [will_not_call_mercury], "
- mercury_exception *ex;
-
- // XXX should look for string objects and set them as the message
- if (false) {
- ex = new mercury_exception;
- } else {
- ex = new mercury_exception(""hello"");
- }
+ % For the .NET backend we override throw_impl as it is easier to
+ % implement these things using foreign_proc.
- throw ex;
+:- pragma foreign_proc("C#", throw_impl(T::in), [will_not_call_mercury], "
+ throw new mercury.runtime.Exception(T);
").
-:- pragma foreign_proc("MC++",
- builtin_catch(_Pred::pred(out) is det,
- _Handler::in(handler), _T::out), [will_not_call_mercury], "
- mercury::runtime::Errors::SORRY(""foreign code for this function"");
+
+:- pragma foreign_proc("C#",
+ catch_impl(Pred::pred(out) is det,
+ Handler::in(handler), T::out), [will_not_call_mercury], "
+ try {
+ mercury.exception.mercury_code.ML_call_goal_det(
+ TypeInfo_for_T, Pred, ref T);
+ }
+ catch (mercury.runtime.Exception ex) {
+ mercury.exception.mercury_code.ML_call_handler_det(
+ TypeInfo_for_T, Handler, ex.mercury_exception, ref T);
+ }
").
-:- pragma foreign_proc("MC++",
- builtin_catch(_Pred::pred(out) is semidet,
- _Handler::in(handler), _T::out), [will_not_call_mercury], "
- mercury::runtime::Errors::SORRY(""foreign code for this function"");
+:- pragma foreign_proc("C#",
+ catch_impl(Pred::pred(out) is cc_multi,
+ Handler::in(handler), T::out), [will_not_call_mercury], "
+ try {
+ mercury.exception.mercury_code.ML_call_goal_det(
+ TypeInfo_for_T, Pred, ref T);
+ }
+ catch (mercury.runtime.Exception ex) {
+ mercury.exception.mercury_code.ML_call_handler_det(
+ TypeInfo_for_T, Handler, ex.mercury_exception, ref T);
+ }
").
-:- pragma foreign_proc("MC++",
- builtin_catch(_Pred::pred(out) is cc_multi,
- _Handler::in(handler), _T::out), [will_not_call_mercury], "
- mercury::runtime::Errors::SORRY(""foreign code for this function"");
+/*
+
+ % We can't implement these until we implement semidet procedures
+ % for the C# interface.
+
+:- pragma foreign_proc("C#",
+ catch_impl(Pred::pred(out) is semidet,
+ Handler::in(handler), T::out), [will_not_call_mercury], "
+ mercury.runtime.Errors.SORRY(""foreign code for this function"");
").
-:- pragma foreign_proc("MC++",
- builtin_catch(_Pred::pred(out) is cc_nondet,
- _Handler::in(handler), _T::out), [will_not_call_mercury], "
- mercury::runtime::Errors::SORRY(""foreign code for this function"");
+
+:- pragma foreign_proc("C#",
+ catch_impl(Pred::pred(out) is cc_nondet,
+ Handler::in(handler), T::out), [will_not_call_mercury], "
+ mercury.runtime.Errors.SORRY(""foreign code for this function"");
").
-:- pragma foreign_proc("MC++",
- builtin_catch(_Pred::pred(out) is multi,
+
+
+ % We can't implement these because nondet C# foreign_proc for C#
+ % is not possible.
+
+:- pragma foreign_proc("C#",
+ catch_impl(_Pred::pred(out) is multi,
_Handler::in(handler), _T::out), [will_not_call_mercury],
local_vars(""),
first_code(""),
retry_code(""),
common_code("
- mercury::runtime::Errors::SORRY(""foreign code for this function"");
+ mercury.runtime.Errors.SORRY(""foreign code for this function"");
")
).
-:- pragma foreign_proc("MC++",
- builtin_catch(_Pred::pred(out) is nondet,
+:- pragma foreign_proc("C#",
+ catch_impl(_Pred::pred(out) is nondet,
_Handler::in(handler), _T::out), [will_not_call_mercury],
local_vars(""),
first_code(""),
retry_code(""),
common_code("
- mercury::runtime::Errors::SORRY(""foreign code for this function"");
+ mercury.runtime.Errors.SORRY(""foreign code for this function"");
")
).
-
*/
-/*********
-This causes problems because the LLDS back-end
-does not let you export code with determinism `nondet'.
-Instead we hand-code it... see below.
-Hand-coding it also avoids the casting needed to use MR_Word
-(which `pragma export' procedures use for polymorphically
-typed arguments) rather than MR_Box.
+
:- pred call_goal(pred(T), T).
:- mode call_goal(pred(out) is det, out) is det.
@@ -1153,11 +1201,22 @@
:- pragma export(call_goal(pred(out) is det, out), "ML_call_goal_det").
:- pragma export(call_goal(pred(out) is semidet, out), "ML_call_goal_semidet").
+
+% This causes problems because the LLDS back-end
+% does not let you export code with determinism `nondet'.
+% Instead for C backends we hand-code it... see below.
+% Hand-coding it also avoids the casting needed to use MR_Word
+% (which `pragma export' procedures use for polymorphically
+% typed arguments) rather than MR_Box.
+%
+% XXX for .NET backend we don't yet implement nondet exception handling.
+
% :- pragma export(call_goal(pred(out) is nondet, out), "ML_call_goal_nondet").
:- pragma export(call_handler(pred(in, out) is det, in, out),
"ML_call_handler_det").
+/*
*******/
%-----------------------------------------------------------------------------%
Index: library/math.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/math.m,v
retrieving revision 1.30
diff -u -r1.30 math.m
--- library/math.m 14 Aug 2001 10:18:47 -0000 1.30
+++ library/math.m 23 Aug 2001 16:37:51 -0000
@@ -267,7 +267,7 @@
static void
ML_math_domain_error(string where)
{
- throw new mercury.runtime.Exception(where);
+ throw new mercury.runtime.SystemException(where);
}
"). % end pragma foreign_code
Index: runtime/mercury_mcpp.cpp
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_mcpp.cpp,v
retrieving revision 1.8
diff -u -r1.8 mercury_mcpp.cpp
--- runtime/mercury_mcpp.cpp 16 Aug 2001 15:04:14 -0000 1.8
+++ runtime/mercury_mcpp.cpp 23 Aug 2001 16:37:53 -0000
@@ -25,17 +25,29 @@
namespace runtime {
- // XXX Exception support is utterly incomplete.
+ // A user exception -- really just a wrapper for the exception
+ // data.
+
__gc public class Exception : public System::Exception
{
public:
- // XXX there should be a Mercury object here.
- Exception(MR_String Msg) : System::Exception(Msg)
- {
- // XXX this should set the exception message
- }
+ Exception(MR_Word data)
+ {
+ mercury_exception = data;
+ }
+ MR_Word mercury_exception;
};
+__gc public class SystemException : public System::Exception
+{
+public:
+ SystemException(MR_String Msg) : System::Exception(Msg)
+ {
+ // XXX this should set the exception message
+ }
+};
+
+
__gc public class LowLevelData
{
@@ -75,14 +87,14 @@
{
MR_String msg;
msg = System::String::Concat("Sorry, unimplemented: ", s);
- throw new mercury::runtime::Exception(msg);
+ throw new mercury::runtime::SystemException(msg);
}
static void fatal_error(MR_String s)
{
MR_String msg;
msg = System::String::Concat("Fatal error: ", s);
- throw new mercury::runtime::Exception(msg);
+ throw new mercury::runtime::SystemException(msg);
}
};
--------------------------------------------------------------------------
mercury-reviews mailing list
post: mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------
More information about the reviews
mailing list