Changeset 465ed18 for doc/working
- Timestamp:
- Jun 12, 2017, 3:30:08 PM (8 years ago)
- Branches:
- ADT, aaron-thesis, arm-eh, ast-experimental, cleanup-dtors, deferred_resn, demangler, enum, forall-pointer-decay, jacob/cs343-translation, jenkins-sandbox, master, new-ast, new-ast-unique-expr, new-env, no_list, persistent-indexer, pthread-emulation, qualifiedEnum, resolv-new, with_gc
- Children:
- 8d50e34, a724ac1
- Parents:
- ca78437
- Location:
- doc/working/exception
- Files:
-
- 1 added
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
TabularUnified doc/working/exception/design.txt ¶
rca78437 r465ed18 1 1 Design of Exceptions and EHM in Cforall: 2 2 3 Currently this is a combination of ideas and big questions that still have to 4 be addressed. It also includes some other error handling options, how they 5 interact and compare to exceptions. 3 4 Exception Instances: 5 Currently, exceptions are integers (like errno). 6 7 They are planned to be the new "tagged structures", which allows them to 8 exist in a simple hierarchy which shared functionality throughout. However 9 the tagged structures are not yet implemented so that will wait. 10 11 A single built in exception is at the top of the hierarchy and all other 12 exceptions are its children. When you match against an exception, you match 13 for an exception and its children, so the top of the hierarchy is used as a 14 catch-all option. 15 16 The shared functionality across exceptions has not been finalized, but will 17 probably include things like human readable descriptions and default handlers. 6 18 7 19 8 What is an Exception: 20 Throwing: 21 There are currently two kinds of throws, "throw" for termination and 22 "throwResume" for resumption. Both keywords can be used to create a throw 23 statement. The kind of throw decides what handlers may catch the exception 24 and weither control flow can return to the throw site. 9 25 10 In other words what do we throw? What is matched against, how does it carry 11 data with it? A very important question that has not been answered. 26 Syntax 27 "throw" exception ";" 28 "throwResume" exception ";" 12 29 13 Option 1: Strutures 30 Non-local throws are allowed for resumption only. A target is an object with 31 a stack, with which it may propagate and handle the exception. 14 32 15 Considering the current state of Cforall the most natural form of the 16 exception would likely be a struture, implementing a trait repersenting the 17 minimum features of an exception. This has many advantages, including arbitray 18 fields, some polymorphism and it matches exceptations of many current systems. 33 Syntax 34 "throwResume" exception "_At" target ";" 19 35 20 The main issue with this is matching, without OOP inheritance there is no 21 exception hierarchy. Meaning all handling has to happen on the exact exception 22 without the ease of grouping parents. There are several ways to attempt to 23 recover this. 24 25 The first is with conditional matching (a check after the type has been 26 matched) which allows for matching particular values of a known type. However 27 this does not dynamically expand and requires an extra step (as opposed to 28 mearly allowing one). I would not recomend this as the primary method. 29 30 Second is to try and use type casts/conversions to create an implicate 31 hierachy, so that a catch clause catches anything of the given type or 32 anything that converts to the given type. 33 34 Plan9 (from what I know of it) would be a powerful tool here. Even with it, 35 creating a hierarchy of types at runtime might be too expencive. Esecially 36 since they are less likely to be tree like at that point. 37 38 Option 2: Tags 39 40 The other option is to introduce a new construct into the language. A tag 41 repersents a type of exception, it is not a structure or variable (or even 42 a normal type). It may be usable in some of those contexts. 43 44 Tags can declare an existing tag as its parent. Tags can be caught by handlers 45 that catch their parents. (There is a single base_exception that all other 46 exceptions are children of eventually.) This allows for grouping of exceptions 47 that repersent similar errors. 48 49 Tags should also have some assotiated data, where and on what did the error 50 occur. Keeping with the otherness of exception tags and allowing them to be 51 expanded, using a parameter list. Each exception can have a list of paramters 52 given to it on a throw. Each tag would have a declared list of parameters 53 (which could be treated more like a set of fields as well). Child tags must 54 use their parent's list as a prefix to their own, so that the parameters can 55 be accessed when the child tag is matched against the parent. 56 57 Option N: ... 58 59 This list is not complete. 36 Termination throws unwind the stack until a handler is reached, control moves 37 onwards from the end of the handler. Resumption throws do not unwind, if a 38 handler is found and control will return to the throw after the exception is 39 handled. 60 40 61 41 62 Seperating Termination and Resumption: 42 Catching: 43 The catch and handle of an exception is preformed with a try statement, which 44 also can have finally clauses to exceute on exit from the scope. 63 45 64 Differentating the types of exceptions based on exception would be hard with 65 exceptions as structures. It is possible with exceptions as tags by having 66 two base exceptions, one for each type of throw. However recompining them 67 to dual types would be harder. 46 Syntax 47 "try" 48 try-block 49 ( ("catch" | "catchResume") 50 "(" exception_type [identifier] [";" conditional_expression] ")" 51 catch-block 52 )* 53 ("finally" 54 finally-block 55 )? 68 56 69 Reguardless, using different keywords would also be useful for clarity, even 70 if it does not add functality. Using the C++ like keywords would be a good 71 base. Resumption exceptions could use a different set (ex. raise->handle) or 72 use resume as a qualifier on the existing statements. 57 Either at least 1 handler clause or the finally clasue must be given on each 58 try block. Each handler clause handles 1 of the two types of throws. Each 59 handler also specifies a type of exception it handles, and will handle all 60 children exceptions as well. In addition, a conditional expression which, if 61 included, must be true for the handler to catch the exception. 62 63 The two types of handlers may be intermixed. Multiple handlers catching the 64 same type may also be used, to allow for fallbacks on false conditionals. 73 65 74 66 75 Conditional Matching:67 Implementation Overview: 76 68 77 A possible useful feature, it allows for arbitrary checks on a catch block 78 instead of merely matching a type. However there are few use cases that 79 cannot be avoided with proper use of a type hierarchy, and this shrinks even 80 further with a good use of re-throws.69 The implementation has two main parts. The first is just a collection of the 70 support definitions we need, the data types and functions used within the 71 exception handling code. Second is a translation from Cforall code to C code 72 that uses those definitions to throw, catch and handle exceptions. 81 73 82 Also it assumes one sweep, that might also be a problem. But might also give 83 it an advantage over re-throws. 74 Termination handlers call a specially annotated function, passing it inner 75 functions that act as the varius sub-blocks. Termination throws use the 76 unwind library that checks the underlying code for those annotations. Each 77 time one is found some magic is used to check for a matching handler, if one 78 is found control goes to the special function which excecutes the handler and 79 returns. 80 81 Resumption handlers maintain a linked list of stack allocated nodes that have 82 the handler functions attached. Throwing a resumption exception traverses this 83 list, and calls each handler, the handlers handle the exception if they can 84 and return if they did or not. 85 86 Finally clauses just use stack cleanup to force a nested function, which has 87 the code from the finally clause, to execute when we leave that section. 84 88 85 89 86 Alternative s: Implicate Handlers &Return Unions90 Alternative Error Handling: Return Unions 87 91 88 Both act as a kind of local version of an exception. Implicate handlers act as 89 resumption exceptions and return unions like termination exceptions. By local 90 I mean they work well at one or two levels of calls, but do not cover N levels 91 as cleanly. 92 Return unions (Maybe and Result), are types that can encode a success or 93 other result in a single value. Maybe stores a value or nothing, Result stores 94 a value or an error. 92 95 93 Implicate handles require a growing number of function pointers (which should 94 not be used very often) to be passed to functions, creating and additional 95 preformance cost. Return unions have to be checked and handled at every level, 96 which has some preformance cost, but also can significantly clutter code. 97 Proper tools can help with the latter. 96 For errors that are usually handled quite close to where they occur, these 97 can replace exceptions. 98 98 99 However, they may work better at that local level as they do not require stack 100 walking or unwinding. In addition they are closer to regular control flow and 101 are easier to staticly check. So even if they can't replace exceptions 102 (directly) they may still be worth using together. 103 104 For instance, consider the Python iterator interface. It uses a single 105 function, __next__, to access the next value and to signal the end of the 106 sequence. If a value is returned, it is the next value, if the StopIteration 107 exception is thrown the sequence has finished. 108 109 However almost every use of an iterator will add a try-except block around the 110 call site (possibly through for or next) to catch and handle the exception 111 immediately, ignoring the advantages of more distant exception handling. 112 113 In this case it may be cleaner to use a Maybe for both cases (as in Rust) 114 which gives similar results without having to jump to the exception handler. 115 This will likely handle the error case more efficiently and the success case a 116 bit less so. 117 118 It also mixes the error and regular control flow, which can hurt readablity, 119 but very little if the handling is simple, for instance providing a default 120 value. Similarly, if the error (or alternate outcome) is common enough 121 encoding it in the function signature may be good commuication. 99 They tend to be faster and require similar or less amounts of code to handle. 100 However they can slow down the normal path with some extra conditionals and 101 can mix the normal and exceptional control flow path. If handling the error 102 is simple, and happens relatively frequently, this might be prefered but in 103 other cases it just hurts speed and readability. 122 104 123 105 In short, these errors seem to be more effective when errors are likely and … … 125 107 be handled locally, might be better off using these instead of exceptions. 126 108 127 Also the implicate handlers and return unions could use exceptions as well. 128 For instance, a useful default might handler might be to throw an exception, 129 seaching up the stack for a solution if one is not locally provided. 130 131 Or here is a possible helper for unpacking a Result value: 109 Also the return unions could use exceptions as well. Getting the improper 110 side of a return union might throw an exception. Or we can provide helpers 111 for results withe exceptions as in: 132 112 forall(otype T, otype E | exception(E)) 133 113 T get_or_throw (Result(T, E) * this) { 134 if ( is_success(this)) {135 return get_ success(this);114 if (has_value(this)) { 115 return get_value(this); 136 116 } else { 137 throw get_ failure(this);117 throw get_error(this); 138 118 } 139 119 } 140 So they can feed off of each other.
Note: See TracChangeset
for help on using the changeset viewer.