source: doc/proposals/exceptions-pab.md@ cea0d0c

Last change on this file since cea0d0c was cea0d0c, checked in by Peter A. Buhr <pabuhr@…>, 2 months ago

yet another exception proposal

  • Property mode set to 100644
File size: 7.8 KB
Line 
1# Exception Handling Changes
2
3This proposal changes the C&forall; exception handling mechanism from matching an object type to a function type.
4Using functions fits with function overloading in the C&forall; type system.
5
6The following shows a resumption example.
7
8```
9exception int resumpt( int, double ); // return result, no stack unwinding
10try {
11 i = resumpt( 3, 3.5 ); // exception raise
12} catch( int resumpt( int i, double d ) { // i = 3, d = 3.5
13 ... return 42; // return value to raise point
14}
15```
16
17The following shows a termination example.
18
19```
20exception void termin( double ); // void return, stack unwinding
21try {
22 termin( 3.5 ); // exception raise
23} catch( void termin( int i, double d ) { // i = 3, d = 3.5
24 ... // fall through to lexical block
25}
26```
27
28There is no explicit *raise* statement.
29(See Mesa Language Manual page 135, footnote)
30The compiler knows a function is an exception, so it does a search call (propagation) instead of a branch call.
31
32An exception function is denoted by the `exception` keyword to differentiate it from a regular function.
33An exception function is defined in a `catch` clause, which is a local, inline implementation of the exception function.
34The catch clause must specify the exception-function name and type for matching, and arbitrary parameter names to access the raise arguments.
35Data is carried from the raise call to the `catch` body via the argument/parameter mechanism, and data can be returned to the raise call via function return.
36An exception function cannot be passed as a function pointer nor can a raise use a pointer to an exception function.
37
38Overloading of the exception raise occurs among existing exception functions.
39
40```
41exception int fix( int, double );
42exception double fix( int, double );
43exception double fix( double );
44try {
45 int i = fix ( 3, 3.5 ); // choose best overloading
46} catch( int fix( int i, double d ) ) { // match with exact catch body
47} catch( double fix( int i, double d ) ) {
48} catch( double fix( double d ) ) {
49}
50```
51
52Normal overload resolution occur at the raise point, where the best fit *exception* function is chosen based on arguments and left-hand side.
53Matching between raise and catch is exact, as for function pointers, with the selected overload chosen at the raise point (compare mangled names dynamically)
54
55An exception function has a *kind*: return, noreturn, or dual, denoting resumption, termination, or both.
56An exception function cannot be overloaded on the exception kind.
57A return exception function must return a type, a noreturn exception function must return `void`, a dual exception function must return a type.
58A dual exception function is specified using the quasi-keyword `dual`.
59
60```
61exception int dual( int, double ) dual; // Dual, return result or exit handler block, no initial stack unwinding
62```
63
64Within the handler for kind return (stack not unwound), the handler *must* return a value as for any value returning function;
65any other control-flow statement or fallthrough is an error, like `break` or fallthrough on a value returning function.
66
67```
68 } catch( int fix( int i, float j ) ) { // stack not unwound, i and j accessible, must return
69 return 42; // implies return to fix call, not return of nested function
70 break; // syntax error => must return
71 // fallthrough => syntax error
72 }
73```
74
75Within the handler for kind noreturn (stack unwound), `break` or fallthrough &rArr; exit handler, like in the `case` clause of a switch statement;
76any other control-flow statement works on the lexical context outside the handler.
77
78```
79 } catch( void recover( int i ) ) { // stack unwound, cannot return
80 break; // implies exit catch routine
81 return; // implies return from lexical function
82 // fallthrough => implies break
83 }
84```
85
86Within the handler for kind dual (stack not unwound), `break` or fallthrough &rArr; exit handler and unwind stack, or return a value to the raise at top of the stack.
87
88```
89 } catch( int dual( int i, float j ) ) { // stack not unwound, can return
90 return 42; // implies return to fix call, not return of nested function
91 break; // implies exit catch body
92 // fallthrough => implies break
93 }
94```
95
96In all cases, a handler can raise another exception.
97
98An exception function can have a static body, which define the action if the exception is not caught.
99If no body is specified, there are default actions added.
100This matches with `defaultResume` and `defaultTerminate` in current C&forall;/&mu;C++.
101Here a parameter name is necessary to access the raise argument(s).
102
103Resumption default:
104
105```
106exception int resumpt( int i ) { // called if no handler found
107 // return default correction or abort or raise another exception
108 // default if not specified is to call UnhandledException at resumer or joiner.
109}
110```
111
112Termination default:
113
114```
115exception void termin( double d ) { // called if no handler found
116 // abort or raise another exception
117 // default if not specified is to call UnhandledException at resumer or joiner.
118}
119```
120
121Dual default is the same as termination default, as having a default correction action is unlikely.
122
123A `throws` clause can be added to a regular function to indicate alternate outcomes.
124
125```
126int foo(...) throws( int ex( int ), float ex( int ), char ex( double, double ) );
127```
128
129The `throws` clause is *not* part of the function type, and hence, is not used for overloading.
130Functions with a `throws` clause cause are handled by a separate type-checking pass, which examines the statically call structure to determine if calls to `foo` are nested directly or indirectly within guarded blocks with matching catch clauses, e.g.:
131
132```
133int bar( int i ) throws( int fixup( int ) );
134
135void foo(...) {
136 ... i = bar( 3 ); ... // call statically nested within handler for fixup
137}
138void baz() {
139 try {
140 foo(...);
141 } catch( int fixup( int ) ) {
142 ... return 42;
143 }
144}
145```
146
147If this type check fails, a warning is given, and a dynamic check is wrapped around the call to verify only the specified exception functions are raised.
148This dual approach allows all forms of reuse to exist, and is similar to checked/unchecked exceptions in Java.
149
150Resumption example:
151
152```
153exception int fix( int i, float f );
154
155void foo() {
156 try {
157 for () {
158 if ( ... ) x = fix( 3, 5.4 );
159 }
160 } catch( int fix( int i, float j ) ) {
161 ... return 42; // fix up problem and return to raise
162 }
163}
164```
165
166Termination example:
167
168```
169exception void end_of_file( ifstream is );
170
171void foo() {
172 int i;
173 try {
174 for () {
175 sin | i; // internally, does a call end_of_file( is ), which implicitly throws exception
176 sout | i;
177 }
178 } catch( end_of_file( ifstream is ) && is == sin ) { // "is" used in predicate selection
179 // close file
180 }
181}
182```
183
184Dual example:
185
186```
187exception int dual( int i, float f ) dual;
188
189void baz() {
190 try {
191 for () {
192 if ( ... ) x = dual( 3, 5.4 );
193 }
194 } catch( int dual( int i, float j ) ) {
195 if ( ... ) return 42; // return to raise calls
196 if ( ... ) break; // exit catch body
197 // fallthrough // implies break
198 }
199}
200```
201
202I'm not sure this polymorphism is doing anything, except suggesting a common pattern.
203A catch clause cannot be polymorphic because there is no RTTI matching.
204
205```
206forall( T ) exception void arithmetic( T op1, T op2, int retcode ) noreturn;
207
208enum FloatExceptions ! { Invalid, ZeroDiv, Overflow, Underflow, Inexact };
209exception double arithmetic( double op1, double op2, int retcode ) {
210 // default abort
211}
212enum IntExceptions ! { ZeroDiv, Overflow, Underflow };
213exception double arithmetic( int op1, int op2, int retcode ) {
214 // default abort
215}
216
217void xxx() {
218 double x = 3.5;
219 try {
220 x = x / 0.0;
221 } catch( arithmetic( double op1, double op2, int retcode ) ) {
222 if ( retcode == FloatExceptions.ZeroDiv ) ...
223 }
224
225 int i = 3;
226 try {
227 i = i / 0;
228 } catch( arithmetic( int op1, int op2, int retcode ) ) {
229 if ( retcode == IntExceptions.ZeroDiv ) ...
230 }
231}
232```
Note: See TracBrowser for help on using the repository browser.