From 47a89e4edff3b5d3ffa516d2f02ed62c4da48848 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Wed, 8 Nov 2023 17:41:53 +0100 Subject: [PATCH] 1.6 updates (#18) --- src/00_introduction.md | 4 ++++ src/errors.exceptions.md | 26 +++++++------------------- src/errors.md | 2 +- src/errors.result.md | 4 ++-- src/language.memory.md | 2 +- src/language.proctypes.md | 7 +++---- 6 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/00_introduction.md b/src/00_introduction.md index 7bac01d..06bbbfa 100644 --- a/src/00_introduction.md +++ b/src/00_introduction.md @@ -28,6 +28,10 @@ When in doubt: The latest version of this book can be found [online](https://status-im.github.io/nim-style-guide/) or on [GitHub](https://github.com/status-im/nim-style-guide/). +This guide currently targets Nim v1.6. + +At the time of writing, v2.0 has been released but its new garbage collector is not yet stable enough for production use. It is advisable to test new code with both `--mm:gc` and `--mm:orc` (the default) in the transition period. + ## Practical notes diff --git a/src/errors.exceptions.md b/src/errors.exceptions.md index 8fb1a46..d96d2aa 100644 --- a/src/errors.exceptions.md +++ b/src/errors.exceptions.md @@ -4,26 +4,17 @@ In general, prefer [explicit error handling mechanisms](errors.result.md). Annotate each module at top-level (before imports): -* `{.push raises: [Defect].}` (nim 1.2) -* `{.push raises: [].}` (nim 1.4+) - -To make a module compatible with both Nim 1.2 and newer versions, use: - ```nim -when (NimMajor, NimMinor) < (1, 4): - {.push raises: [Defect].} -else: {.push raises: [].} ``` - Use explicit `{.raises.}` annotation for each public (`*`) function. -Raise `Defect` to signal panics and situations that the code is not prepared to handle. +Raise `Defect` to signal panics and undefined behavior that the code is not prepared to handle. ```nim # Enable exception tracking for all functions in this module -`{.push raises: [Defect].}` # Always at start of module +`{.push raises: [].}` # Always at start of module # Inherit from CatchableError and name XxxError type MyLibraryError = object of CatchableError @@ -37,7 +28,7 @@ type MySpecificError = object of MyLibraryError # Explicitly annotate functions with raises - this replaces the more strict # module-level push declaration on top -func f() {.raises: [Defect, MySpecificError]} = discard +func f() {.raises: [MySpecificError]} = discard # Isolate code that may generate exceptions using expression-based try: let x = @@ -68,8 +59,7 @@ raise (ref MyError)(msg: "description", data: value) * Nim exception hierarchy unclear and changes between versions * The distinction between `Exception`, `CatchableError` and `Defect` is inconsistently implemented * [Exception hierarchy RFC not being implemented](https://github.com/nim-lang/Nim/issues/11776) - * `Defect` is [not correctly tracked]((https://github.com/nim-lang/Nim/issues/12862)) - * Nim 1.4 further weakens compiler analysis around `Defect`(https://github.com/nim-lang/Nim/pull/13626) + * `Defect` is [not tracked](https://github.com/nim-lang/Nim/pull/13626) * Without translation, exceptions leak information between abstraction layers * Writing exception-safe code in Nim impractical due to missing critical features present in C++ * No RAII - resources often leak in the presence of exceptions @@ -77,7 +67,7 @@ raise (ref MyError)(msg: "description", data: value) * No constructors, thus no way to force particular object states at construction * `ref` types incompatible with destructors, even if they worked * Poor performance of error path - * Several heap allocations for each Exception (exception, stack trace, string) + * Several heap allocations for each `Exception`` (exception, stack trace, message) * Expensive stack trace * Poor performance on happy path * Every `try` and `defer` has significant performance overhead due to `setjmp` exception handling implementation @@ -86,11 +76,9 @@ raise (ref MyError)(msg: "description", data: value) The use of exceptions in Nim has significantly contributed to resource leaks, deadlocks and other difficult bugs. The various exception handling proposals aim to alleviate some of the issues but have not found sufficient grounding in the Nim community to warrant the language changes necessary to proceed. -A notable exception to the guideline is `chronos` and `async`/`await` transformations that lack support for propagating checked exception information. Several bugs and implementation issues exist in the exception handling transformation employed by `async`. - ### `Defect` -`Defect` does [not cause](https://github.com/nim-lang/Nim/issues/12862) a `raises` effect - code must be manually verified - common sources of Defect include: +`Defect` does [not cause](https://github.com/nim-lang/Nim/issues/12862) a `raises` effect - code must be manually verified - common sources of `Defect` include: * Over/underflows in signed arithmetic * `[]` operator for indexing arrays/seqs/etc (but not tables!) @@ -98,7 +86,7 @@ A notable exception to the guideline is `chronos` and `async`/`await` transforma ### `CatchableError` -Catching `CatchableError` implies that any errors are funnelled through the same exception handler. When called code starts raising new exceptions, it becomes difficult to find affected code - catching more specific errors avoids this maintenance problem. +Catching `CatchableError` implies that all errors are funnelled through the same exception handler. When called code starts raising new exceptions, it becomes difficult to find affected code - catching more specific errors avoids this maintenance problem. Frameworks may catch `CatchableError` to forward exceptions through layers. Doing so leads to type erasure of the actual raised exception type in `raises` tracking. diff --git a/src/errors.md b/src/errors.md index d4d022d..2a122b3 100644 --- a/src/errors.md +++ b/src/errors.md @@ -20,4 +20,4 @@ When porting code, there are two approaches: * Bottom up - fix the underlying library / code * Top down - isolate the legacy code with `try/except` - * In this case, we note where the Exception effect is coming from, should it be fixed in the future + * In this case, we note where the `Exception` effect is coming from, should it be fixed in the future diff --git a/src/errors.result.md b/src/errors.result.md index 20438fd..c53e6c0 100644 --- a/src/errors.result.md +++ b/src/errors.result.md @@ -14,7 +14,7 @@ Isolate legacy code with explicit exception handling, converting the errors to ` ```nim # Enable exception tracking for all functions in this module -{.push raises: [Defect].} # Always at start of module +{.push raises: [].} # Always at start of module import stew/results export results # Re-export modules used in public symbols @@ -24,7 +24,7 @@ export results # Re-export modules used in public symbols func f*(): Result[void, cstring] # In special cases that warrant the use of exceptions, list these explicitly using the `raises` pragma. -func parse(): Type {.raises: [Defect, ParseError]} +func parse(): Type {.raises: [ParseError]} ``` See also [Result](libraries.results.md) for more recommendations about `Result`. diff --git a/src/language.memory.md b/src/language.memory.md index ca383f7..9ca3636 100644 --- a/src/language.memory.md +++ b/src/language.memory.md @@ -5,7 +5,7 @@ Use heap allocation in glue layers. Avoid `alloca`. -``` +```nim func init(T: type Yyy, a, b: int): T = ... # Heap allocation as a local decision diff --git a/src/language.proctypes.md b/src/language.proctypes.md index 4ab65f6..5c36f06 100644 --- a/src/language.proctypes.md +++ b/src/language.proctypes.md @@ -1,17 +1,16 @@ ## Callbacks, closures and forward declarations `[language.proctypes]` -Annotate `proc` type definitions and forward declarations with `{.raises [Defect], gcsafe.}` or specific exception types. +Annotate `proc` type definitions and forward declarations with `{.raises [], gcsafe.}` or specific exception types. ```nim # By default, Nim assumes closures may raise any exception and are not gcsafe # By annotating the callback with raises and gcsafe, the compiler ensures that # any functions assigned to the closure fit the given constraints -type Callback = proc(...) {.raises: [Defect], gcsafe.} +type Callback = proc(...) {.raises: [], gcsafe.} ``` ### Practical notes -* Without annotations, `raises Exception` and no GC-safety is assumed by the compiler, infecting deduction in the whole call stack +* Without annotations, `{.raises [Exception].}` and no GC-safety is assumed by the compiler, infecting deduction in the whole call stack * Annotations constrain the functions being assigned to the callback to follow its declaration, simplifying calling the callback safely * In particular, callbacks are difficult to reason about when they raise exceptions - what should the caller of the callback do? -