diff --git a/doc/manual.md b/doc/manual.md index d3c81aa78f..a35a67851b 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -9043,3 +9043,119 @@ This means the following compiles (for now) even though it really should not: inc i access a[i].v ``` + +Strict definitions and `out` parameters +======================================= + +*every* local variable must be initialized explicitly before it can be used: + + ```nim + proc test = + var s: seq[string] + s.add "abc" # invalid! + ``` + +Needs to be written as: + + ```nim + proc test = + var s: seq[string] = @[] + s.add "abc" # valid! + ``` + +A control flow analysis is performed in order to prove that a variable has been written to +before it is used. Thus the following is valid: + + ```nim + proc test(cond: bool) = + var s: seq[string] + if cond: + s = @["y"] + else: + s = @[] + s.add "abc" # valid! + ``` + +In this example every path does set `s` to a value before it is used. + + ```nim + proc test(cond: bool) = + let s: seq[string] + if cond: + s = @["y"] + else: + s = @[] + ``` + +`let` statements are allowed to not have an initial value, but every path should set `s` to a value before it is used. + + +`out` parameters +---------------- + +An `out` parameter is like a `var` parameter but it must be written to before it can be used: + + ```nim + proc myopen(f: out File; name: string): bool = + f = default(File) + result = open(f, name) + ``` + +While it is usually the better style to use the return type in order to return results API and ABI +considerations might make this infeasible. Like for `var T` Nim maps `out T` to a hidden pointer. +For example POSIX's `stat` routine can be wrapped as: + + ```nim + proc stat*(a1: cstring, a2: out Stat): cint {.importc, header: "".} + ``` + +When the implementation of a routine with output parameters is analysed, the compiler +checks that every path before the (implicit or explicit) return does set every output +parameter: + + ```nim + proc p(x: out int; y: out string; cond: bool) = + x = 4 + if cond: + y = "abc" + # error: not every path initializes 'y' + ``` + + +Out parameters and exception handling +------------------------------------- + +The analysis should take exceptions into account (but currently does not): + + ```nim + proc p(x: out int; y: out string; cond: bool) = + x = canRaise(45) + y = "abc" # <-- error: not every path initializes 'y' + ``` + +Once the implementation takes exceptions into account it is easy enough to +use `outParam = default(typeof(outParam))` in the beginning of the proc body. + +Out parameters and inheritance +------------------------------ + +It is not valid to pass an lvalue of a supertype to an `out T` parameter: + + ```nim + type + Superclass = object of RootObj + a: int + Subclass = object of Superclass + s: string + + proc init(x: out Superclass) = + x = Superclass(a: 8) + + var v: Subclass + init v + use v.s # the 's' field was never initialized! + ``` + +However, in the future this could be allowed and provide a better way to write object +constructors that take inheritance into account. + diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index 9bf64790f5..81defd70b5 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -1919,136 +1919,6 @@ restrictions / changes: yet performed for ordinary slices outside of a `parallel` section. -Strict definitions and `out` parameters -======================================= - -With `experimental: "strictDefs"` *every* local variable must be initialized explicitly before it can be used: - - ```nim - {.experimental: "strictDefs".} - - proc test = - var s: seq[string] - s.add "abc" # invalid! - - ``` - -Needs to be written as: - - ```nim - {.experimental: "strictDefs".} - - proc test = - var s: seq[string] = @[] - s.add "abc" # valid! - - ``` - -A control flow analysis is performed in order to prove that a variable has been written to -before it is used. Thus the following is valid: - - ```nim - {.experimental: "strictDefs".} - - proc test(cond: bool) = - var s: seq[string] - if cond: - s = @["y"] - else: - s = @[] - s.add "abc" # valid! - ``` - -In this example every path does set `s` to a value before it is used. - - ```nim - {.experimental: "strictDefs".} - - proc test(cond: bool) = - let s: seq[string] - if cond: - s = @["y"] - else: - s = @[] - ``` - -With `experimental: "strictDefs"`, `let` statements are allowed to not have an initial value, but every path should set `s` to a value before it is used. - - -`out` parameters ----------------- - -An `out` parameter is like a `var` parameter but it must be written to before it can be used: - - ```nim - proc myopen(f: out File; name: string): bool = - f = default(File) - result = open(f, name) - ``` - -While it is usually the better style to use the return type in order to return results API and ABI -considerations might make this infeasible. Like for `var T` Nim maps `out T` to a hidden pointer. -For example POSIX's `stat` routine can be wrapped as: - - ```nim - proc stat*(a1: cstring, a2: out Stat): cint {.importc, header: "".} - ``` - -When the implementation of a routine with output parameters is analysed, the compiler -checks that every path before the (implicit or explicit) return does set every output -parameter: - - ```nim - proc p(x: out int; y: out string; cond: bool) = - x = 4 - if cond: - y = "abc" - # error: not every path initializes 'y' - ``` - - -Out parameters and exception handling -------------------------------------- - -The analysis should take exceptions into account (but currently does not): - - ```nim - proc p(x: out int; y: out string; cond: bool) = - x = canRaise(45) - y = "abc" # <-- error: not every path initializes 'y' - ``` - -Once the implementation takes exceptions into account it is easy enough to -use `outParam = default(typeof(outParam))` in the beginning of the proc body. - -Out parameters and inheritance ------------------------------- - -It is not valid to pass an lvalue of a supertype to an `out T` parameter: - - ```nim - type - Superclass = object of RootObj - a: int - Subclass = object of Superclass - s: string - - proc init(x: out Superclass) = - x = Superclass(a: 8) - - var v: Subclass - init v - use v.s # the 's' field was never initialized! - ``` - -However, in the future this could be allowed and provide a better way to write object -constructors that take inheritance into account. - - -**Note**: The implementation of "strict definitions" and "out parameters" is experimental but the concept -is solid and it is expected that eventually this mode becomes the default in later versions. - - Strict case objects ===================