Changeset 5a40e4e for doc/theses/andrew_beach_MMath/features.tex
- Timestamp:
- Sep 9, 2021, 3:56:32 PM (4 years ago)
- Branches:
- ADT, ast-experimental, enum, forall-pointer-decay, master, pthread-emulation, qualifiedEnum
- Children:
- d0b9247
- Parents:
- dd1cc02 (diff), d8d512e (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)links above to see all the changes relative to each parent. - File:
-
- 1 edited
-
doc/theses/andrew_beach_MMath/features.tex (modified) (34 diffs)
Legend:
- Unmodified
- Added
- Removed
-
doc/theses/andrew_beach_MMath/features.tex
rdd1cc02 r5a40e4e 19 19 20 20 \paragraph{Raise} 21 The raise is the starting point for exception handling 21 The raise is the starting point for exception handling, 22 22 by raising an exception, which passes it to 23 23 the EHM. … … 30 30 \paragraph{Handle} 31 31 The primary purpose of an EHM is to run some user code to handle a raised 32 exception. This code is given, with some other information, in a handler. 32 exception. This code is given, along with some other information, 33 in a handler. 33 34 34 35 A handler has three common features: the previously mentioned user code, a 35 region of code it guards ,and an exception label/condition that matches36 the raised exception.36 region of code it guards and an exception label/condition that matches 37 against the raised exception. 37 38 Only raises inside the guarded region and raising exceptions that match the 38 39 label can be handled by a given handler. … … 41 42 42 43 The @try@ statements of \Cpp, Java and Python are common examples. All three 43 show the common features of guarded region, raise, matching and handler. 44 \begin{cfa} 45 try { // guarded region 46 ... 47 throw exception; // raise 48 ... 49 } catch( exception ) { // matching condition, with exception label 50 ... // handler code 51 } 52 \end{cfa} 44 also show another common feature of handlers, they are grouped by the guarded 45 region. 53 46 54 47 \subsection{Propagation} 55 48 After an exception is raised comes what is usually the biggest step for the 56 EHM: finding and setting up the handler for execution. The propagation from raise to 49 EHM: finding and setting up the handler for execution. 50 The propagation from raise to 57 51 handler can be broken up into three different tasks: searching for a handler, 58 52 matching against the handler and installing the handler. … … 60 54 \paragraph{Searching} 61 55 The EHM begins by searching for handlers that might be used to handle 62 the exception. The search is restricted to63 handlers that have the raise site in their guarded56 the exception. 57 The search will find handlers that have the raise site in their guarded 64 58 region. 65 59 The search includes handlers in the current function, as well as any in … … 67 61 68 62 \paragraph{Matching} 69 Each handler found is matchedwith the raised exception. The exception63 Each handler found is with the raised exception. The exception 70 64 label defines a condition that is used with the exception and decides if 71 65 there is a match or not. 66 % 72 67 In languages where the first match is used, this step is intertwined with 73 68 searching; a match check is performed immediately after the search finds … … 84 79 different course of action for this case. 85 80 This situation only occurs with unchecked exceptions as checked exceptions 86 (such as in Java) are guaranteed to find a matching handler.81 (such as in Java) can make the guarantee. 87 82 The unhandled action is usually very general, such as aborting the program. 88 83 … … 98 93 A handler labeled with any given exception can handle exceptions of that 99 94 type or any child type of that exception. The root of the exception hierarchy 100 (here \code{C}{exception}) acts as a catch-all, leaf types catch single types ,95 (here \code{C}{exception}) acts as a catch-all, leaf types catch single types 101 96 and the exceptions in the middle can be used to catch different groups of 102 97 related exceptions. 103 98 104 99 This system has some notable advantages, such as multiple levels of grouping, 105 the ability for libraries to add new exception types ,and the isolation100 the ability for libraries to add new exception types and the isolation 106 101 between different sub-hierarchies. 107 102 This design is used in \CFA even though it is not a object-orientated … … 123 118 For effective exception handling, additional information is often passed 124 119 from the raise to the handler and back again. 125 So far, only communication of the exception's identity is covered. 126 A common communication method for passing more information is putting fields into the exception instance 120 So far, only communication of the exceptions' identity is covered. 121 A common communication method for adding information to an exception 122 is putting fields into the exception instance 127 123 and giving the handler access to them. 128 Using reference fields pointing to data at the raise location allows data to be 129 passed in both directions. 124 % You can either have pointers/references in the exception, or have p/rs to 125 % the exception when it doesn't have to be copied. 126 Passing references or pointers allows data at the raise location to be 127 updated, passing information in both directions. 130 128 131 129 \section{Virtuals} 130 \label{s:virtuals} 132 131 Virtual types and casts are not part of \CFA's EHM nor are they required for 133 132 an EHM. 134 133 However, one of the best ways to support an exception hierarchy 135 134 is via a virtual hierarchy and dispatch system. 136 137 Ideally, the virtual system should have been part of \CFA before the work 135 Ideally, the virtual system would have been part of \CFA before the work 138 136 on exception handling began, but unfortunately it was not. 139 137 Hence, only the features and framework needed for the EHM were 140 designed and implemented for this thesis. Other features were considered to ensure that 138 designed and implemented for this thesis. 139 Other features were considered to ensure that 141 140 the structure could accommodate other desirable features in the future 142 141 but are not implemented. 143 142 The rest of this section only discusses the implemented subset of the 144 virtual -system design.143 virtual system design. 145 144 146 145 The virtual system supports multiple ``trees" of types. Each tree is … … 149 148 number of children. 150 149 Any type that belongs to any of these trees is called a virtual type. 151 152 150 % A type's ancestors are its parent and its parent's ancestors. 153 151 % The root type has no ancestors. 154 152 % A type's descendants are its children and its children's descendants. 155 153 156 Every virtual type also has a list of virtual members. Children inherit 157 their parent's list of virtual members but may add new members to it. 158 It is important to note that these are virtual members, not virtual methods 159 of object-orientated programming, and can be of any type. 160 161 \PAB{Need to look at these when done. 162 163 \CFA still supports virtual methods as a special case of virtual members. 164 Function pointers that take a pointer to the virtual type are modified 165 with each level of inheritance so that refers to the new type. 166 This means an object can always be passed to a function in its virtual table 167 as if it were a method. 168 \todo{Clarify (with an example) virtual methods.} 169 170 Each virtual type has a unique id. 171 This id and all the virtual members are combined 172 into a virtual table type. Each virtual type has a pointer to a virtual table 173 as a hidden field. 174 \todo{Might need a diagram for virtual structure.} 175 }% 154 For the purposes of illustration, a proposed -- but unimplemented syntax -- 155 will be used. Each virtual type is represented by a trait with an annotation 156 that makes it a virtual type. This annotation is empty for a root type, which 157 creates a new tree: 158 \begin{cfa} 159 trait root_type(T) virtual() {} 160 \end{cfa} 161 The annotation may also refer to any existing virtual type to make this new 162 type a child of that type and part of the same tree. The parent may itself 163 be a child or a root type and may have any number of existing children. 164 165 % OK, for some reason the b and t positioning options are reversed here. 166 \begin{minipage}[b]{0.6\textwidth} 167 \begin{cfa} 168 trait child_a(T) virtual(root_type) {} 169 trait grandchild(T) virtual(child_a) {} 170 trait child_b(T) virtual(root_type) {} 171 \end{cfa} 172 \end{minipage} 173 \begin{minipage}{0.4\textwidth} 174 \begin{center} 175 \input{virtual-tree} 176 \end{center} 177 \end{minipage} 178 179 Every virtual type also has a list of virtual members and a unique id, 180 both are stored in a virtual table. 181 Every instance of a virtual type also has a pointer to a virtual table stored 182 in it, although there is no per-type virtual table as in many other languages. 183 184 The list of virtual members is built up down the tree. Every virtual type 185 inherits the list of virtual members from its parent and may add more 186 virtual members to the end of the list which are passed on to its children. 187 Again, using the unimplemented syntax this might look like: 188 \begin{cfa} 189 trait root_type(T) virtual() { 190 const char * to_string(T const & this); 191 unsigned int size; 192 } 193 194 trait child_type(T) virtual(root_type) { 195 char * irrelevant_function(int, char); 196 } 197 \end{cfa} 198 % Consider adding a diagram, but we might be good with the explanation. 199 200 As @child_type@ is a child of @root_type@ it has the virtual members of 201 @root_type@ (@to_string@ and @size@) as well as the one it declared 202 (@irrelevant_function@). 203 204 It is important to note that these are virtual members, and may contain 205 arbitrary fields, functions or otherwise. 206 The names ``size" and ``align" are reserved for the size and alignment of the 207 virtual type, and are always automatically initialized as such. 208 The other special case are uses of the trait's polymorphic argument 209 (@T@ in the example), which are always updated to refer to the current 210 virtual type. This allows functions that refer to to polymorphic argument 211 to act as traditional virtual methods (@to_string@ in the example), as the 212 object can always be passed to a virtual method in its virtual table. 176 213 177 214 Up until this point the virtual system is similar to ones found in 178 object-orientated languages but this is where \CFA diverges. Objects encapsulate a 179 single set of methods in each type, universally across the entire program, 180 and indeed all programs that use that type definition. Even if a type inherits and adds methods, it still encapsulate a 181 single set of methods. In this sense, 182 object-oriented types are ``closed" and cannot be altered. 183 184 In \CFA, types do not encapsulate any code. Traits are local for each function and 185 types can satisfy a local trait, stop satisfying it or, satisfy the same 186 trait in a different way at any lexical location in the program where a function is call. 187 In this sense, the set of functions/variables that satisfy a trait for a type is ``open" as the set can change at every call site. 215 object-oriented languages but this is where \CFA diverges. 216 Objects encapsulate a single set of methods in each type, 217 universally across the entire program, 218 and indeed all programs that use that type definition. 219 The only way to change any method is to inherit and define a new type with 220 its own universal implementation. In this sense, 221 these object-oriented types are ``closed" and cannot be altered. 222 % Because really they are class oriented. 223 224 In \CFA, types do not encapsulate any code. 225 Whether or not satisfies any given assertion, and hence any trait, is 226 context sensitive. Types can begin to satisfy a trait, stop satisfying it or 227 satisfy the same trait at any lexical location in the program. 228 In this sense, an type's implementation in the set of functions and variables 229 that allow it to satisfy a trait is ``open" and can change 230 throughout the program. 188 231 This capability means it is impossible to pick a single set of functions 189 232 that represent a type's implementation across a program. … … 192 235 type. A user can define virtual tables that are filled in at their 193 236 declaration and given a name. Anywhere that name is visible, even if it is 194 defined locally inside a function \PAB{What does this mean? (although that means it does not have a195 static lifetime)}, it can be used.237 defined locally inside a function (although in this case the user must ensure 238 it outlives any objects that use it), it can be used. 196 239 Specifically, a virtual type is ``bound" to a virtual table that 197 240 sets the virtual members for that object. The virtual members can be accessed 198 241 through the object. 242 243 This means virtual tables are declared and named in \CFA. 244 They are declared as variables, using the type 245 @vtable(VIRTUAL_TYPE)@ and any valid name. For example: 246 \begin{cfa} 247 vtable(virtual_type_name) table_name; 248 \end{cfa} 249 250 Like any variable they may be forward declared with the @extern@ keyword. 251 Forward declaring virtual tables is relatively common. 252 Many virtual types have an ``obvious" implementation that works in most 253 cases. 254 A pattern that has appeared in the early work using virtuals is to 255 implement a virtual table with the the obvious definition and place a forward 256 declaration of it in the header beside the definition of the virtual type. 257 258 Even on the full declaration, no initializer should be used. 259 Initialization is automatic. 260 The type id and special virtual members ``size" and ``align" only depend on 261 the virtual type, which is fixed given the type of the virtual table and 262 so the compiler fills in a fixed value. 263 The other virtual members are resolved, using the best match to the member's 264 name and type, in the same context as the virtual table is declared using 265 \CFA's normal resolution rules. 199 266 200 267 While much of the virtual infrastructure is created, it is currently only used … … 212 279 @EXPRESSION@ object, otherwise it returns @0p@ (null pointer). 213 280 214 \section{Exception} 215 % Leaving until later, hopefully it can talk about actual syntax instead 216 % of my many strange macros. Syntax aside I will also have to talk about the 217 % features all exceptions support. 218 219 Exceptions are defined by the trait system; there are a series of traits, and 220 if a type satisfies them, then it can be used as an exception. The following 281 \section{Exceptions} 282 283 The syntax for declaring an exception is the same as declaring a structure 284 except the keyword that is swapped out: 285 \begin{cfa} 286 exception TYPE_NAME { 287 FIELDS 288 }; 289 \end{cfa} 290 291 Fields are filled in the same way as a structure as well. However an extra 292 field is added that contains the pointer to the virtual table. 293 It must be explicitly initialized by the user when the exception is 294 constructed. 295 296 Here is an example of declaring an exception type along with a virtual table, 297 assuming the exception has an ``obvious" implementation and a default 298 virtual table makes sense. 299 300 \begin{minipage}[t]{0.4\textwidth} 301 Header: 302 \begin{cfa} 303 exception Example { 304 int data; 305 }; 306 307 extern vtable(Example) 308 example_base_vtable; 309 \end{cfa} 310 \end{minipage} 311 \begin{minipage}[t]{0.6\textwidth} 312 Source: 313 \begin{cfa} 314 vtable(Example) example_base_vtable 315 \end{cfa} 316 \vfil 317 \end{minipage} 318 319 %\subsection{Exception Details} 320 This is the only interface needed when raising and handling exceptions. 321 However it is actually a short hand for a more complex 322 trait based interface. 323 324 The language views exceptions through a series of traits. 325 If a type satisfies them, then it can be used as an exception. The following 221 326 is the base trait all exceptions need to match. 222 327 \begin{cfa} … … 225 330 }; 226 331 \end{cfa} 227 The trait is defined over two types ,the exception type and the virtual table332 The trait is defined over two types: the exception type and the virtual table 228 333 type. Each exception type should have a single virtual table type. 229 334 There are no actual assertions in this trait because the trait system … … 231 336 completing the virtual system). The imaginary assertions would probably come 232 337 from a trait defined by the virtual system, and state that the exception type 233 is a virtual type, is a descendant of @exception_t@ (the base exception type) ,234 and note itsvirtual table type.338 is a virtual type, is a descendant of @exception_t@ (the base exception type) 339 and allow the user to find the virtual table type. 235 340 236 341 % I did have a note about how it is the programmer's responsibility to make … … 250 355 }; 251 356 \end{cfa} 252 Both traits ensure a pair of types arean exception type, its virtual table253 type ,357 Both traits ensure a pair of types is an exception type, its virtual table 358 type 254 359 and defines one of the two default handlers. The default handlers are used 255 360 as fallbacks and are discussed in detail in \vref{s:ExceptionHandling}. … … 260 365 facing way. So these three macros are provided to wrap these traits to 261 366 simplify referring to the names: 262 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ ,and @IS_RESUMPTION_EXCEPTION@.367 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@. 263 368 264 369 All three take one or two arguments. The first argument is the name of the … … 283 388 These twin operations are the core of \CFA's exception handling mechanism. 284 389 This section covers the general patterns shared by the two operations and 285 then goes on to cover the details ofeach individual operation.390 then goes on to cover the details each individual operation. 286 391 287 392 Both operations follow the same set of steps. 288 393 First, a user raises an exception. 289 Second, the exception propagates up the stack .394 Second, the exception propagates up the stack, searching for a handler. 290 395 Third, if a handler is found, the exception is caught and the handler is run. 291 396 After that control continues at a raise-dependent location. 292 Fourth, if a handler is not found, a default handler is run and, if it returns, then control 397 As an alternate to the third step, 398 if a handler is not found, a default handler is run and, if it returns, 399 then control 293 400 continues after the raise. 294 401 295 %This general description covers what the two kinds have in common. 296 The differences in the two operations include how propagation is performed, where execution continues 297 a fter an exception is caught and handled, and which default handler is run.402 The differences between the two operations include how propagation is 403 performed, where execution continues after an exception is handled 404 and which default handler is run. 298 405 299 406 \subsection{Termination} 300 407 \label{s:Termination} 301 Termination handling is the familiar EHM and used in most programming 408 Termination handling is the familiar kind of handling 409 and used in most programming 302 410 languages with exception handling. 303 411 It is a dynamic, non-local goto. If the raised exception is matched and … … 331 439 Then propagation starts with the search. \CFA uses a ``first match" rule so 332 440 matching is performed with the copied exception as the search key. 333 It starts from the raise in the throwing function and proceeds towards thebase of the stack,441 It starts from the raise site and proceeds towards base of the stack, 334 442 from callee to caller. 335 443 At each stack frame, a check is made for termination handlers defined by the … … 345 453 \end{cfa} 346 454 When viewed on its own, a try statement simply executes the statements 347 in the \snake{GUARDED_BLOCK} ,and when those are finished,455 in the \snake{GUARDED_BLOCK} and when those are finished, 348 456 the try statement finishes. 349 457 … … 371 479 termination exception types. 372 480 The global default termination handler performs a cancellation 373 (see \vref{s:Cancellation} for the justification) on the current stack with the copied exception. 374 Since it is so general, a more specific handler is usually 375 defined, possibly with a detailed message, and used for specific exception type, effectively overriding the default handler. 481 (as described in \vref{s:Cancellation}) 482 on the current stack with the copied exception. 483 Since it is so general, a more specific handler can be defined, 484 overriding the default behaviour for the specific exception types. 376 485 377 486 \subsection{Resumption} 378 487 \label{s:Resumption} 379 488 380 Resumption exception handling is the less familar EHM, but is 489 Resumption exception handling is less familar form of exception handling, 490 but is 381 491 just as old~\cite{Goodenough75} and is simpler in many ways. 382 492 It is a dynamic, non-local function call. If the raised exception is … … 387 497 function once the error is corrected, and 388 498 ignorable events, such as logging where nothing needs to happen and control 389 should always continue from the raise point. 499 should always continue from the raise site. 500 501 Except for the changes to fit into that pattern, resumption exception 502 handling is symmetric with termination exception handling, by design 503 (see \autoref{s:Termination}). 390 504 391 505 A resumption raise is started with the @throwResume@ statement: … … 393 507 throwResume EXPRESSION; 394 508 \end{cfa} 395 \todo{Decide on a final set of keywords and use them everywhere.} 396 It works much the same way as the termination throw. 397 The expression must return a reference to a resumption exception, 398 where the resumption exception is any type that satisfies the trait 399 @is_resumption_exception@ at the call site. 400 The assertions from this trait are available to 401 the exception system while handling the exception. 402 403 At run-time, no exception copy is made, since 509 % The new keywords are currently ``experimental" and not used in this work. 510 It works much the same way as the termination raise, except the 511 type must satisfy the \snake{is_resumption_exception} that uses the 512 default handler: \defaultResumptionHandler. 513 This can be specialized for particular exception types. 514 515 At run-time, no exception copy is made. Since 404 516 resumption does not unwind the stack nor otherwise remove values from the 405 current scope, so there is no need to manage memory to keep the exception in scope.406 407 Then propagation starts with the search. It starts from the raise in the 408 resuming function and proceeds towards the base of the stack,409 f rom callee to caller.410 At each stack frame, a check is made for resumption handlers defined by the 411 @catchResume@ clauses of a @try@ statement.517 current scope, there is no need to manage memory to keep the exception 518 allocated. 519 520 Then propagation starts with the search, 521 following the same search path as termination, 522 from the raise site to the base of stack and top of try statement to bottom. 523 However, the handlers on try statements are defined by @catchResume@ clauses. 412 524 \begin{cfa} 413 525 try { … … 419 531 } 420 532 \end{cfa} 421 % PAB, you say this above. 422 % When a try statement is executed, it simply executes the statements in the 423 % @GUARDED_BLOCK@ and then finishes. 424 % 425 % However, while the guarded statements are being executed, including any 426 % invoked functions, all the handlers in these statements are included in the 427 % search path. 428 % Hence, if a resumption exception is raised, these handlers may be matched 429 % against the exception and may handle it. 430 % 431 % Exception matching checks the handler in each catch clause in the order 432 % they appear, top to bottom. If the representation of the raised exception type 433 % is the same or a descendant of @EXCEPTION_TYPE@$_i$, then @NAME@$_i$ 434 % (if provided) is bound to a pointer to the exception and the statements in 435 % @HANDLER_BLOCK@$_i$ are executed. 436 % If control reaches the end of the handler, execution continues after the 437 % the raise statement that raised the handled exception. 438 % 439 % Like termination, if no resumption handler is found during the search, 440 % then the default handler (\defaultResumptionHandler) visible at the raise 441 % statement is called. It will use the best match at the raise sight according 442 % to \CFA's overloading rules. The default handler is 443 % passed the exception given to the raise. When the default handler finishes 444 % execution continues after the raise statement. 445 % 446 % There is a global @defaultResumptionHandler{} is polymorphic over all 447 % resumption exceptions and performs a termination throw on the exception. 448 % The \defaultTerminationHandler{} can be overridden by providing a new 449 % function that is a better match. 450 451 The @GUARDED_BLOCK@ and its associated nested guarded statements work the same 452 for resumption as for termination, as does exception matching at each 453 @catchResume@. Similarly, if no resumption handler is found during the search, 454 then the currently visible default handler (\defaultResumptionHandler) is 455 called and control continues after the raise statement if it returns. Finally, 456 there is also a global @defaultResumptionHandler@, which can be overridden, 457 that is polymorphic over all resumption exceptions but performs a termination 458 throw on the exception rather than a cancellation. 459 460 Throwing the exception in @defaultResumptionHandler@ has the positive effect of 461 walking the stack a second time for a recovery handler. Hence, a programmer has 462 two chances for help with a problem, fixup or recovery, should either kind of 463 handler appear on the stack. However, this dual stack walk leads to following 464 apparent anomaly: 465 \begin{cfa} 466 try { 467 throwResume E; 468 } catch (E) { 469 // this handler runs 470 } 471 \end{cfa} 472 because the @catch@ appears to handle a @throwResume@, but a @throwResume@ only 473 matches with @catchResume@. The anomaly results because the unmatched 474 @catchResuem@, calls @defaultResumptionHandler@, which in turn throws @E@. 475 476 % I wonder if there would be some good central place for this. 477 Note, termination and resumption handlers may be used together 533 Note that termination handlers and resumption handlers may be used together 478 534 in a single try statement, intermixing @catch@ and @catchResume@ freely. 479 535 Each type of handler only interacts with exceptions from the matching 480 536 kind of raise. 537 Like @catch@ clauses, @catchResume@ clauses have no effect if an exception 538 is not raised. 539 540 The matching rules are exactly the same as well. 541 The first major difference here is that after 542 @EXCEPTION_TYPE@$_i$ is matched and @NAME@$_i$ is bound to the exception, 543 @HANDLER_BLOCK@$_i$ is executed right away without first unwinding the stack. 544 After the block has finished running control jumps to the raise site, where 545 the just handled exception came from, and continues executing after it, 546 not after the try statement. 481 547 482 548 \subsubsection{Resumption Marking} … … 486 552 and run, its try block (the guarded statements) and every try statement 487 553 searched before it are still on the stack. There presence can lead to 488 the \emph{recursive resumption problem}. 554 the recursive resumption problem.\cite{Buhr00a} 555 % Other possible citation is MacLaren77, but the form is different. 489 556 490 557 The recursive resumption problem is any situation where a resumption handler … … 500 567 When this code is executed, the guarded @throwResume@ starts a 501 568 search and matches the handler in the @catchResume@ clause. This 502 call is placed on the stack above the try-block. Now the second raise in the handler 503 searches the same try block, matches, and puts another instance of the 569 call is placed on the stack above the try-block. 570 Now the second raise in the handler searches the same try block, 571 matches again and then puts another instance of the 504 572 same handler on the stack leading to infinite recursion. 505 573 506 While this situation is trivial and easy to avoid, much more complex cycles can 507 form with multiple handlers and different exception types. The key point is 508 that the programmer's intuition expects every raise in a handler to start 509 searching \emph{below} the @try@ statement, making it difficult to understand 510 and fix the problem. 511 574 While this situation is trivial and easy to avoid, much more complex cycles 575 can form with multiple handlers and different exception types. 512 576 To prevent all of these cases, each try statement is ``marked" from the 513 time the exception search reaches it to either when a matching handler514 completesor when the search reaches the base577 time the exception search reaches it to either when a handler completes 578 handling that exception or when the search reaches the base 515 579 of the stack. 516 580 While a try statement is marked, its handlers are never matched, effectively … … 524 588 for instance, marking just the handlers that caught the exception, 525 589 would also prevent recursive resumption. 526 However, the rule selected mirrors what happens with termination,527 and hence, matches programmer intuition that a raise searches below a try.528 529 In detail, the marked try statements are the ones that would be removed from590 However, the rules selected mirrors what happens with termination, 591 so this reduces the amount of rules and patterns a programmer has to know. 592 593 The marked try statements are the ones that would be removed from 530 594 the stack for a termination exception, \ie those on the stack 531 595 between the handler and the raise statement. … … 593 657 594 658 \subsection{Comparison with Reraising} 595 Without conditional catch, the only approach to match in more detail is to reraise 596 the exception after it has been caught, if it could not be handled. 659 In languages without conditional catch, that is no ability to match an 660 exception based on something other than its type, it can be mimicked 661 by matching all exceptions of the right type, checking any additional 662 conditions inside the handler and re-raising the exception if it does not 663 match those. 664 665 Here is a minimal example comparing both patterns, using @throw;@ 666 (no argument) to start a re-raise. 597 667 \begin{center} 598 \begin{tabular}{l |l}668 \begin{tabular}{l r} 599 669 \begin{cfa} 600 670 try { 601 do_work_may_throw();602 } catch(excep _t * ex; can_handle(ex)) {603 604 handle(ex);605 606 607 608 } 671 do_work_may_throw(); 672 } catch(exception_t * exc ; 673 can_handle(exc)) { 674 handle(exc); 675 } 676 677 678 609 679 \end{cfa} 610 680 & 611 681 \begin{cfa} 612 682 try { 613 do_work_may_throw(); 614 } catch(excep_t * ex) { 615 if (can_handle(ex)) { 616 handle(ex); 683 do_work_may_throw(); 684 } catch(exception_t * exc) { 685 if (can_handle(exc)) { 686 handle(exc); 687 } else { 688 throw; 689 } 690 } 691 \end{cfa} 692 \end{tabular} 693 \end{center} 694 At first glance catch-and-reraise may appear to just be a quality of life 695 feature, but there are some significant differences between the two 696 stratagies. 697 698 A simple difference that is more important for \CFA than many other languages 699 is that the raise site changes, with a re-raise but does not with a 700 conditional catch. 701 This is important in \CFA because control returns to the raise site to run 702 the per-site default handler. Because of this only a conditional catch can 703 allow the original raise to continue. 704 705 The more complex issue comes from the difference in how conditional 706 catches and re-raises handle multiple handlers attached to a single try 707 statement. A conditional catch will continue checking later handlers while 708 a re-raise will skip them. 709 If the different handlers could handle some of the same exceptions, 710 translating a try statement that uses one to use the other can quickly 711 become non-trivial: 712 713 \noindent 714 Original, with conditional catch: 715 \begin{cfa} 716 ... 717 } catch (an_exception * e ; check_a(e)) { 718 handle_a(e); 719 } catch (exception_t * e ; check_b(e)) { 720 handle_b(e); 721 } 722 \end{cfa} 723 Translated, with re-raise: 724 \begin{cfa} 725 ... 726 } catch (exception_t * e) { 727 an_exception * an_e = (virtual an_exception *)e; 728 if (an_e && check_a(an_e)) { 729 handle_a(an_e); 730 } else if (check_b(e)) { 731 handle_b(e); 617 732 } else { 618 733 throw; … … 620 735 } 621 736 \end{cfa} 622 \end{tabular} 623 \end{center} 624 Notice catch-and-reraise increases complexity by adding additional data and 625 code to the exception process. Nevertheless, catch-and-reraise can simulate 626 conditional catch straightforwardly, when exceptions are disjoint, \ie no 627 inheritance. 628 629 However, catch-and-reraise simulation becomes unusable for exception inheritance. 630 \begin{flushleft} 631 \begin{cfa}[xleftmargin=6pt] 632 exception E1; 633 exception E2(E1); // inheritance 634 \end{cfa} 635 \begin{tabular}{l|l} 636 \begin{cfa} 637 try { 638 ... foo(); ... // raise E1/E2 639 ... bar(); ... // raise E1/E2 640 } catch( E2 e; e.rtn == foo ) { 641 ... 642 } catch( E1 e; e.rtn == foo ) { 643 ... 644 } catch( E1 e; e.rtn == bar ) { 645 ... 646 } 647 648 \end{cfa} 649 & 650 \begin{cfa} 651 try { 652 ... foo(); ... 653 ... bar(); ... 654 } catch( E2 e ) { 655 if ( e.rtn == foo ) { ... 656 } else throw; // reraise 657 } catch( E1 e ) { 658 if (e.rtn == foo) { ... 659 } else if (e.rtn == bar) { ... 660 else throw; // reraise 661 } 662 \end{cfa} 663 \end{tabular} 664 \end{flushleft} 665 The derived exception @E2@ must be ordered first in the catch list, otherwise 666 the base exception @E1@ catches both exceptions. In the catch-and-reraise code 667 (right), the @E2@ handler catches exceptions from both @foo@ and 668 @bar@. However, the reraise misses the following catch clause. To fix this 669 problem, an enclosing @try@ statement is need to catch @E2@ for @bar@ from the 670 reraise, and its handler must duplicate the inner handler code for @bar@. To 671 generalize, this fix for any amount of inheritance and complexity of try 672 statement requires a technique called \emph{try-block 673 splitting}~\cite{Krischer02}, which is not discussed in this thesis. It is 674 sufficient to state that conditional catch is more expressive than 675 catch-and-reraise in terms of complexity. 676 677 \begin{comment} 678 That is, they have the same behaviour in isolation. 679 Two things can expose differences between these cases. 680 681 One is the existence of multiple handlers on a single try statement. 682 A reraise skips all later handlers for a try statement but a conditional 683 catch does not. 684 % Hence, if an earlier handler contains a reraise later handlers are 685 % implicitly skipped, with a conditional catch they are not. 686 Still, they are equivalently powerful, 687 both can be used two mimic the behaviour of the other, 688 as reraise can pack arbitrary code in the handler and conditional catches 689 can put arbitrary code in the predicate. 690 % I was struggling with a long explanation about some simple solutions, 691 % like repeating a condition on later handlers, and the general solution of 692 % merging everything together. I don't think it is useful though unless its 693 % for a proof. 694 % https://en.cppreference.com/w/cpp/language/throw 695 696 The question then becomes ``Which is a better default?" 697 We believe that not skipping possibly useful handlers is a better default. 698 If a handler can handle an exception it should and if the handler can not 699 handle the exception then it is probably safer to have that explicitly 700 described in the handler itself instead of implicitly described by its 701 ordering with other handlers. 702 % Or you could just alter the semantics of the throw statement. The handler 703 % index is in the exception so you could use it to know where to start 704 % searching from in the current try statement. 705 % No place for the `goto else;` metaphor. 706 707 The other issue is all of the discussion above assumes that the only 708 way to tell apart two raises is the exception being raised and the remaining 709 search path. 710 This is not true generally, the current state of the stack can matter in 711 a number of cases, even only for a stack trace after an program abort. 712 But \CFA has a much more significant need of the rest of the stack, the 713 default handlers for both termination and resumption. 714 715 % For resumption it turns out it is possible continue a raise after the 716 % exception has been caught, as if it hadn't been caught in the first place. 717 This becomes a problem combined with the stack unwinding used in termination 718 exception handling. 719 The stack is unwound before the handler is installed, and hence before any 720 reraises can run. So if a reraise happens the previous stack is gone, 721 the place on the stack where the default handler was supposed to run is gone, 722 if the default handler was a local function it may have been unwound too. 723 There is no reasonable way to restore that information, so the reraise has 724 to be considered as a new raise. 725 This is the strongest advantage conditional catches have over reraising, 726 they happen before stack unwinding and avoid this problem. 727 728 % The one possible disadvantage of conditional catch is that it runs user 729 % code during the exception search. While this is a new place that user code 730 % can be run destructors and finally clauses are already run during the stack 731 % unwinding. 737 (There is a simpler solution if @handle_a@ never raises exceptions, 738 using nested try statements.) 739 740 % } catch (an_exception * e ; check_a(e)) { 741 % handle_a(e); 742 % } catch (exception_t * e ; !(virtual an_exception *)e && check_b(e)) { 743 % handle_b(e); 744 % } 732 745 % 733 % https://www.cplusplus.com/reference/exception/current_exception/ 734 % `exception_ptr current_exception() noexcept;` 735 % https://www.python.org/dev/peps/pep-0343/ 736 \end{comment} 746 % } catch (an_exception * e) 747 % if (check_a(e)) { 748 % handle_a(e); 749 % } else throw; 750 % } catch (exception_t * e) 751 % if (check_b(e)) { 752 % handle_b(e); 753 % } else throw; 754 % } 755 In similar simple examples translating from re-raise to conditional catch 756 takes less code but it does not have a general trivial solution either. 757 758 So, given that the two patterns do not trivially translate into each other, 759 it becomes a matter of which on should be encouraged and made the default. 760 From the premise that if a handler that could handle an exception then it 761 should, it follows that checking as many handlers as possible is preferred. 762 So conditional catch and checking later handlers is a good default. 737 763 738 764 \section{Finally Clauses} … … 750 776 The @FINALLY_BLOCK@ is executed when the try statement is removed from the 751 777 stack, including when the @GUARDED_BLOCK@ finishes, any termination handler 752 finishes ,or during an unwind.778 finishes or during an unwind. 753 779 The only time the block is not executed is if the program is exited before 754 780 the stack is unwound. … … 770 796 they have their own strengths, similar to top-level function and lambda 771 797 functions with closures. 772 Destructors take more work for their creation, but if there is clean-up code798 Destructors take more work to create, but if there is clean-up code 773 799 that needs to be run every time a type is used, they are much easier 774 to set-up .800 to set-up for each use. % It's automatic. 775 801 On the other hand finally clauses capture the local context, so is easy to 776 802 use when the clean-up is not dependent on the type of a variable or requires … … 788 814 raise, this exception is not used in matching only to pass information about 789 815 the cause of the cancellation. 790 Final y, since a cancellation only unwinds and forwards, there is no default handler.816 Finally, as no handler is provided, there is no default handler. 791 817 792 818 After @cancel_stack@ is called the exception is copied into the EHM's memory … … 799 825 After the main stack is unwound there is a program-level abort. 800 826 801 The reasons for this semantics in a sequential program is that there is no more code to execute.802 This semantics also applies to concurrent programs, too, even if threads are running.803 That is, if any threads starts a cancellation, it implies all threads terminate. 804 Keeping the same behaviour in sequential and concurrent programs is simple.805 Also, even in concurrent programs there may not currently be any other stacks 806 and even if other stacks do exist, main has no way to know where they are.827 The first reason for this behaviour is for sequential programs where there 828 is only one stack, and hence to stack to pass information to. 829 Second, even in concurrent programs, the main stack has no dependency 830 on another stack and no reliable way to find another living stack. 831 Finally, keeping the same behaviour in both sequential and concurrent 832 programs is simple and easy to understand. 807 833 808 834 \paragraph{Thread Stack} … … 834 860 835 861 With explicit join and a default handler that triggers a cancellation, it is 836 possible to cascade an error across any number of threads, cleaning up each 862 possible to cascade an error across any number of threads, 863 alternating between the resumption (possibly termination) and cancellation, 864 cleaning up each 837 865 in turn, until the error is handled or the main thread is reached. 838 866 … … 847 875 caller's context and passes it to the internal report. 848 876 849 A coroutine only knows of two other coroutines, its starter and its last resumer. 877 A coroutine only knows of two other coroutines, 878 its starter and its last resumer. 850 879 The starter has a much more distant connection, while the last resumer just 851 880 (in terms of coroutine state) called resume on this coroutine, so the message … … 853 882 854 883 With a default handler that triggers a cancellation, it is possible to 855 cascade an error across any number of coroutines, cleaning up each in turn, 884 cascade an error across any number of coroutines, 885 alternating between the resumption (possibly termination) and cancellation, 886 cleaning up each in turn, 856 887 until the error is handled or a thread stack is reached. 857 858 \PAB{Part of this I do not understand. A cancellation cannot be caught. But you859 talk about handling a cancellation in the last sentence. Which is correct?}
Note:
See TracChangeset
for help on using the changeset viewer.