mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-20 16:31:04 +00:00
Estimated hours taken: 36 Instead of generating the layout structures of labels, procs and modules as rvals, generate them almost entirely as C structures. This will make future modifications much easier, since mismatches between what the runtime expects and what the compiler generates will now be pointed out by the C compiler. (It also reduces the size of the C source files generated with debugging enabled by about 5%.) Layout structures contain a few components that are not well-typed in C; we continue to generate these as rvals. Closure layout structures used to have a well-typed part and a non-well-typed part. We now generate the well-typed part as a separate structure, pointed to from the other. We also extend the well-typed part, so that instead of just giving the name the called procedure, it also identifies the source location where the closure was constructed. This could be useful for the debugger and for deep profiling. This diff also includes a change to get the compiler to bootstrap with lcc in grade none.gc.debug.tr: initializing the string tables in module layouts not as a string but as an array of characters. runtime/mercury_stack_layout.h: Reorganize the definitions of layout structures. Rename Stack_Layout_Entry structures as Proc_Layout structures, and Stack_Layout_Label structures as Label_Layout structures. (The debugger paper refers to the structures by the new names.) Fold the Stack_Layout_Vars structure into the structure that contains it, the Label_Layout structure. Add a Closure_Id structure that contains a Proc_Id structure as well as extra information identifying the source location where the closure was created. Create "short" versions of the Proc_Layout structures, which contain only the first one or two of the three groups of fields. Previously, the Mercury compiler would define new C types when it generated such short structures. Since we are not defining new C types anymore, there must be a C type for every kind of structure the Mercury compiler can generate. We now also have separate variants for the layouts of user-defined and compiler-generated procedures, since the format of their procedure id information is different. While the runtime system refers to their procedure id information through a union, the C types of the structures generated by the Mercury compiler do not use a union, since a union cannot be initialized through its second member. Make the constant fields of structures const, since we now generate values of those structure types, and initialize them with constant data. Move the documentation of layout structures here from stack_layout.m. runtime/mercury_ho_call.h: Instead of bodily including an MR_Proc_Id structure in closures, include a pointer to the more detailed MR_Closure_Id structure. runtime/mercury_accurate_gc.c: runtime/mercury_agc_debug.c: runtime/mercury_init.h: runtime/mercury_label.[ch]: runtime/mercury_layout_util.[ch]: Minor updates to conform to changes in mercury_stack_layout.h. runtime/mercury_goto.h: Use separate naming schemes for label layout structures and proc layout structures. library/exception.m: Minor updates to conform to changes in mercury_stack_layout.h. compiler/layout.m: A new module that defines data structures for label, proc and module layout structures and for closure id structures. compiler/layout_out.m: A new module that converts the Mercury data structures of layout.m into declarations and definitions of C data structures. compiler/stack_layout.m: Generate the new layout structures instead of rvals. Move the documentation of layout structures from here to runtime/mercury_stack_layout.h, since this module is no longer aware of some of their details. compiler/llds.m: Make layout structures a separate kind of compiler-generated data. compiler/llds_out.m: Remove the code for the output of layout structures; call layout_out.m instead. compiler/llds_out.m: compiler/rtti_out.m: Turn some predicates into functions. compiler/code_gen.m: compiler/code_info.m: compiler/llds.m: compiler/mercury_compile.m: compiler/unify_gen.m: Instead of handling closure layouts like other static data, handle them separately. Add a counter to the code_info structure in order to allow closure id structures to be identified uniquely by a pair consisting of the id of the procedure that generates them and a closure sequence number within that procedure. compiler/llds_common.m: Look for common rvals among the rvals in layout structures. compiler/opt_debug.m: Generate developer-friendly names for layout structure references. browser/dl.m: Update the code for constructing closure layouts.
1685 lines
52 KiB
Mathematica
1685 lines
52 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1997-2001 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.
|
|
% Stability: medium
|
|
|
|
% 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'
|
|
% throws an exception which is not caught within that procedure, then
|
|
% you will get undefined behaviour.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
:- module exception.
|
|
:- interface.
|
|
:- import_module std_util, list, io.
|
|
|
|
%
|
|
% throw(Exception):
|
|
% Throw the specified exception.
|
|
%
|
|
:- pred throw(T).
|
|
:- mode throw(in) is erroneous.
|
|
|
|
:- func throw(T) = _.
|
|
:- mode throw(in) = out is erroneous.
|
|
|
|
% The following types are used by try/3 and try/5.
|
|
|
|
:- type exception_result(T)
|
|
---> succeeded(T)
|
|
; failed
|
|
; exception(univ).
|
|
|
|
:- inst cannot_fail
|
|
---> succeeded(ground)
|
|
; exception(ground).
|
|
|
|
%
|
|
% try(Goal, Result):
|
|
% Operational semantics:
|
|
% Call Goal(R).
|
|
% If Goal(R) fails, succeed with Result = failed.
|
|
% If Goal(R) succeeds, succeed with Result = succeeded(R).
|
|
% If Goal(R) throws an exception E, succeed with Result = exception(E).
|
|
% Declarative semantics:
|
|
% try(Goal, Result) <=>
|
|
% ( Goal(R), Result = succeeded(R)
|
|
% ; not Goal(_), Result = failed
|
|
% ; Result = exception(_)
|
|
% ).
|
|
%
|
|
:- pred try(pred(T), exception_result(T)).
|
|
:- mode try(pred(out) is det, out(cannot_fail)) is cc_multi.
|
|
:- mode try(pred(out) is semidet, out) is cc_multi.
|
|
:- mode try(pred(out) is cc_multi, out(cannot_fail)) is cc_multi.
|
|
:- mode try(pred(out) is cc_nondet, out) is cc_multi.
|
|
|
|
%
|
|
% try_io(Goal, Result, IO_0, IO):
|
|
% Operational semantics:
|
|
% Call Goal(R, IO_0, IO_1).
|
|
% If it succeeds, succeed with Result = succeeded(R) and IO = IO_1.
|
|
% If it throws an exception E, succeed with Result = exception(E)
|
|
% and with the final IO state being whatever state resulted
|
|
% from the partial computation from IO_0.
|
|
% Declarative semantics:
|
|
% try_io(Goal, Result, IO_0, IO) <=>
|
|
% ( Goal(R, IO_0, IO), Result = succeeded(R)
|
|
% ; Result = exception(_)
|
|
% ).
|
|
%
|
|
:- pred try_io(pred(T, io__state, io__state),
|
|
exception_result(T), io__state, io__state).
|
|
:- mode try_io(pred(out, di, uo) is det,
|
|
out(cannot_fail), di, uo) is cc_multi.
|
|
:- mode try_io(pred(out, di, uo) is cc_multi,
|
|
out(cannot_fail), di, uo) is cc_multi.
|
|
|
|
%
|
|
% try_all(Goal, ResultList):
|
|
% Operational semantics:
|
|
% Try to find all solutions to Goal(R), using backtracking.
|
|
% Collect the solutions found in the ResultList, until
|
|
% the goal either throws an exception or fails.
|
|
% If it throws an exception, put that exception at the end of
|
|
% the ResultList.
|
|
% Declaratively:
|
|
% try_all(Goal, ResultList) <=>
|
|
% (if
|
|
% list__reverse(ResultList, [Last | AllButLast]),
|
|
% Last = exception(_)
|
|
% then
|
|
% all [M] (list__member(M, AllButLast) =>
|
|
% (M = succeeded(R), Goal(R))),
|
|
% else
|
|
% all [M] (list__member(M, ResultList) =>
|
|
% (M = succeeded(R), Goal(R))),
|
|
% all [R] (Goal(R) =>
|
|
% list__member(succeeded(R), ResultList)),
|
|
% ).
|
|
|
|
:- pred try_all(pred(T), list(exception_result(T))).
|
|
:- mode try_all(pred(out) is det, out(try_all_det)) is cc_multi.
|
|
:- mode try_all(pred(out) is semidet, out(try_all_semidet)) is cc_multi.
|
|
:- mode try_all(pred(out) is multi, out(try_all_multi)) is cc_multi.
|
|
:- mode try_all(pred(out) is nondet, out(try_all_nondet)) is cc_multi.
|
|
|
|
:- inst [] ---> [].
|
|
:- inst try_all_det ---> [cannot_fail].
|
|
:- inst try_all_semidet ---> [] ; [cannot_fail].
|
|
:- inst try_all_multi ---> [cannot_fail | try_all_nondet].
|
|
:- inst try_all_nondet == list_skel(cannot_fail).
|
|
|
|
%
|
|
% incremental_try_all(Goal, AccumulatorPred, Acc0, Acc):
|
|
% Same as
|
|
% try_all(Goal, Results),
|
|
% std_util__unsorted_aggregate(Results, AccumulatorPred, Acc0, Acc)
|
|
% except that operationally, the execution of try_all
|
|
% and std_util__unsorted_aggregate is interleaved.
|
|
|
|
:- pred incremental_try_all(pred(T), pred(exception_result(T), A, A), A, A).
|
|
:- mode incremental_try_all(pred(out) is nondet,
|
|
pred(in, di, uo) is det, di, uo) is cc_multi.
|
|
:- mode incremental_try_all(pred(out) is nondet,
|
|
pred(in, in, out) is det, in, out) is cc_multi.
|
|
|
|
%
|
|
% rethrow(ExceptionResult):
|
|
% Rethrows the specified exception result
|
|
% (which should be of the form `exception(_)',
|
|
% not `succeeded(_)' or `failed'.).
|
|
%
|
|
:- pred rethrow(exception_result(T)).
|
|
:- mode rethrow(in(bound(exception(ground)))) is erroneous.
|
|
|
|
:- func rethrow(exception_result(T)) = _.
|
|
:- mode rethrow(in(bound(exception(ground)))) = out is erroneous.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
:- import_module string, require.
|
|
|
|
:- pred try(determinism, pred(T), exception_result(T)).
|
|
:- mode try(in(bound(det)), pred(out) is det, out(cannot_fail))
|
|
is cc_multi.
|
|
:- mode try(in(bound(semidet)), pred(out) is semidet, out) is cc_multi.
|
|
:- mode try(in(bound(cc_multi)), pred(out) is cc_multi, out(cannot_fail))
|
|
is cc_multi.
|
|
:- mode try(in(bound(cc_nondet)), pred(out) is cc_nondet, out) is cc_multi.
|
|
|
|
:- pred try_io(determinism, pred(T, io__state, io__state),
|
|
exception_result(T), io__state, io__state).
|
|
:- mode try_io(in(bound(det)), pred(out, di, uo) is det,
|
|
out(cannot_fail), di, uo) is cc_multi.
|
|
:- mode try_io(in(bound(cc_multi)), pred(out, di, uo) is cc_multi,
|
|
out(cannot_fail), di, uo) is cc_multi.
|
|
|
|
:- pred try_all(determinism, pred(T), list(exception_result(T))).
|
|
:- mode try_all(in(bound(det)), pred(out) is det,
|
|
out(try_all_det)) is cc_multi.
|
|
:- mode try_all(in(bound(semidet)), pred(out) is semidet,
|
|
out(try_all_semidet)) is cc_multi.
|
|
:- mode try_all(in(bound(multi)), pred(out) is multi,
|
|
out(try_all_multi)) is cc_multi.
|
|
:- mode try_all(in(bound(nondet)), pred(out) is nondet,
|
|
out(try_all_nondet)) is cc_multi.
|
|
|
|
% The functors in this type must be in the same order as the
|
|
% enumeration constants in the foreign language enums `ML_Determinism'
|
|
% defined below.
|
|
:- type determinism
|
|
---> det
|
|
; semidet
|
|
; cc_multi
|
|
; cc_nondet
|
|
; multi
|
|
; nondet
|
|
; erroneous
|
|
; failure.
|
|
|
|
:- pred get_determinism(pred(T), determinism).
|
|
:- mode get_determinism(pred(out) is det, out(bound(det))) is cc_multi.
|
|
:- mode get_determinism(pred(out) is semidet, out(bound(semidet))) is cc_multi.
|
|
:- mode get_determinism(pred(out) is multi, out(bound(multi))) is cc_multi.
|
|
:- mode get_determinism(pred(out) is nondet, out(bound(nondet))) is cc_multi.
|
|
:- mode get_determinism(pred(out) is cc_multi, out(bound(cc_multi)))
|
|
is cc_multi.
|
|
:- mode get_determinism(pred(out) is cc_nondet, out(bound(cc_nondet)))
|
|
is cc_multi.
|
|
|
|
:- pred get_determinism_2(pred(T, io__state, io__state), determinism).
|
|
:- mode get_determinism_2(pred(out, di, uo) is det, out(bound(det)))
|
|
is cc_multi.
|
|
:- mode get_determinism_2(pred(out, di, uo) is cc_multi, out(bound(cc_multi)))
|
|
is cc_multi.
|
|
|
|
% Unfortunately the only way to implement get_determinism/2 is to use
|
|
% the C interface, since Mercury doesn't allow different code for different
|
|
% modes.
|
|
|
|
:- pragma foreign_decl("C", "
|
|
/* The `#ifndef ... #define ... #endif' guards against multiple inclusion */
|
|
#ifndef ML_DETERMINISM_GUARD
|
|
#define ML_DETERMINISM_GUARD
|
|
/*
|
|
** The enumeration constants in this enum must be in the same
|
|
** order as the functors in the Mercury type `determinism'
|
|
** defined above.
|
|
*/
|
|
typedef enum {
|
|
ML_DET,
|
|
ML_SEMIDET,
|
|
ML_CC_MULTI,
|
|
ML_CC_NONDET,
|
|
ML_MULTI,
|
|
ML_NONDET,
|
|
ML_ERRONEOUS,
|
|
ML_FAILURE
|
|
} ML_Determinism;
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_code("C",
|
|
get_determinism(_Pred::pred(out) is det,
|
|
Det::out(bound(det))),
|
|
will_not_call_mercury,
|
|
"Det = ML_DET"
|
|
).
|
|
:- pragma foreign_code("C",
|
|
get_determinism(_Pred::pred(out) is semidet,
|
|
Det::out(bound(semidet))),
|
|
will_not_call_mercury,
|
|
"Det = ML_SEMIDET"
|
|
).
|
|
:- pragma foreign_code("C",
|
|
get_determinism(_Pred::pred(out) is cc_multi,
|
|
Det::out(bound(cc_multi))),
|
|
will_not_call_mercury,
|
|
"Det = ML_CC_MULTI"
|
|
).
|
|
:- pragma foreign_code("C",
|
|
get_determinism(_Pred::pred(out) is cc_nondet,
|
|
Det::out(bound(cc_nondet))),
|
|
will_not_call_mercury,
|
|
"Det = ML_CC_NONDET"
|
|
).
|
|
:- pragma foreign_code("C",
|
|
get_determinism(_Pred::pred(out) is multi,
|
|
Det::out(bound(multi))),
|
|
will_not_call_mercury,
|
|
"Det = ML_MULTI"
|
|
).
|
|
:- pragma foreign_code("C",
|
|
get_determinism(_Pred::pred(out) is nondet,
|
|
Det::out(bound(nondet))),
|
|
will_not_call_mercury,
|
|
"Det = ML_NONDET"
|
|
).
|
|
|
|
:- pragma foreign_code("C",
|
|
get_determinism_2(_Pred::pred(out, di, uo) is det,
|
|
Det::out(bound(det))),
|
|
will_not_call_mercury,
|
|
"Det = ML_DET"
|
|
).
|
|
|
|
:- pragma foreign_code("C",
|
|
get_determinism_2(_Pred::pred(out, di, uo) is cc_multi,
|
|
Det::out(bound(cc_multi))),
|
|
will_not_call_mercury,
|
|
"Det = ML_CC_MULTI"
|
|
).
|
|
|
|
:- pragma foreign_decl("MC++", "
|
|
/* The `#ifndef ... #define ... #endif' guards against multiple inclusion */
|
|
#ifndef ML_DETERMINISM_GUARD
|
|
#define ML_DETERMINISM_GUARD
|
|
|
|
/*
|
|
** The enumeration constants in this enum must be in the same
|
|
** order as the functors in the Mercury type `determinism'
|
|
** defined above.
|
|
*/
|
|
typedef enum {
|
|
ML_DET,
|
|
ML_SEMIDET,
|
|
ML_CC_MULTI,
|
|
ML_CC_NONDET,
|
|
ML_MULTI,
|
|
ML_NONDET,
|
|
ML_ERRONEOUS,
|
|
ML_FAILURE
|
|
} ML_Determinism;
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_code("MC++",
|
|
get_determinism(_Pred::pred(out) is det,
|
|
Det::out(bound(det))),
|
|
will_not_call_mercury,
|
|
"MR_newenum(Det, ML_DET);"
|
|
).
|
|
:- pragma foreign_code("MC++",
|
|
get_determinism(_Pred::pred(out) is semidet,
|
|
Det::out(bound(semidet))),
|
|
will_not_call_mercury,
|
|
"MR_newenum(Det, ML_SEMIDET);"
|
|
).
|
|
:- pragma foreign_code("MC++",
|
|
get_determinism(_Pred::pred(out) is cc_multi,
|
|
Det::out(bound(cc_multi))),
|
|
will_not_call_mercury,
|
|
"MR_newenum(Det, ML_CC_MULTI);"
|
|
).
|
|
:- pragma foreign_code("MC++",
|
|
get_determinism(_Pred::pred(out) is cc_nondet,
|
|
Det::out(bound(cc_nondet))),
|
|
will_not_call_mercury,
|
|
"MR_newenum(Det, ML_CC_NONDET);"
|
|
).
|
|
:- pragma foreign_code("MC++",
|
|
get_determinism(_Pred::pred(out) is multi,
|
|
Det::out(bound(multi))),
|
|
will_not_call_mercury,
|
|
"MR_newenum(Det, ML_MULTI);"
|
|
).
|
|
:- pragma foreign_code("MC++",
|
|
get_determinism(_Pred::pred(out) is nondet,
|
|
Det::out(bound(nondet))),
|
|
will_not_call_mercury,
|
|
"MR_newenum(Det, ML_NONDET);"
|
|
).
|
|
|
|
:- pragma foreign_code("MC++",
|
|
get_determinism_2(_Pred::pred(out, di, uo) is det,
|
|
Det::out(bound(det))),
|
|
will_not_call_mercury,
|
|
"MR_newenum(Det, ML_DET);"
|
|
).
|
|
|
|
:- pragma foreign_code("MC++",
|
|
get_determinism_2(_Pred::pred(out, di, uo) is cc_multi,
|
|
Det::out(bound(cc_multi))),
|
|
will_not_call_mercury,
|
|
"MR_newenum(Det, ML_CC_MULTI);"
|
|
).
|
|
|
|
|
|
% These are not worth inlining, since they will
|
|
% (presumably) not be called frequently, and so
|
|
% any increase in speed from inlining is not worth
|
|
% the increase in code size.
|
|
:- pragma no_inline(throw/1).
|
|
:- pragma no_inline(rethrow/1).
|
|
|
|
throw(Exception) :-
|
|
type_to_univ(Exception, Univ),
|
|
builtin_throw(Univ).
|
|
|
|
throw(Exception) = _ :-
|
|
throw(Exception).
|
|
|
|
rethrow(exception(Univ)) :-
|
|
builtin_throw(Univ).
|
|
|
|
rethrow(ExceptionResult) = _ :-
|
|
rethrow(ExceptionResult).
|
|
|
|
:- 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 multi, out) is multi.
|
|
:- mode wrap_success(pred(out) is nondet, out) is nondet.
|
|
:- mode wrap_success(pred(out) is cc_multi, out) is cc_multi.
|
|
:- mode wrap_success(pred(out) is cc_nondet, out) is cc_nondet.
|
|
wrap_success(Goal, succeeded(R)) :- Goal(R).
|
|
|
|
:- pred wrap_success_or_failure(pred(T), exception_result(T)) is det.
|
|
:- mode wrap_success_or_failure(pred(out) is det, out) is det.
|
|
:- mode wrap_success_or_failure(pred(out) is semidet, out) is det.
|
|
:- mode wrap_success_or_failure(pred(out) is multi, out) is multi.
|
|
:- mode wrap_success_or_failure(pred(out) is nondet, out) is multi.
|
|
:- mode wrap_success_or_failure(pred(out) is cc_multi, out) is cc_multi.
|
|
:- mode wrap_success_or_failure(pred(out) is cc_nondet, out) is cc_multi.
|
|
wrap_success_or_failure(Goal, Result) :-
|
|
(if Goal(R) then Result = succeeded(R) else Result = failed).
|
|
|
|
/*********************
|
|
% This doesn't work, due to
|
|
% bash$ mmc exception.m
|
|
% Software error: sorry, not implemented: taking address of pred
|
|
% `wrap_success_or_failure/2' with multiple modes.
|
|
% Instead, we need to switch on the Detism argument.
|
|
|
|
try(_Detism, Goal, Result) :-
|
|
builtin_catch(wrap_success_or_failure(Goal), wrap_exception, Result).
|
|
*********************/
|
|
|
|
try(Goal, Result) :-
|
|
get_determinism(Goal, Detism),
|
|
try(Detism, Goal, Result).
|
|
|
|
try(det, Goal, Result) :-
|
|
builtin_catch((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 :-
|
|
wrap_success_or_failure(Goal, R)),
|
|
wrap_exception, Result0),
|
|
cc_multi_equal(Result0, Result).
|
|
try(cc_multi, Goal, Result) :-
|
|
|
|
builtin_catch(
|
|
(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 :-
|
|
wrap_success_or_failure(Goal, R)),
|
|
wrap_exception, Result).
|
|
|
|
|
|
/**********
|
|
% This doesn't work, due to
|
|
% bash$ mmc exception.m
|
|
% Software error: sorry, not implemented: taking address of pred
|
|
% `wrap_success_or_failure/2' with multiple modes.
|
|
% Instead, we need to switch on the Detism argument.
|
|
|
|
try_all(Goal, ResultList) :-
|
|
unsorted_solutions(builtin_catch(wrap_success(Goal), wrap_exception),
|
|
ResultList).
|
|
**********/
|
|
|
|
try_all(Goal, ResultList) :-
|
|
get_determinism(Goal, Detism),
|
|
try_all(Detism, Goal, ResultList).
|
|
|
|
try_all(det, Goal, [Result]) :-
|
|
try(det, Goal, Result).
|
|
try_all(semidet, Goal, ResultList) :-
|
|
try(semidet, Goal, Result),
|
|
( Result = failed, ResultList = []
|
|
; Result = succeeded(_), ResultList = [Result]
|
|
; Result = exception(_), ResultList = [Result]
|
|
).
|
|
try_all(multi, Goal, ResultList) :-
|
|
unsorted_solutions((pred(Result::out) is multi :-
|
|
builtin_catch((pred(R::out) is multi :-
|
|
wrap_success(Goal, R)),
|
|
wrap_exception, Result)),
|
|
ResultList).
|
|
try_all(nondet, Goal, ResultList) :-
|
|
unsorted_solutions((pred(Result::out) is nondet :-
|
|
builtin_catch((pred(R::out) is nondet :-
|
|
wrap_success(Goal, R)),
|
|
wrap_exception, Result)),
|
|
ResultList).
|
|
|
|
incremental_try_all(Goal, AccPred, Acc0, Acc) :-
|
|
unsorted_aggregate((pred(Result::out) is nondet :-
|
|
builtin_catch((pred(R::out) is nondet :-
|
|
wrap_success(Goal, R)),
|
|
wrap_exception, Result)),
|
|
AccPred, Acc0, Acc).
|
|
|
|
try_io(IO_Goal, Result) -->
|
|
{ get_determinism_2(IO_Goal, Detism) },
|
|
try_io(Detism, IO_Goal, Result).
|
|
|
|
% We'd better not inline try_io/5, since it uses a horrible hack
|
|
% with unsafe_perform_io (see below) that might confuse the compiler.
|
|
:- pragma no_inline(try_io/5).
|
|
try_io(det, IO_Goal, Result) -->
|
|
{ Goal = (pred(R::out) is det :-
|
|
very_unsafe_perform_io(IO_Goal, R)) },
|
|
{ try(det, Goal, Result) }.
|
|
try_io(cc_multi, IO_Goal, Result) -->
|
|
{ Goal = (pred(R::out) is cc_multi :-
|
|
very_unsafe_perform_io(IO_Goal, R)) },
|
|
{ try(cc_multi, Goal, Result) }.
|
|
|
|
:- pred very_unsafe_perform_io(pred(T, io__state, io__state), T).
|
|
:- mode very_unsafe_perform_io(pred(out, di, uo) is det, out) is det.
|
|
:- mode very_unsafe_perform_io(pred(out, di, uo) is cc_multi, out)
|
|
is cc_multi.
|
|
% Mercury doesn't support impure higher-order pred terms, so if we want
|
|
% to form a closure from unsafe_perform_io, as we need to do above,
|
|
% then we must (falsely!) promise that it is pure.
|
|
:- pragma promise_pure(very_unsafe_perform_io/2). % XXX this is a lie
|
|
|
|
very_unsafe_perform_io(Goal, Result) :-
|
|
impure make_io_state(IOState0),
|
|
Goal(Result, IOState0, IOState),
|
|
impure consume_io_state(IOState).
|
|
|
|
:- impure pred make_io_state(io__state::uo) is det.
|
|
:- pragma foreign_code("C", make_io_state(_IO::uo),
|
|
[will_not_call_mercury, thread_safe], "").
|
|
:- pragma foreign_code("MC++", make_io_state(_IO::uo),
|
|
[will_not_call_mercury, thread_safe], "").
|
|
|
|
:- impure pred consume_io_state(io__state::di) is det.
|
|
:- pragma foreign_code("C", consume_io_state(_IO::di),
|
|
[will_not_call_mercury, thread_safe], "").
|
|
:- pragma foreign_code("MC++", consume_io_state(_IO::di),
|
|
[will_not_call_mercury, thread_safe], "").
|
|
|
|
:- 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).
|
|
|
|
%
|
|
% builtin_catch/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.
|
|
%
|
|
:- /* 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.
|
|
|
|
% builtin_throw and builtin_catch are implemented below using
|
|
% hand-coded low-level C code.
|
|
|
|
:- external(builtin_throw/1).
|
|
:- external(builtin_catch/3).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% The --high-level-code implementation
|
|
%
|
|
|
|
:- pragma c_header_code("
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
|
|
/* det ==> model_det */
|
|
#define mercury__exception__builtin_catch_3_p_0 \
|
|
mercury__exception__builtin_catch_model_det
|
|
|
|
/* semidet ==> model_semi */
|
|
#define mercury__exception__builtin_catch_3_p_1 \
|
|
mercury__exception__builtin_catch_model_semi
|
|
|
|
/* cc_multi ==> model_det */
|
|
#define mercury__exception__builtin_catch_3_p_2 \
|
|
mercury__exception__builtin_catch_model_det
|
|
|
|
/* cc_nondet ==> model_semi */
|
|
#define mercury__exception__builtin_catch_3_p_3 \
|
|
mercury__exception__builtin_catch_model_semi
|
|
|
|
/* multi ==> model_non */
|
|
#define mercury__exception__builtin_catch_3_p_4 \
|
|
mercury__exception__builtin_catch_model_non
|
|
|
|
/* nondet ==> model_non */
|
|
#define mercury__exception__builtin_catch_3_p_5 \
|
|
mercury__exception__builtin_catch_model_non
|
|
|
|
void MR_CALL mercury__exception__builtin_throw_1_p_0(MR_Univ);
|
|
|
|
void MR_CALL mercury__exception__builtin_throw_1_p_0(MR_Univ exception);
|
|
void MR_CALL mercury__exception__builtin_catch_model_det(
|
|
MR_Mercury_Type_Info type_info, MR_Pred pred,
|
|
MR_Pred handler_pred, MR_Box *output);
|
|
bool MR_CALL mercury__exception__builtin_catch_model_semi(
|
|
MR_Mercury_Type_Info type_info, MR_Pred pred,
|
|
MR_Pred handler_pred, MR_Box *output);
|
|
void MR_CALL mercury__exception__builtin_catch_model_non(
|
|
MR_Mercury_Type_Info type_info, MR_Pred pred,
|
|
MR_Pred handler_pred, MR_Box *output,
|
|
#ifdef MR_USE_GCC_NESTED_FUNCTIONS
|
|
MR_NestedCont cont
|
|
#else
|
|
MR_Cont cont, void *cont_env
|
|
#endif
|
|
);
|
|
#endif /* MR_HIGHLEVEL_CODE */
|
|
").
|
|
|
|
:- pragma c_code("
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void
|
|
ML_call_goal_det(MR_Mercury_Type_Info type_info,
|
|
MR_Pred closure, MR_Box *result)
|
|
{
|
|
typedef void MR_CALL DetFuncType(void *, MR_Box *);
|
|
DetFuncType *code = (DetFuncType *)
|
|
MR_field(MR_mktag(0), closure, (MR_Integer) 1);
|
|
(*code)((void *) closure, result);
|
|
}
|
|
|
|
static bool
|
|
ML_call_goal_semi(MR_Mercury_Type_Info type_info,
|
|
MR_Pred closure, MR_Box *result)
|
|
{
|
|
typedef bool MR_CALL SemidetFuncType(void *, MR_Box *);
|
|
SemidetFuncType *code = (SemidetFuncType *)
|
|
MR_field(MR_mktag(0), closure, (MR_Integer) 1);
|
|
return (*code)((void *) closure, result);
|
|
}
|
|
|
|
#ifdef MR_USE_GCC_NESTED_FUNCTIONS
|
|
|
|
static void
|
|
ML_call_goal_non(MR_Mercury_Type_Info type_info,
|
|
MR_Pred closure, MR_Box *result, MR_NestedCont cont)
|
|
{
|
|
typedef void MR_CALL NondetFuncType(void *, MR_Box *, MR_NestedCont);
|
|
NondetFuncType *code = (NondetFuncType *)
|
|
MR_field(MR_mktag(0), closure, (MR_Integer) 1);
|
|
(*code)((void *) closure, result, cont);
|
|
}
|
|
|
|
#else
|
|
|
|
static void
|
|
ML_call_goal_non(MR_Mercury_Type_Info type_info,
|
|
MR_Pred closure, MR_Box *result, MR_Cont cont, void *cont_env)
|
|
{
|
|
typedef void MR_CALL NondetFuncType(void *, MR_Box *, MR_Cont, void *);
|
|
NondetFuncType *code = (NondetFuncType *)
|
|
MR_field(MR_mktag(0), closure, (MR_Integer) 1);
|
|
(*code)((void *) closure, result, cont, cont_env);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void
|
|
ML_call_handler_det(MR_Mercury_Type_Info type_info,
|
|
MR_Pred closure, MR_Univ exception, MR_Box *result)
|
|
{
|
|
typedef void MR_CALL HandlerFuncType(void *, MR_Box, MR_Box *);
|
|
HandlerFuncType *code = (HandlerFuncType *)
|
|
MR_field(MR_mktag(0), closure, (MR_Integer) 1);
|
|
(*code)((void *) closure, (MR_Box) exception, result);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#include <stdlib.h>
|
|
#include <setjmp.h>
|
|
|
|
typedef struct ML_ExceptionHandler_struct {
|
|
struct ML_ExceptionHandler_struct *prev;
|
|
jmp_buf handler;
|
|
MR_Univ exception;
|
|
} ML_ExceptionHandler;
|
|
|
|
ML_ExceptionHandler *ML_exception_handler;
|
|
|
|
void MR_CALL
|
|
mercury__exception__builtin_throw_1_p_0(MR_Univ exception)
|
|
{
|
|
if (ML_exception_handler == NULL) {
|
|
ML_report_uncaught_exception((MR_Word) exception);
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""throw longjmp %p\\n"",
|
|
ML_exception_handler->handler);
|
|
#endif
|
|
ML_exception_handler->exception = exception;
|
|
longjmp(ML_exception_handler->handler, 1);
|
|
}
|
|
}
|
|
|
|
void MR_CALL
|
|
mercury__exception__builtin_catch_model_det(MR_Mercury_Type_Info type_info,
|
|
MR_Pred pred, MR_Pred handler_pred, MR_Box *output)
|
|
{
|
|
ML_ExceptionHandler this_handler;
|
|
|
|
this_handler.prev = ML_exception_handler;
|
|
ML_exception_handler = &this_handler;
|
|
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""detcatch setjmp %p\\n"", this_handler.handler);
|
|
#endif
|
|
|
|
if (setjmp(this_handler.handler) == 0) {
|
|
ML_call_goal_det(type_info, pred, output);
|
|
ML_exception_handler = this_handler.prev;
|
|
} else {
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""detcatch caught jmp %p\\n"",
|
|
this_handler.handler);
|
|
#endif
|
|
|
|
ML_exception_handler = this_handler.prev;
|
|
ML_call_handler_det(type_info, handler_pred,
|
|
this_handler.exception, output);
|
|
}
|
|
}
|
|
|
|
bool MR_CALL
|
|
mercury__exception__builtin_catch_model_semi(MR_Mercury_Type_Info type_info,
|
|
MR_Pred pred, MR_Pred handler_pred, MR_Box *output)
|
|
{
|
|
ML_ExceptionHandler this_handler;
|
|
|
|
this_handler.prev = ML_exception_handler;
|
|
ML_exception_handler = &this_handler;
|
|
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""semicatch setjmp %p\\n"", this_handler.handler);
|
|
#endif
|
|
|
|
if (setjmp(this_handler.handler) == 0) {
|
|
bool result = ML_call_goal_semi(type_info, pred, output);
|
|
ML_exception_handler = this_handler.prev;
|
|
return result;
|
|
} else {
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""semicatch caught jmp %p\\n"",
|
|
this_handler.handler);
|
|
#endif
|
|
|
|
ML_exception_handler = this_handler.prev;
|
|
ML_call_handler_det(type_info, handler_pred,
|
|
this_handler.exception, output);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
#ifdef MR_USE_GCC_NESTED_FUNCTIONS
|
|
|
|
void MR_CALL
|
|
mercury__exception__builtin_catch_model_non(MR_Mercury_Type_Info type_info,
|
|
MR_Pred pred, MR_Pred handler_pred, MR_Box *output,
|
|
MR_NestedCont cont)
|
|
{
|
|
ML_ExceptionHandler this_handler;
|
|
|
|
auto void MR_CALL success_cont(void);
|
|
void MR_CALL success_cont(void) {
|
|
/*
|
|
** If we reach here, it means that
|
|
** the nondet goal has succeeded, so we
|
|
** need to restore the previous exception
|
|
** handler before calling its continuation
|
|
*/
|
|
ML_exception_handler = this_handler.prev;
|
|
(*cont)();
|
|
|
|
/*
|
|
** If we get here, it means that the continuation
|
|
** has failed, and so we are about to redo the
|
|
** nondet goal. Thus we need to re-establish
|
|
** its exception handler.
|
|
*/
|
|
ML_exception_handler = &this_handler;
|
|
}
|
|
|
|
this_handler.prev = ML_exception_handler;
|
|
ML_exception_handler = &this_handler;
|
|
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""noncatch setjmp %p\\n"", this_handler.handler);
|
|
#endif
|
|
|
|
if (setjmp(this_handler.handler) == 0) {
|
|
ML_call_goal_non(type_info, pred, output, success_cont);
|
|
ML_exception_handler = this_handler.prev;
|
|
} else {
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""noncatch caught jmp %p\\n"",
|
|
this_handler.handler);
|
|
#endif
|
|
|
|
ML_exception_handler = this_handler.prev;
|
|
ML_call_handler_det(type_info, handler_pred,
|
|
this_handler.exception, output);
|
|
(*cont)();
|
|
}
|
|
}
|
|
|
|
#else /* ! MR_USE_GCC_NESTED_FUNCTIONS */
|
|
|
|
struct ML_catch_env {
|
|
ML_ExceptionHandler this_handler;
|
|
MR_Cont cont;
|
|
void *cont_env;
|
|
};
|
|
|
|
static void MR_CALL
|
|
ML_catch_success_cont(void *env_ptr) {
|
|
struct ML_catch_env *env = (struct ML_catch_env *) env_ptr;
|
|
|
|
/*
|
|
** If we reach here, it means that
|
|
** the nondet goal has succeeded, so we
|
|
** need to restore the previous exception
|
|
** handler before calling its continuation
|
|
*/
|
|
ML_exception_handler = env->this_handler.prev;
|
|
(*env->cont)(env->cont_env);
|
|
|
|
/*
|
|
** If we get here, it means that the continuation
|
|
** has failed, and so we are about to redo the
|
|
** nondet goal. Thus we need to re-establish
|
|
** its exception handler.
|
|
*/
|
|
ML_exception_handler = &env->this_handler;
|
|
}
|
|
|
|
void MR_CALL
|
|
mercury__exception__builtin_catch_model_non(MR_Mercury_Type_Info type_info,
|
|
MR_Pred pred, MR_Pred handler_pred, MR_Box *output,
|
|
MR_Cont cont, void *cont_env)
|
|
{
|
|
struct ML_catch_env locals;
|
|
locals.cont = cont;
|
|
locals.cont_env = cont_env;
|
|
|
|
locals.this_handler.prev = ML_exception_handler;
|
|
ML_exception_handler = &locals.this_handler;
|
|
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""noncatch setjmp %p\\n"", locals.this_handler.handler);
|
|
#endif
|
|
|
|
if (setjmp(locals.this_handler.handler) == 0) {
|
|
ML_call_goal_non(type_info, pred, output,
|
|
ML_catch_success_cont, &locals);
|
|
/*
|
|
** If we reach here, it means that
|
|
** the nondet goal has failed, so we
|
|
** need to restore the previous exception
|
|
** handler
|
|
*/
|
|
ML_exception_handler = locals.this_handler.prev;
|
|
return;
|
|
} else {
|
|
/*
|
|
** We caught an exception.
|
|
** Restore the previous exception handler,
|
|
** and then invoke the handler predicate
|
|
** for this handler.
|
|
*/
|
|
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""noncatch caught jmp %p\\n"",
|
|
locals.this_handler.handler);
|
|
#endif
|
|
|
|
|
|
ML_exception_handler = locals.this_handler.prev;
|
|
ML_call_handler_det(type_info, handler_pred,
|
|
locals.this_handler.exception, output);
|
|
cont(cont_env);
|
|
}
|
|
}
|
|
|
|
#endif /* ! MR_USE_GCC_NESTED_FUNCTIONS */
|
|
|
|
#endif /* MR_HIGHLEVEL_CODE */
|
|
").
|
|
|
|
/*
|
|
|
|
XXX :- external stops us from using this
|
|
|
|
:- pragma foreign_code("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"");
|
|
}
|
|
|
|
throw ex;
|
|
").
|
|
|
|
:- pragma foreign_code("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_code("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_code("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"");
|
|
").
|
|
:- pragma foreign_code("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_code("MC++",
|
|
builtin_catch(_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"");
|
|
")
|
|
).
|
|
:- pragma foreign_code("MC++",
|
|
builtin_catch(_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"");
|
|
")
|
|
).
|
|
|
|
*/
|
|
|
|
|
|
/*********
|
|
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.
|
|
:- mode call_goal(pred(out) is semidet, out) is semidet.
|
|
:- mode call_goal(pred(out) is nondet, out) is nondet.
|
|
|
|
call_goal(Goal, Result) :- Goal(Result).
|
|
|
|
:- pred call_handler(pred(univ, T), univ, T).
|
|
:- mode call_handler(pred(in, out) is det, in, out) is det.
|
|
:- mode call_handler(pred(in, out) is semidet, in, out) is semidet.
|
|
:- mode call_handler(pred(in, out) is nondet, in, out) is nondet.
|
|
|
|
call_handler(Handler, Exception, Result) :- Handler(Exception, Result).
|
|
|
|
:- 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").
|
|
% :- 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").
|
|
|
|
*******/
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% The --no-high-level-code implementation
|
|
%
|
|
|
|
:- pragma c_header_code("
|
|
#ifndef MR_HIGHLEVEL_CODE
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include ""mercury_deep_copy.h""
|
|
#include ""mercury_trace_base.h""
|
|
#include ""mercury_stack_trace.h""
|
|
#include ""mercury_layout_util.h""
|
|
|
|
MR_DECLARE_TYPE_CTOR_INFO_STRUCT( \
|
|
mercury_data_std_util__type_ctor_info_univ_0);
|
|
#endif
|
|
").
|
|
|
|
:- pragma c_code("
|
|
#ifdef MR_HIGHLEVEL_CODE
|
|
void mercury_sys_init_exceptions(void);
|
|
void mercury_sys_init_exceptions(void) { return; }
|
|
#else
|
|
|
|
/*
|
|
** MR_trace_throw():
|
|
** Unwind the stack as far as possible, until we reach a frame
|
|
** with an exception handler. As we go, invoke
|
|
** `MR_trace(..., MR_PORT_EXCEPTION, ...)' for each stack frame,
|
|
** to signal to the debugger that that procedure has exited via
|
|
** an exception. This allows to user to use the `retry' command
|
|
** to restart a goal which exited via an exception.
|
|
**
|
|
** Note that if MR_STACK_TRACE is not defined, then we may not be
|
|
** able to traverse the stack all the way; in that case, we just
|
|
** print a warning and then continue. It might be better to just
|
|
** `#ifdef' out all this code (and the code in builtin_throw which
|
|
** calls it) if MR_STACK_TRACE is not defined.
|
|
*/
|
|
|
|
#define WARNING(msg) \\
|
|
do { \\
|
|
fflush(stdout); \\
|
|
fprintf(stderr, ""mdb: warning: %s\\n"" \\
|
|
""This may result in some exception events\\n"" \\
|
|
""being omitted from the trace.\\n"", (msg)); \\
|
|
} while (0)
|
|
|
|
static MR_Code *
|
|
MR_trace_throw(MR_Code *success_pointer, MR_Word *det_stack_pointer,
|
|
MR_Word *current_frame)
|
|
{
|
|
const MR_Internal *label;
|
|
const MR_Label_Layout *return_label_layout;
|
|
|
|
/*
|
|
** Find the layout info for the stack frame pointed to by MR_succip
|
|
*/
|
|
label = MR_lookup_internal_by_addr(success_pointer);
|
|
if (label == NULL) {
|
|
WARNING(""internal label not found\\n"");
|
|
return NULL;
|
|
}
|
|
return_label_layout = label->i_layout;
|
|
|
|
while (return_label_layout != NULL) {
|
|
const MR_Proc_Layout *entry_layout;
|
|
MR_Code *MR_jumpaddr;
|
|
MR_Stack_Walk_Step_Result result;
|
|
const char *problem;
|
|
|
|
/*
|
|
** check if we've reached a frame with an exception handler
|
|
*/
|
|
entry_layout = return_label_layout->MR_sll_entry;
|
|
if (!MR_DETISM_DET_STACK(entry_layout->MR_sle_detism)
|
|
&& MR_redoip_slot(current_frame) ==
|
|
MR_ENTRY(MR_exception_handler_do_fail))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
** invoke MR_trace() to trace the exception
|
|
*/
|
|
if (return_label_layout->MR_sll_port != MR_PORT_EXCEPTION) {
|
|
MR_fatal_error(""return layout port is not exception"");
|
|
}
|
|
|
|
MR_jumpaddr = MR_trace(return_label_layout);
|
|
if (MR_jumpaddr != NULL) {
|
|
return MR_jumpaddr;
|
|
}
|
|
|
|
/*
|
|
** unwind the stacks back to the previous stack frame
|
|
*/
|
|
result = MR_stack_walk_step(entry_layout, &return_label_layout,
|
|
&det_stack_pointer, ¤t_frame, &problem);
|
|
if (result != STEP_OK) {
|
|
WARNING(problem);
|
|
return NULL;
|
|
}
|
|
MR_restore_transient_registers();
|
|
MR_sp = det_stack_pointer;
|
|
MR_curfr = current_frame;
|
|
MR_save_transient_registers();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* swap the heap with the solutions heap */
|
|
#define swap_heaps() \\
|
|
{ \\
|
|
/* save the current heap */ \\
|
|
MR_Word *swap_heaps_temp_hp; \\
|
|
MR_MemoryZone *swap_heaps_temp_hp_zone; \\
|
|
\\
|
|
swap_heaps_temp_hp = MR_hp; \\
|
|
swap_heaps_temp_hp_zone = MR_ENGINE(heap_zone); \\
|
|
\\
|
|
/* set heap to solutions heap */ \\
|
|
MR_hp = MR_sol_hp; \\
|
|
MR_ENGINE(heap_zone) = MR_ENGINE(solutions_heap_zone); \\
|
|
\\
|
|
/* set the solutions heap to be the old heap */ \\
|
|
MR_sol_hp = swap_heaps_temp_hp; \\
|
|
MR_ENGINE(solutions_heap_zone) = swap_heaps_temp_hp_zone; \\
|
|
}
|
|
|
|
MR_define_extern_entry(mercury__exception__builtin_catch_3_0); /* det */
|
|
MR_define_extern_entry(mercury__exception__builtin_catch_3_1); /* semidet */
|
|
MR_define_extern_entry(mercury__exception__builtin_catch_3_2); /* cc_multi */
|
|
MR_define_extern_entry(mercury__exception__builtin_catch_3_3); /* cc_nondet */
|
|
MR_define_extern_entry(mercury__exception__builtin_catch_3_4); /* multi */
|
|
MR_define_extern_entry(mercury__exception__builtin_catch_3_5); /* nondet */
|
|
|
|
MR_define_extern_entry(mercury__exception__builtin_throw_1_0);
|
|
|
|
/* the following is defined in runtime/mercury_ho_call.c */
|
|
MR_declare_entry(mercury__do_call_closure);
|
|
|
|
/* the following is defined in runtime/mercury_trace_base.c */
|
|
MR_declare_entry(MR_do_trace_redo_fail);
|
|
|
|
MR_declare_label(mercury__exception__builtin_catch_3_2_i2);
|
|
MR_declare_label(mercury__exception__builtin_catch_3_3_i2);
|
|
MR_declare_label(mercury__exception__builtin_catch_3_5_i2);
|
|
#ifdef MR_USE_TRAIL
|
|
MR_declare_label(mercury__exception__builtin_catch_3_5_i3);
|
|
#endif
|
|
MR_declare_label(mercury__exception__builtin_throw_1_0_i1);
|
|
|
|
#define BUILTIN_THROW_STACK_SIZE 1
|
|
|
|
|
|
/*
|
|
** MR_MAKE_PROC_LAYOUT(entry, detism, slots, succip_locn, pred_or_func,
|
|
** module, name, arity, mode)
|
|
*/
|
|
|
|
MR_MAKE_PROC_LAYOUT(mercury__exception__builtin_throw_1_0,
|
|
MR_DETISM_DET, BUILTIN_THROW_STACK_SIZE, MR_LONG_LVAL_STACKVAR(1),
|
|
MR_PREDICATE, ""exception"", ""builtin_throw"", 1, 0);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_throw_1_0, 1);
|
|
|
|
/*
|
|
** The following procedures all allocate their stack frames on
|
|
** the nondet stack, so for the purposes of doing stack traces
|
|
** we say they have MR_DETISM_NON, even though they are not
|
|
** actually nondet.
|
|
*/
|
|
MR_MAKE_PROC_LAYOUT(mercury__exception__builtin_catch_3_2,
|
|
MR_DETISM_NON, /* really cc_multi; also used for det */
|
|
MR_ENTRY_NO_SLOT_COUNT, MR_LONG_LVAL_TYPE_UNKNOWN,
|
|
MR_PREDICATE, ""exception"", ""builtin_catch"", 3, 2);
|
|
MR_MAKE_PROC_LAYOUT(mercury__exception__builtin_catch_3_3,
|
|
MR_DETISM_NON, /* really cc_nondet; also used for semidet */
|
|
MR_ENTRY_NO_SLOT_COUNT, MR_LONG_LVAL_TYPE_UNKNOWN,
|
|
MR_PREDICATE, ""exception"", ""builtin_catch"", 3, 3);
|
|
MR_MAKE_PROC_LAYOUT(mercury__exception__builtin_catch_3_5,
|
|
MR_DETISM_NON, /* ; also used for multi */
|
|
MR_ENTRY_NO_SLOT_COUNT, MR_LONG_LVAL_TYPE_UNKNOWN,
|
|
MR_PREDICATE, ""exception"", ""builtin_catch"", 3, 5);
|
|
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_2, 1);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_2, 2);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_3, 1);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_3, 2);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_5, 1);
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_5, 2);
|
|
#ifdef MR_USE_TRAIL
|
|
MR_MAKE_INTERNAL_LAYOUT(mercury__exception__builtin_catch_3_5, 3);
|
|
#endif
|
|
|
|
MR_BEGIN_MODULE(exceptions_module)
|
|
MR_init_entry(mercury__exception__builtin_catch_3_0);
|
|
MR_init_entry(mercury__exception__builtin_catch_3_1);
|
|
MR_init_entry_sl(mercury__exception__builtin_catch_3_2);
|
|
MR_init_entry_sl(mercury__exception__builtin_catch_3_3);
|
|
MR_init_entry(mercury__exception__builtin_catch_3_4);
|
|
MR_init_entry_sl(mercury__exception__builtin_catch_3_5);
|
|
MR_init_label_sl(mercury__exception__builtin_catch_3_2_i2);
|
|
MR_init_label_sl(mercury__exception__builtin_catch_3_3_i2);
|
|
MR_init_label_sl(mercury__exception__builtin_catch_3_5_i2);
|
|
#ifdef MR_USE_TRAIL
|
|
MR_init_label(mercury__exception__builtin_catch_3_5_i3);
|
|
#endif
|
|
MR_init_entry(mercury__exception__builtin_throw_1_0);
|
|
MR_init_label(mercury__exception__builtin_throw_1_0_i1);
|
|
MR_BEGIN_CODE
|
|
|
|
/*
|
|
** builtin_catch(Goal, Handler, Result)
|
|
** call Goal(R).
|
|
** if succeeds, set Result = R.
|
|
** if throws an exception, call Handler(Exception, Result).
|
|
**
|
|
** This is the model_det version.
|
|
** On entry, we have a type_info (which we don't use) in MR_r1,
|
|
** the Goal to execute in MR_r2 and the Handler in MR_r3.
|
|
** On exit, we should put Result in MR_r1.
|
|
*/
|
|
MR_define_entry(mercury__exception__builtin_catch_3_0); /* det */
|
|
#ifdef PROFILE_CALLS
|
|
{
|
|
MR_tailcall(MR_ENTRY(mercury__exception__builtin_catch_3_2),
|
|
MR_ENTRY(mercury__exception__builtin_catch_3_0));
|
|
}
|
|
#endif
|
|
MR_define_entry(mercury__exception__builtin_catch_3_2); /* cc_multi */
|
|
/*
|
|
** Create an exception handler entry on the nondet stack.
|
|
** (Register MR_r3 holds the Handler closure.)
|
|
*/
|
|
MR_create_exception_handler(""builtin_catch/3 [model_det]"",
|
|
MR_MODEL_DET_HANDLER, MR_r3, MR_ENTRY(MR_do_fail));
|
|
|
|
/*
|
|
** Now call `Goal(Result)'.
|
|
*/
|
|
MR_r1 = MR_r2; /* The Goal to call */
|
|
MR_r2 = 0; /* Zero additional input arguments */
|
|
MR_r3 = 1; /* One output argument */
|
|
MR_call(MR_ENTRY(mercury__do_call_closure),
|
|
MR_LABEL(mercury__exception__builtin_catch_3_2_i2),
|
|
MR_ENTRY(mercury__exception__builtin_catch_3_2));
|
|
|
|
MR_define_label(mercury__exception__builtin_catch_3_2_i2);
|
|
MR_update_prof_current_proc(
|
|
MR_LABEL(mercury__exception__builtin_catch_3_2));
|
|
/*
|
|
** On exit from mercury__do_call_closure, Result is in MR_r1
|
|
**
|
|
** We must now deallocate the ticket and nondet stack frame that
|
|
** were allocated by MR_create_exception_handler().
|
|
*/
|
|
#ifdef MR_USE_TRAIL
|
|
MR_prune_ticket();
|
|
#endif
|
|
MR_succeed_discard();
|
|
|
|
/*
|
|
** builtin_catch(Goal, Handler, Result)
|
|
** call Goal(R).
|
|
** if succeeds, set Result = R.
|
|
** if fails, fail.
|
|
** if throws an exception, call Handler(Exception, Result).
|
|
**
|
|
** This is the model_semi version.
|
|
** On entry, we have a type_info (which we don't use) in MR_r1,
|
|
** the Goal to execute in MR_r2 and the Handler in MR_r3,
|
|
** and on exit, we should put Result in MR_r2.
|
|
*/
|
|
MR_define_entry(mercury__exception__builtin_catch_3_1); /* semidet */
|
|
#ifdef PROFILE_CALLS
|
|
{
|
|
MR_tailcall(MR_ENTRY(mercury__exception__builtin_catch_3_3),
|
|
MR_ENTRY(mercury__exception__builtin_catch_3_1));
|
|
}
|
|
#endif
|
|
MR_define_entry(mercury__exception__builtin_catch_3_3); /* cc_nondet */
|
|
/*
|
|
** Create an exception handler entry on the nondet stack.
|
|
** (Register MR_r3 holds the Handler closure.)
|
|
*/
|
|
MR_create_exception_handler(""builtin_catch/3 [model_semi]"",
|
|
MR_MODEL_SEMI_HANDLER, MR_r3, MR_ENTRY(MR_do_fail));
|
|
|
|
/*
|
|
** Now call `Goal(Result)'.
|
|
*/
|
|
MR_r1 = MR_r2; /* The Goal to call */
|
|
MR_r2 = 0; /* Zero additional input arguments */
|
|
MR_r3 = 1; /* One output argument */
|
|
MR_call(MR_ENTRY(mercury__do_call_closure),
|
|
MR_LABEL(mercury__exception__builtin_catch_3_3_i2),
|
|
MR_ENTRY(mercury__exception__builtin_catch_3_3));
|
|
|
|
MR_define_label(mercury__exception__builtin_catch_3_3_i2);
|
|
MR_update_prof_current_proc(
|
|
MR_LABEL(mercury__exception__builtin_catch_3_3));
|
|
/*
|
|
** On exit from do_call_semidet_closure, the success/failure
|
|
** indicator is in MR_r1, and Result is in MR_r2.
|
|
** Note that we call succeed_discard() to exit regardless
|
|
** of whether MR_r1 is true or false. We just return the MR_r1 value
|
|
** back to our caller.
|
|
*/
|
|
#ifdef MR_USE_TRAIL
|
|
if (MR_r1) {
|
|
MR_prune_ticket();
|
|
} else {
|
|
MR_discard_ticket();
|
|
}
|
|
#endif
|
|
MR_succeed_discard();
|
|
|
|
/*
|
|
** builtin_catch(Goal, Handler, Result)
|
|
** call Goal(R).
|
|
** if succeeds, set Result = R.
|
|
** if fails, fail.
|
|
** if throws an exception, call Handler(Exception, Result).
|
|
**
|
|
** This is the model_non version.
|
|
** On entry, we have a type_info (which we don't use) in MR_r1,
|
|
** the Goal to execute in MR_r2 and the Handler in MR_r3.
|
|
** On exit, we should put Result in MR_r1.
|
|
*/
|
|
MR_define_entry(mercury__exception__builtin_catch_3_4); /* multi */
|
|
#ifdef PROFILE_CALLS
|
|
{
|
|
MR_tailcall(MR_ENTRY(mercury__exception__builtin_catch_3_5),
|
|
MR_ENTRY(mercury__exception__builtin_catch_3_4));
|
|
}
|
|
#endif
|
|
MR_define_entry(mercury__exception__builtin_catch_3_5); /* nondet */
|
|
/*
|
|
** Create an exception handler entry on the nondet stack.
|
|
** (Register MR_r3 holds the Handler closure.)
|
|
*/
|
|
#ifdef MR_USE_TRAIL
|
|
MR_create_exception_handler(""builtin_catch/3 [model_nondet]"",
|
|
MR_MODEL_NON_HANDLER, MR_r3,
|
|
MR_LABEL(mercury__exception__builtin_catch_3_5_i3));
|
|
#else
|
|
MR_create_exception_handler(""builtin_catch/3 [model_nondet]"",
|
|
MR_MODEL_NON_HANDLER, MR_r3, MR_ENTRY(MR_do_fail));
|
|
#endif
|
|
|
|
|
|
/*
|
|
** Now call `Goal(Result)'.
|
|
*/
|
|
MR_r1 = MR_r2; /* the Goal to call */
|
|
MR_r2 = 0; /* Zero additional input arguments */
|
|
MR_r3 = 1; /* One output argument */
|
|
MR_call(MR_ENTRY(mercury__do_call_closure),
|
|
MR_LABEL(mercury__exception__builtin_catch_3_5_i2),
|
|
MR_ENTRY(mercury__exception__builtin_catch_3_5));
|
|
|
|
MR_define_label(mercury__exception__builtin_catch_3_5_i2);
|
|
MR_update_prof_current_proc(
|
|
MR_LABEL(mercury__exception__builtin_catch_3_5));
|
|
/*
|
|
** On exit from do_call_nondet_closure, Result is in MR_r1
|
|
**
|
|
** Note that we need to keep the trail ticket still,
|
|
** in case it is needed again on backtracking.
|
|
** We can only discard it when we MR_fail() out, or
|
|
** (if an exception is thrown) in the throw.
|
|
*/
|
|
MR_succeed();
|
|
|
|
#ifdef MR_USE_TRAIL
|
|
MR_define_label(mercury__exception__builtin_catch_3_5_i3);
|
|
MR_discard_ticket();
|
|
MR_fail();
|
|
#endif
|
|
|
|
/*
|
|
** builtin_throw(Exception):
|
|
** Throw the specified exception.
|
|
** That means unwinding the nondet stack until we find a handler,
|
|
** unwinding all the other Mercury stacks, and then
|
|
** calling longjmp() to unwind the C stack.
|
|
** The longjmp() will branch to builtin_catch which will then
|
|
** call Handler(Exception, Result).
|
|
**
|
|
** On entry, we have Exception in MR_r1.
|
|
*/
|
|
MR_define_entry(mercury__exception__builtin_throw_1_0);
|
|
{
|
|
MR_Word exception = MR_r1;
|
|
MR_Word handler;
|
|
enum MR_HandlerCodeModel catch_code_model;
|
|
MR_Word *orig_curfr;
|
|
MR_Unsigned exception_event_number = MR_trace_event_number;
|
|
|
|
/*
|
|
** let the debugger trace exception throwing
|
|
*/
|
|
if (MR_trace_enabled) {
|
|
MR_Code *MR_jumpaddr;
|
|
MR_trace_set_exception_value(exception);
|
|
MR_save_transient_registers();
|
|
MR_jumpaddr = MR_trace_throw(MR_succip, MR_sp, MR_curfr);
|
|
MR_restore_transient_registers();
|
|
if (MR_jumpaddr != NULL) MR_GOTO(MR_jumpaddr);
|
|
}
|
|
|
|
/*
|
|
** Search the nondet stack for an exception handler,
|
|
** i.e. a frame whose redoip is `MR_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 left choice points
|
|
** behind.
|
|
*/
|
|
orig_curfr = MR_curfr;
|
|
while (MR_redoip_slot(MR_curfr)
|
|
!= MR_ENTRY(MR_exception_handler_do_fail))
|
|
{
|
|
MR_curfr = MR_succfr_slot(MR_curfr);
|
|
if (MR_curfr < MR_CONTEXT(nondetstack_zone)->min) {
|
|
MR_Word *save_succip;
|
|
/*
|
|
** There was no exception handler.
|
|
**
|
|
** We restore the original value of MR_curfr,
|
|
** print out some diagnostics,
|
|
** and then terminate execution.
|
|
**
|
|
** We need to save the registers to the fake_reg
|
|
** array using MR_save_registers() before calling
|
|
** ML_report_uncaught_exception, since that is
|
|
** Mercury code and the C->Mercury interface expects
|
|
** the registers to be saved.
|
|
** We also need to save & restore the MR_succip
|
|
** across that call, since any call to Mercury code
|
|
** may clobber MR_succip (and also the Mercury
|
|
** registers MR_r1, MR_r2, MR_r3, etc., but for those
|
|
** we don't care, since we don't use them).
|
|
** Note that the MR_save_registers() alone is not
|
|
** sufficient since the Mercury code may clobber the
|
|
** copy of MR_succip in the fake_reg.
|
|
*/
|
|
MR_curfr = orig_curfr;
|
|
fflush(stdout);
|
|
save_succip = MR_succip;
|
|
MR_save_registers();
|
|
ML_report_uncaught_exception(exception);
|
|
MR_succip = save_succip;
|
|
MR_trace_report(stderr);
|
|
if (exception_event_number > 0) {
|
|
fprintf(stderr, ""Last trace event before ""
|
|
""the unhandled exception was ""
|
|
""event #%ld.\\n"",
|
|
(long) exception_event_number);
|
|
}
|
|
if (MR_trace_enabled) {
|
|
/*
|
|
** The stack has already been unwound
|
|
** by MR_trace_throw(), so we can't dump it.
|
|
** (In fact, if we tried to dump the now-empty
|
|
** stack, we'd get incorrect results, since
|
|
** MR_trace_throw() does not restore MR_succip
|
|
** to the appropriate value.)
|
|
*/
|
|
} else {
|
|
MR_dump_stack(MR_succip, MR_sp, MR_curfr,
|
|
FALSE);
|
|
}
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Save the handler we found
|
|
*/
|
|
catch_code_model = MR_EXCEPTION_FRAMEVARS->code_model;
|
|
handler = MR_EXCEPTION_FRAMEVARS->handler;
|
|
|
|
/*
|
|
** Reset the success ip (i.e. return address).
|
|
** This ensures that when we return from this procedure,
|
|
** we will return to the caller of `builtin_catch'.
|
|
*/
|
|
MR_succip = MR_succip_slot(MR_curfr);
|
|
|
|
/*
|
|
** Reset the det stack.
|
|
*/
|
|
MR_sp = MR_EXCEPTION_FRAMEVARS->stack_ptr;
|
|
|
|
#ifdef MR_USE_TRAIL
|
|
/*
|
|
** Reset the trail.
|
|
*/
|
|
MR_reset_ticket(MR_EXCEPTION_FRAMEVARS->trail_ptr, MR_exception);
|
|
MR_discard_tickets_to(MR_EXCEPTION_FRAMEVARS->ticket_counter);
|
|
#endif
|
|
#ifndef CONSERVATIVE_GC
|
|
/*
|
|
** Reset the heap. But we need to be careful to preserve the
|
|
** thrown exception object.
|
|
**
|
|
** The following algorithm uses the `solutions heap', and will work
|
|
** with non-conservative gc. We copy the exception object to the
|
|
** solutions_heap, reset the heap pointer, and then copy it back.
|
|
**
|
|
** An improvement to this would be to copy the exception object to the
|
|
** solutions heap, but have deep_copy add an offset to the pointers
|
|
** (at least, those that would otherwise point to the solutions heap),
|
|
** so that, when finished, a block move of the solutions heap back to
|
|
** the real heap will leave all the pointers in the correct place.
|
|
*/
|
|
{
|
|
MR_Word * saved_solns_heap_ptr;
|
|
|
|
/* switch to the solutions heap */
|
|
if (MR_ENGINE(heap_zone) == MR_EXCEPTION_FRAMEVARS->heap_zone) {
|
|
swap_heaps();
|
|
}
|
|
|
|
saved_solns_heap_ptr = MR_hp;
|
|
|
|
/*
|
|
** MR_deep_copy() the exception to the solutions heap.
|
|
** Note that we need to save/restore the hp register, if it
|
|
** is transient, before/after calling MR_deep_copy().
|
|
*/
|
|
assert(MR_EXCEPTION_FRAMEVARS->heap_ptr <=
|
|
MR_EXCEPTION_FRAMEVARS->heap_zone->top);
|
|
MR_save_transient_registers();
|
|
exception = MR_deep_copy(&exception,
|
|
(MR_TypeInfo) &mercury_data_std_util__type_ctor_info_univ_0,
|
|
MR_EXCEPTION_FRAMEVARS->heap_ptr,
|
|
MR_EXCEPTION_FRAMEVARS->heap_zone->top);
|
|
MR_restore_transient_registers();
|
|
|
|
/* switch back to the ordinary heap */
|
|
swap_heaps();
|
|
|
|
/* reset the heap */
|
|
assert(MR_EXCEPTION_FRAMEVARS->heap_ptr <= MR_hp);
|
|
MR_hp = MR_EXCEPTION_FRAMEVARS->heap_ptr;
|
|
|
|
/* MR_deep_copy the exception back to the ordinary heap */
|
|
assert(MR_EXCEPTION_FRAMEVARS->solns_heap_ptr <=
|
|
MR_ENGINE(solutions_heap_zone)->top);
|
|
MR_save_transient_registers();
|
|
exception = MR_deep_copy(&exception,
|
|
(MR_TypeInfo) &mercury_data_std_util__type_ctor_info_univ_0,
|
|
saved_solns_heap_ptr, MR_ENGINE(solutions_heap_zone)->top);
|
|
MR_restore_transient_registers();
|
|
|
|
/* reset the solutions heap */
|
|
assert(MR_EXCEPTION_FRAMEVARS->solns_heap_ptr <= saved_solns_heap_ptr);
|
|
assert(saved_solns_heap_ptr <= MR_sol_hp);
|
|
if (catch_code_model == MR_MODEL_NON_HANDLER) {
|
|
/*
|
|
** If the code inside the try (catch) was nondet,
|
|
** then its caller (which may be solutions/2) may
|
|
** have put some more stuff on the solutions-heap
|
|
** after the goal succeeded; the goal may have
|
|
** only thrown after being re-entered on backtracking.
|
|
** Thus we can only reset the solutions heap to
|
|
** where it was before copying the exception object to it.
|
|
*/
|
|
MR_sol_hp = saved_solns_heap_ptr;
|
|
} else {
|
|
/*
|
|
** If the code inside the try (catch) was det or semidet,
|
|
** we can safely reset the solutions heap to where
|
|
** it was when it try (catch) was entered.
|
|
*/
|
|
MR_sol_hp = MR_EXCEPTION_FRAMEVARS->solns_heap_ptr;
|
|
}
|
|
}
|
|
#endif /* !defined(CONSERVATIVE_GC) */
|
|
|
|
/*
|
|
** Pop the final exception handler frame off the nondet stack,
|
|
** and reset the nondet stack top. (This must be done last,
|
|
** since it invalidates all the framevars.)
|
|
*/
|
|
MR_maxfr = MR_prevfr_slot(MR_curfr);
|
|
MR_curfr = MR_succfr_slot(MR_curfr);
|
|
|
|
/*
|
|
** Now longjmp to the catch, which will invoke the handler
|
|
** that we found.
|
|
*/
|
|
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""throw catch_code_model %d\\n"", catch_code_model);
|
|
#endif
|
|
|
|
if (catch_code_model == MR_C_LONGJMP_HANDLER) {
|
|
#ifdef MR_DEBUG_JMPBUFS
|
|
fprintf(stderr, ""throw longjmp %p\\n"",
|
|
*(MR_ENGINE(e_jmp_buf)));
|
|
#endif
|
|
|
|
MR_ENGINE(e_exception) = (MR_Word *) exception;
|
|
MR_save_registers();
|
|
longjmp(*(MR_ENGINE(e_jmp_buf)), 1);
|
|
}
|
|
|
|
/*
|
|
** Otherwise, the handler is a Mercury closure.
|
|
** Invoke the handler as `Handler(Exception, Result)'.
|
|
*/
|
|
MR_r1 = handler; /* get the Handler closure */
|
|
MR_r2 = 1; /* One additional input argument */
|
|
MR_r3 = 1; /* One output argument */
|
|
MR_r4 = exception; /* This is our one input argument */
|
|
|
|
/*
|
|
** If the catch was semidet, we need to set the success indicator
|
|
** MR_r1 to TRUE and return the result in MR_r2; otherwise, we return
|
|
** the result in MR_r1, which is where mercury__do_call_closure puts
|
|
** it, so we can do a tailcall.
|
|
*/
|
|
if (catch_code_model != MR_MODEL_SEMI_HANDLER) {
|
|
MR_tailcall(MR_ENTRY(mercury__do_call_closure),
|
|
MR_ENTRY(mercury__exception__builtin_throw_1_0));
|
|
}
|
|
MR_incr_sp_push_msg(1, ""builtin_throw/1"");
|
|
MR_stackvar(1) = (MR_Word) MR_succip;
|
|
MR_call(MR_ENTRY(mercury__do_call_closure),
|
|
MR_LABEL(mercury__exception__builtin_throw_1_0_i1),
|
|
MR_ENTRY(mercury__exception__builtin_throw_1_0));
|
|
}
|
|
MR_define_label(mercury__exception__builtin_throw_1_0_i1);
|
|
MR_update_prof_current_proc(
|
|
MR_LABEL(mercury__exception__builtin_throw_1_0));
|
|
/* we've just returned from mercury__do_call_closure */
|
|
MR_r2 = MR_r1;
|
|
MR_r1 = TRUE;
|
|
MR_succip = (MR_Code *) MR_stackvar(1);
|
|
MR_decr_sp_pop_msg(1);
|
|
MR_proceed(); /* return to the caller of `builtin_catch' */
|
|
|
|
MR_END_MODULE
|
|
|
|
|
|
/* Ensure that the initialization code for the above module gets run. */
|
|
/*
|
|
INIT mercury_sys_init_exceptions
|
|
*/
|
|
|
|
/* suppress gcc -Wmissing-decls warning */
|
|
void mercury_sys_init_exceptions(void);
|
|
|
|
void mercury_sys_init_exceptions(void) {
|
|
exceptions_module();
|
|
}
|
|
|
|
#endif /* ! MR_HIGHLEVEL_CODE */
|
|
|
|
").
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- 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__flush_output,
|
|
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)
|
|
),
|
|
io__flush_output(StdErr).
|
|
|
|
%-----------------------------------------------------------------------------%
|