If we are to achieve a successful combination of type-safety and run-time-binding modularity, I think we need to look very carefully at error-condition-mechanisms, as they can be a significant cause of ABI backward-compatibility challenges when components are updated. Performance of error-condition mechanisms is also an important issue.
The simple analogy I choose for this problem is to consider how much traction or trust the standard C library would have ever achieved if it contained calls to exit(1) after runtime events, if error return codes did not have a reliable backward-compatible success/fail test, such as the zero-as-single-success-value pattern, or if certain error conditions executed sleep(1) or spin-wait before they were returned. This would have been untenable for programming in the large. In my view, a successful systems programming environment must (a) rely on an error-signaling mechanism with a backward-and-forward compatible success/fail test, must (b) not change the error-generating profile of a function in successive supposedly-compatible versions (for this is a break of the ABI), and should (c) not mandate excessive performance overhead for error-signaling. Further, if we wish to aid the programmer, we should consider (zz) mechanisms which help assure exhaustive success/fail-handling and/or exhaustive condition handling when desired. IMO, most (all?) modular-compilation stack-unroll exception implementations fail *both* (a) and (b). This includes not only the older C++, but also the very modern CLR. While catch-all exception handlers might seem like a backward compatible success/fail test, in the context-of stack-unroll and non-local exception handling they practically are not. In order to meet criteria (a), one would need to wrap *every* statement in a catchall try block, and never handle exceptions non-locally. Here is a look at some specific mechanisms.. C++: Fails both (a), and (b) and does not provide (zz). Shap recently quoted 20% as an overhead for it's exception mechanism, which is slower than return values but nowhere near as bad as some exception unroll costs. CLR: Fails both (a) and (b) and does not provide (zz). Further, because the CLR exception unroll mechanism is *very* expensive, library functions often provide exception and return-value versions. However, if the library author did not choose to create a return-value version, the library becomes unusable for high-performance use. Java: Fails (a) and (b) despite substantial support for (zz), through it's checked-exception system. It fails (a) and (b) due to frequent user-defined RuntimeExceptions, which are exacerbated by the cumbersome nature of it's checked-exception system. -- IMO..there are less-cumbersome ways to achieve the same level of checked-exception-safety through compiler inference, requiring throws declaration only at module boundaries (see my old brainstorm C# checked exception proposal<http://dj1.willowmail.com/~jeske/unsolicitedDave/csharp_checked_exception_proposal.html>) If this were fixed, IMO the use of RuntimeExceptions could be minimized or eliminated. This solution however, is unviable for JVM, as it has no such assembly/module boundary over which to perform inference. Google-Go: has no stack-unroll exception mechanism, instead relying on error-return-values and the backward-compatible zero-as-success-value test. Google-Go meets both criteria (a) and (b), though it is unfortunate that so-far it offers no compiler assistance to assure exhaustive error-condition checking as in (zz). It also carries all the Objective-C: Contains a C++ like exception mechanism, but standard MacOS/iOS libraries do not make use of exceptions, instead returning error-codes. These standard system libraries meet (a) and (b), though if they used Objective-C exceptions they would not. I think there are two attractive paths forward for error-condition signaling: (1) Structured error return handling. By which I mean a system based on error-return codes with more try/catch/raise like language syntax, to help reduce confusion and compiler-assisted exhaustive checking and declarations, to increase safety. This has the advantage that return-values are very fast, and the disadvantage of potentially more complex compilers and module-internal sub-compilation unit invalidation (slower compile times). (2) Fully checked-exceptions, with performant setup and unroll, and module-level inference. By which I mean a system substantially like Java's checked exception system, but where throws declarations are only required on module boundaries. I think it's also worth noting that there was some ML research on type-polymorphic exceptions implemented using regular-return-values, which not only solved some interesting exception polymorphism cases but was also faster than stack-unroll equivalents. It's going to take me some time to dig up the research reference.
_______________________________________________ bitc-dev mailing list [email protected] http://www.coyotos.org/mailman/listinfo/bitc-dev
