[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