Ignore:
Timestamp:
Sep 9, 2021, 3:56:32 PM (4 years ago)
Author:
Thierry Delisle <tdelisle@…>
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.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

File:
1 edited

Legend:

Unmodified
Added
Removed
  • doc/theses/andrew_beach_MMath/features.tex

    rdd1cc02 r5a40e4e  
    1919
    2020\paragraph{Raise}
    21 The raise is the starting point for exception handling
     21The raise is the starting point for exception handling,
    2222by raising an exception, which passes it to
    2323the EHM.
     
    3030\paragraph{Handle}
    3131The 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.
     32exception. This code is given, along with some other information,
     33in a handler.
    3334
    3435A handler has three common features: the previously mentioned user code, a
    35 region of code it guards, and an exception label/condition that matches
    36 the raised exception.
     36region of code it guards and an exception label/condition that matches
     37against the raised exception.
    3738Only raises inside the guarded region and raising exceptions that match the
    3839label can be handled by a given handler.
     
    4142
    4243The @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}
     44also show another common feature of handlers, they are grouped by the guarded
     45region.
    5346
    5447\subsection{Propagation}
    5548After 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
     49EHM: finding and setting up the handler for execution.
     50The propagation from raise to
    5751handler can be broken up into three different tasks: searching for a handler,
    5852matching against the handler and installing the handler.
     
    6054\paragraph{Searching}
    6155The EHM begins by searching for handlers that might be used to handle
    62 the exception. The search is restricted to
    63 handlers that have the raise site in their guarded
     56the exception.
     57The search will find handlers that have the raise site in their guarded
    6458region.
    6559The search includes handlers in the current function, as well as any in
     
    6761
    6862\paragraph{Matching}
    69 Each handler found is matched with the raised exception. The exception
     63Each handler found is with the raised exception. The exception
    7064label defines a condition that is used with the exception and decides if
    7165there is a match or not.
     66%
    7267In languages where the first match is used, this step is intertwined with
    7368searching; a match check is performed immediately after the search finds
     
    8479different course of action for this case.
    8580This 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.
    8782The unhandled action is usually very general, such as aborting the program.
    8883
     
    9893A handler labeled with any given exception can handle exceptions of that
    9994type 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
    10196and the exceptions in the middle can be used to catch different groups of
    10297related exceptions.
    10398
    10499This system has some notable advantages, such as multiple levels of grouping,
    105 the ability for libraries to add new exception types, and the isolation
     100the ability for libraries to add new exception types and the isolation
    106101between different sub-hierarchies.
    107102This design is used in \CFA even though it is not a object-orientated
     
    123118For effective exception handling, additional information is often passed
    124119from 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
     120So far, only communication of the exceptions' identity is covered.
     121A common communication method for adding information to an exception
     122is putting fields into the exception instance
    127123and 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.
     126Passing references or pointers allows data at the raise location to be
     127updated, passing information in both directions.
    130128
    131129\section{Virtuals}
     130\label{s:virtuals}
    132131Virtual types and casts are not part of \CFA's EHM nor are they required for
    133132an EHM.
    134133However, one of the best ways to support an exception hierarchy
    135134is via a virtual hierarchy and dispatch system.
    136 
    137 Ideally, the virtual system should have been part of \CFA before the work
     135Ideally, the virtual system would have been part of \CFA before the work
    138136on exception handling began, but unfortunately it was not.
    139137Hence, only the features and framework needed for the EHM were
    140 designed and implemented for this thesis. Other features were considered to ensure that
     138designed and implemented for this thesis.
     139Other features were considered to ensure that
    141140the structure could accommodate other desirable features in the future
    142141but are not implemented.
    143142The rest of this section only discusses the implemented subset of the
    144 virtual-system design.
     143virtual system design.
    145144
    146145The virtual system supports multiple ``trees" of types. Each tree is
     
    149148number of children.
    150149Any type that belongs to any of these trees is called a virtual type.
    151 
    152150% A type's ancestors are its parent and its parent's ancestors.
    153151% The root type has no ancestors.
    154152% A type's descendants are its children and its children's descendants.
    155153
    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 }%
     154For the purposes of illustration, a proposed -- but unimplemented syntax --
     155will be used. Each virtual type is represented by a trait with an annotation
     156that makes it a virtual type. This annotation is empty for a root type, which
     157creates a new tree:
     158\begin{cfa}
     159trait root_type(T) virtual() {}
     160\end{cfa}
     161The annotation may also refer to any existing virtual type to make this new
     162type a child of that type and part of the same tree. The parent may itself
     163be 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}
     168trait child_a(T) virtual(root_type) {}
     169trait grandchild(T) virtual(child_a) {}
     170trait 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
     179Every virtual type also has a list of virtual members and a unique id,
     180both are stored in a virtual table.
     181Every instance of a virtual type also has a pointer to a virtual table stored
     182in it, although there is no per-type virtual table as in many other languages.
     183
     184The list of virtual members is built up down the tree. Every virtual type
     185inherits the list of virtual members from its parent and may add more
     186virtual members to the end of the list which are passed on to its children.
     187Again, using the unimplemented syntax this might look like:
     188\begin{cfa}
     189trait root_type(T) virtual() {
     190        const char * to_string(T const & this);
     191        unsigned int size;
     192}
     193
     194trait 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
     200As @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
     204It is important to note that these are virtual members, and may contain   
     205arbitrary fields, functions or otherwise.
     206The names ``size" and ``align" are reserved for the size and alignment of the
     207virtual type, and are always automatically initialized as such.
     208The 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
     210virtual type. This allows functions that refer to to polymorphic argument
     211to act as traditional virtual methods (@to_string@ in the example), as the
     212object can always be passed to a virtual method in its virtual table.
    176213
    177214Up 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.
     215object-oriented languages but this is where \CFA diverges.
     216Objects encapsulate a single set of methods in each type,
     217universally across the entire program,
     218and indeed all programs that use that type definition.
     219The only way to change any method is to inherit and define a new type with
     220its own universal implementation. In this sense,
     221these object-oriented types are ``closed" and cannot be altered.
     222% Because really they are class oriented.
     223
     224In \CFA, types do not encapsulate any code.
     225Whether or not satisfies any given assertion, and hence any trait, is
     226context sensitive. Types can begin to satisfy a trait, stop satisfying it or
     227satisfy the same trait at any lexical location in the program.
     228In this sense, an type's implementation in the set of functions and variables
     229that allow it to satisfy a trait is ``open" and can change
     230throughout the program.
    188231This capability means it is impossible to pick a single set of functions
    189232that represent a type's implementation across a program.
     
    192235type. A user can define virtual tables that are filled in at their
    193236declaration 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 a
    195 static lifetime)}, it can be used.
     237defined locally inside a function (although in this case the user must ensure
     238it outlives any objects that use it), it can be used.
    196239Specifically, a virtual type is ``bound" to a virtual table that
    197240sets the virtual members for that object. The virtual members can be accessed
    198241through the object.
     242
     243This means virtual tables are declared and named in \CFA.
     244They are declared as variables, using the type
     245@vtable(VIRTUAL_TYPE)@ and any valid name. For example:
     246\begin{cfa}
     247vtable(virtual_type_name) table_name;
     248\end{cfa}
     249
     250Like any variable they may be forward declared with the @extern@ keyword.
     251Forward declaring virtual tables is relatively common.
     252Many virtual types have an ``obvious" implementation that works in most
     253cases.
     254A pattern that has appeared in the early work using virtuals is to
     255implement a virtual table with the the obvious definition and place a forward
     256declaration of it in the header beside the definition of the virtual type.
     257
     258Even on the full declaration, no initializer should be used.
     259Initialization is automatic.
     260The type id and special virtual members ``size" and ``align" only depend on
     261the virtual type, which is fixed given the type of the virtual table and
     262so the compiler fills in a fixed value.
     263The other virtual members are resolved, using the best match to the member's
     264name and type, in the same context as the virtual table is declared using
     265\CFA's normal resolution rules.
    199266
    200267While much of the virtual infrastructure is created, it is currently only used
     
    212279@EXPRESSION@ object, otherwise it returns @0p@ (null pointer).
    213280
    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
     283The syntax for declaring an exception is the same as declaring a structure
     284except the keyword that is swapped out:
     285\begin{cfa}
     286exception TYPE_NAME {
     287        FIELDS
     288};
     289\end{cfa}
     290
     291Fields are filled in the same way as a structure as well. However an extra
     292field is added that contains the pointer to the virtual table.
     293It must be explicitly initialized by the user when the exception is
     294constructed.
     295
     296Here is an example of declaring an exception type along with a virtual table,
     297assuming the exception has an ``obvious" implementation and a default
     298virtual table makes sense.
     299
     300\begin{minipage}[t]{0.4\textwidth}
     301Header:
     302\begin{cfa}
     303exception Example {
     304        int data;
     305};
     306
     307extern vtable(Example)
     308        example_base_vtable;
     309\end{cfa}
     310\end{minipage}
     311\begin{minipage}[t]{0.6\textwidth}
     312Source:
     313\begin{cfa}
     314vtable(Example) example_base_vtable
     315\end{cfa}
     316\vfil
     317\end{minipage}
     318
     319%\subsection{Exception Details}
     320This is the only interface needed when raising and handling exceptions.
     321However it is actually a short hand for a more complex
     322trait based interface.
     323
     324The language views exceptions through a series of traits.
     325If a type satisfies them, then it can be used as an exception. The following
    221326is the base trait all exceptions need to match.
    222327\begin{cfa}
     
    225330};
    226331\end{cfa}
    227 The trait is defined over two types, the exception type and the virtual table
     332The trait is defined over two types: the exception type and the virtual table
    228333type. Each exception type should have a single virtual table type.
    229334There are no actual assertions in this trait because the trait system
     
    231336completing the virtual system). The imaginary assertions would probably come
    232337from 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 its virtual table type.
     338is a virtual type, is a descendant of @exception_t@ (the base exception type)
     339and allow the user to find the virtual table type.
    235340
    236341% I did have a note about how it is the programmer's responsibility to make
     
    250355};
    251356\end{cfa}
    252 Both traits ensure a pair of types are an exception type, its virtual table
    253 type,
     357Both traits ensure a pair of types is an exception type, its virtual table
     358type
    254359and defines one of the two default handlers. The default handlers are used
    255360as fallbacks and are discussed in detail in \vref{s:ExceptionHandling}.
     
    260365facing way. So these three macros are provided to wrap these traits to
    261366simplify referring to the names:
    262 @IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@, and @IS_RESUMPTION_EXCEPTION@.
     367@IS_EXCEPTION@, @IS_TERMINATION_EXCEPTION@ and @IS_RESUMPTION_EXCEPTION@.
    263368
    264369All three take one or two arguments. The first argument is the name of the
     
    283388These twin operations are the core of \CFA's exception handling mechanism.
    284389This section covers the general patterns shared by the two operations and
    285 then goes on to cover the details of each individual operation.
     390then goes on to cover the details each individual operation.
    286391
    287392Both operations follow the same set of steps.
    288393First, a user raises an exception.
    289 Second, the exception propagates up the stack.
     394Second, the exception propagates up the stack, searching for a handler.
    290395Third, if a handler is found, the exception is caught and the handler is run.
    291396After 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
     397As an alternate to the third step,
     398if a handler is not found, a default handler is run and, if it returns,
     399then control
    293400continues after the raise.
    294401
    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 after an exception is caught and handled, and which default handler is run.
     402The differences between the two operations include how propagation is
     403performed, where execution continues after an exception is handled
     404and which default handler is run.
    298405
    299406\subsection{Termination}
    300407\label{s:Termination}
    301 Termination handling is the familiar EHM and used in most programming
     408Termination handling is the familiar kind of handling
     409and used in most programming
    302410languages with exception handling.
    303411It is a dynamic, non-local goto. If the raised exception is matched and
     
    331439Then propagation starts with the search. \CFA uses a ``first match" rule so
    332440matching is performed with the copied exception as the search key.
    333 It starts from the raise in the throwing function and proceeds towards the base of the stack,
     441It starts from the raise site and proceeds towards base of the stack,
    334442from callee to caller.
    335443At each stack frame, a check is made for termination handlers defined by the
     
    345453\end{cfa}
    346454When viewed on its own, a try statement simply executes the statements
    347 in the \snake{GUARDED_BLOCK}, and when those are finished,
     455in the \snake{GUARDED_BLOCK} and when those are finished,
    348456the try statement finishes.
    349457
     
    371479termination exception types.
    372480The 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})
     482on the current stack with the copied exception.
     483Since it is so general, a more specific handler can be defined,
     484overriding the default behaviour for the specific exception types.
    376485
    377486\subsection{Resumption}
    378487\label{s:Resumption}
    379488
    380 Resumption exception handling is the less familar EHM, but is
     489Resumption exception handling is less familar form of exception handling,
     490but is
    381491just as old~\cite{Goodenough75} and is simpler in many ways.
    382492It is a dynamic, non-local function call. If the raised exception is
     
    387497function once the error is corrected, and
    388498ignorable events, such as logging where nothing needs to happen and control
    389 should always continue from the raise point.
     499should always continue from the raise site.
     500
     501Except for the changes to fit into that pattern, resumption exception
     502handling is symmetric with termination exception handling, by design
     503(see \autoref{s:Termination}).
    390504
    391505A resumption raise is started with the @throwResume@ statement:
     
    393507throwResume EXPRESSION;
    394508\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.
     510It works much the same way as the termination raise, except the
     511type must satisfy the \snake{is_resumption_exception} that uses the
     512default handler: \defaultResumptionHandler.
     513This can be specialized for particular exception types.
     514
     515At run-time, no exception copy is made. Since
    404516resumption 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 from 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.
     517current scope, there is no need to manage memory to keep the exception
     518allocated.
     519
     520Then propagation starts with the search,
     521following the same search path as termination,
     522from the raise site to the base of stack and top of try statement to bottom.
     523However, the handlers on try statements are defined by @catchResume@ clauses.
    412524\begin{cfa}
    413525try {
     
    419531}
    420532\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
     533Note that termination handlers and resumption handlers may be used together
    478534in a single try statement, intermixing @catch@ and @catchResume@ freely.
    479535Each type of handler only interacts with exceptions from the matching
    480536kind of raise.
     537Like @catch@ clauses, @catchResume@ clauses have no effect if an exception
     538is not raised.
     539
     540The matching rules are exactly the same as well.
     541The 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.
     544After the block has finished running control jumps to the raise site, where
     545the just handled exception came from, and continues executing after it,
     546not after the try statement.
    481547
    482548\subsubsection{Resumption Marking}
     
    486552and run, its try block (the guarded statements) and every try statement
    487553searched before it are still on the stack. There presence can lead to
    488 the \emph{recursive resumption problem}.
     554the recursive resumption problem.\cite{Buhr00a}
     555% Other possible citation is MacLaren77, but the form is different.
    489556
    490557The recursive resumption problem is any situation where a resumption handler
     
    500567When this code is executed, the guarded @throwResume@ starts a
    501568search 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
     569call is placed on the stack above the try-block.
     570Now the second raise in the handler searches the same try block,
     571matches again and then puts another instance of the
    504572same handler on the stack leading to infinite recursion.
    505573
    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 
     574While this situation is trivial and easy to avoid, much more complex cycles
     575can form with multiple handlers and different exception types.
    512576To prevent all of these cases, each try statement is ``marked" from the
    513 time the exception search reaches it to either when a matching handler
    514 completes or when the search reaches the base
     577time the exception search reaches it to either when a handler completes
     578handling that exception or when the search reaches the base
    515579of the stack.
    516580While a try statement is marked, its handlers are never matched, effectively
     
    524588for instance, marking just the handlers that caught the exception,
    525589would 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 from
     590However, the rules selected mirrors what happens with termination,
     591so this reduces the amount of rules and patterns a programmer has to know.
     592
     593The marked try statements are the ones that would be removed from
    530594the stack for a termination exception, \ie those on the stack
    531595between the handler and the raise statement.
     
    593657
    594658\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.
     659In languages without conditional catch, that is no ability to match an
     660exception based on something other than its type, it can be mimicked
     661by matching all exceptions of the right type, checking any additional
     662conditions inside the handler and re-raising the exception if it does not
     663match those.
     664
     665Here is a minimal example comparing both patterns, using @throw;@
     666(no argument) to start a re-raise.
    597667\begin{center}
    598 \begin{tabular}{l|l}
     668\begin{tabular}{l r}
    599669\begin{cfa}
    600670try {
    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
    609679\end{cfa}
    610680&
    611681\begin{cfa}
    612682try {
    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}
     694At first glance catch-and-reraise may appear to just be a quality of life
     695feature, but there are some significant differences between the two
     696stratagies.
     697
     698A simple difference that is more important for \CFA than many other languages
     699is that the raise site changes, with a re-raise but does not with a
     700conditional catch.
     701This is important in \CFA because control returns to the raise site to run
     702the per-site default handler. Because of this only a conditional catch can
     703allow the original raise to continue.
     704
     705The more complex issue comes from the difference in how conditional
     706catches and re-raises handle multiple handlers attached to a single try
     707statement. A conditional catch will continue checking later handlers while
     708a re-raise will skip them.
     709If the different handlers could handle some of the same exceptions,
     710translating a try statement that uses one to use the other can quickly
     711become non-trivial:
     712
     713\noindent
     714Original, 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}
     723Translated, 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);
    617732        } else {
    618733                throw;
     
    620735}
    621736\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,
     738using 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% }
    732745%
    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% }
     755In similar simple examples translating from re-raise to conditional catch
     756takes less code but it does not have a general trivial solution either.
     757
     758So, given that the two patterns do not trivially translate into each other,
     759it becomes a matter of which on should be encouraged and made the default.
     760From the premise that if a handler that could handle an exception then it
     761should, it follows that checking as many handlers as possible is preferred.
     762So conditional catch and checking later handlers is a good default.
    737763
    738764\section{Finally Clauses}
     
    750776The @FINALLY_BLOCK@ is executed when the try statement is removed from the
    751777stack, including when the @GUARDED_BLOCK@ finishes, any termination handler
    752 finishes, or during an unwind.
     778finishes or during an unwind.
    753779The only time the block is not executed is if the program is exited before
    754780the stack is unwound.
     
    770796they have their own strengths, similar to top-level function and lambda
    771797functions with closures.
    772 Destructors take more work for their creation, but if there is clean-up code
     798Destructors take more work to create, but if there is clean-up code
    773799that needs to be run every time a type is used, they are much easier
    774 to set-up.
     800to set-up for each use. % It's automatic.
    775801On the other hand finally clauses capture the local context, so is easy to
    776802use when the clean-up is not dependent on the type of a variable or requires
     
    788814raise, this exception is not used in matching only to pass information about
    789815the cause of the cancellation.
    790 Finaly, since a cancellation only unwinds and forwards, there is no default handler.
     816Finally, as no handler is provided, there is no default handler.
    791817
    792818After @cancel_stack@ is called the exception is copied into the EHM's memory
     
    799825After the main stack is unwound there is a program-level abort.
    800826
    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.
     827The first reason for this behaviour is for sequential programs where there
     828is only one stack, and hence to stack to pass information to.
     829Second, even in concurrent programs, the main stack has no dependency
     830on another stack and no reliable way to find another living stack.
     831Finally, keeping the same behaviour in both sequential and concurrent
     832programs is simple and easy to understand.
    807833
    808834\paragraph{Thread Stack}
     
    834860
    835861With 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
     862possible to cascade an error across any number of threads,
     863alternating between the resumption (possibly termination) and cancellation,
     864cleaning up each
    837865in turn, until the error is handled or the main thread is reached.
    838866
     
    847875caller's context and passes it to the internal report.
    848876
    849 A coroutine only knows of two other coroutines, its starter and its last resumer.
     877A coroutine only knows of two other coroutines,
     878its starter and its last resumer.
    850879The starter has a much more distant connection, while the last resumer just
    851880(in terms of coroutine state) called resume on this coroutine, so the message
     
    853882
    854883With 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,
     884cascade an error across any number of coroutines,
     885alternating between the resumption (possibly termination) and cancellation,
     886cleaning up each in turn,
    856887until 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 you
    859 talk about handling a cancellation in the last sentence. Which is correct?}
Note: See TracChangeset for help on using the changeset viewer.