Version 0.20.0 released

We are very proud to announce Nim version 0.20.

This is a massive release, both literally and figuratively. It contains more than 1,000 commits and it marks our release candidate for version 1.0!

Version 0.20 introduces a number of breaking changes required for version 1.0. These are changes that we feel have been necessary to include in Nim 1.0 and we currently have no plans for any further breaking changes. Version 0.20 is effectively Nim 1.0 RC1.

Why not just release v1.0? We want to give the community a chance to test 0.20.0 and find bugs that may require breaking changes. This is unlikely, but may require further release candidates. Make no mistake, this release represents what we consider Nim 1.0, we have been working towards this milestone for many years and are incredibly excited to reach it.

The stability guarantee

A 1.0 release means that once either Nim 0.20.0 is promoted to 1.0 status, or another release candidate is, there will no longer be any breaking changes made. Version 1.0 will become a long-term supported stable release that will only receive bug fixes and new features in the future, as long as they don’t break backwards compatibility.

The 1.0.x branch will receive bug fixes for as long as there is demand for them. New features (which do not break backwards compatibility) will continue in steadily advancing 1.x branches.

Our goal is to make sure that code which compiled under Nim 1.0 continues to compile under any stable Nim 1.x version.

What’s included under the stability guarantee?

Backwards compatibility covers only the stable fragment of the language, as defined by the manual.

The compiler still implements experimental features which are documented in the newly written “experimental manual”, these features are subject to changes which may be backwards incompatible, some of the features included under this umbrella are concepts, the do notation and a few others. Be wary of using these features in production, but do get in touch with us if you want to learn more about our plans regarding them.

The standard library is also covered, we will continue to deprecate procedures if we must, but they will remain supported throughout the 1.x version series.

Exceptions to the rule

We of course have to concede that there are exceptions. In certain serious cases, like for example when a security vulnerability is discovered in the stdlib, we reserve the right to break code which uses it.

Installing 0.20.0

If you have installed a previous version of Nim using choosenim , getting Nim 0.20.0 is as easy as:

$ choosenim update stable

If you don’t have it already, you can get choosenim by following these instructions or you can install Nim by following the instructions on our install page.

Nimble 0.10.2

This release includes a brand new version of Nimble. See Nimble changelog.

Contributors to v0.20

Our contributors are amazing, and there is far too many to list here. Big thanks to all of you, we couldn’t have pulled off this release without you!

New features

Version 0.20 is positively jam packed with features. Here is a selection of our favourites:

not is always a unary operator

let a = false # v0.19: assert not a # Error: type mismatch: got <proc (cond: untyped, msg: string): typed, bool> assert ( not a ) # workaround # v0.20: assert not a

Stricter compile time checks for integer and float conversions

# v0.19: const b = uint16 ( - 1 ) echo b # 65535 # v0.20: const b = uint16 ( - 1 ) # Error: -1 can't be converted to uint16 const c = not uint16 ( 0 ) echo c # 65535

Tuple unpacking for constant and for loop variables

const ( d , e ) = ( 7 , "eight" ) # v0.19: Error: identifier expected, but got '(' # v0.20: echo d # 7 echo e # eight let f = @[ ( 51 , 10 ), ( 23 , 25 ) ] for ( x , y ) in f : # v0.19: Error: identifier expected, but got '(' echo x + y # v0.20: # 61 # 48

Hash sets and tables are initialized by default

import sets , tables var s : HashSet [ int ] s . incl ( 5 ) # v0.19: `isValid(s)` Error: unhandled exception: The set needs to be initialized. [AssertionError] # v0.20: echo s # {5} var t : Table [ char , int ] t [ 'a' ] = 10 # v0.19: Error: unhandled exception: index out of bounds [IndexError] # v0.20: echo t # {'a': 10}

Better error message for case-statements

type MyEnum = enum first second third fourth proc foo ( x : MyEnum ): int = case x of first : 1 of second : 2 of third : 3 of fourth : 4 else : 99 # v0.19: compiles # v0.20: Error: invalid else, all cases are already covered proc bar ( x : MyEnum ): int = case x of first : 1 of third : 3 # v0.19: Error: not all cases are covered # v0.20: Error: not all cases are covered; missing: {second, fourth}

The length of a table must not change during iteration

import tables var xs = { 1 : "one" , 2 : "two" , 3 : "three" }. toTable for x in xs . keys : if x mod 2 == 0 : xs [ 10 * x ] = "a lot" echo xs # v0.19: {200: "a lot", 1: "one", 2: "two", 3: "three", 20: "a lot"} # v0.20: Error: unhandled exception: the length of the table changed while iterating over it [AssertionError]

Better error message for index out of bounds

let a = [ 10 , 20 , 30 ] echo a [ 5 ] # v0.19: Error: index out of bounds # v0.20: Error: index 5 not in 0 .. 2

Changelog

Changes affecting backwards compatibility

shr is now sign preserving. Use -d:nimOldShiftRight to enable the old behavior globally.

The isLower , isUpper family of procs in strutils/unicode operating on strings have been deprecated since it was unclear what these do. Note that the much more useful procs that operate on char or Rune are not affected.

strutils.editDistance has been deprecated, use editdistance.editDistance or editdistance.editDistanceAscii instead.

The OpenMP parallel iterator ` || ` now supports any #pragma omp directive and not just #pragma omp parallel for . See OpenMP documentation. The default annotation is parallel for , if you used OpenMP without annotation the change is transparent, if you used annotations you will have to prefix your previous annotations with parallel for . Furthermore, an overload with positive stepping is available.

The unchecked pragma was removed, instead use system.UncheckedArray .

The undocumented #? strongSpaces parsing mode has been removed.

The not operator is now always a unary operator, this means that code like assert not isFalse(3) compiles.

getImpl on a var or let symbol will now return the full IdentDefs tree from the symbol declaration instead of just the initializer portion.

Methods are now ordinary “single” methods, only the first parameter is used to select the variant at runtime. For backwards compatibility use the new --multimethods:on switch.

Generic methods are now deprecated; they never worked well.

Compile time checks for integer and float conversions are now stricter. For example, const x = uint32(-1) now gives a compile time error instead of being equivalent to const x = 0xFFFFFFFF'u32 .

Using typed as the result type in templates/macros now means “expression with a type”. The old meaning of typed is preserved as void or no result type at all.

A bug allowed macro foo(): int = 123 to compile even though a macro has to return a NimNode . This has been fixed.

With the exception of uint and uint64 , conversion to unsigned types are now range checked during runtime.

Macro arguments of type typedesc are now passed to the macro as NimNode like every other type except static . Use typed for a behavior that is identical in new and old Nim. See the RFC Pass typedesc as NimNode to macros for more details.

Breaking changes in the standard library

osproc.execProcess now also takes a workingDir parameter.

std/sha1.secureHash now accepts openArray[char] , not string . (Former successful matches should keep working, though former failures will not.)

options.UnpackError is no longer a ref type and inherits from system.Defect instead of system.ValueError .

system.ValueError now inherits from system.CatchableError instead of system.Defect .

The procs parseutils.parseBiggestInt , parseutils.parseInt , parseutils.parseBiggestUInt and parseutils.parseUInt now raise a ValueError when the parsed integer is outside of the valid range. Previously they sometimes raised an OverflowError and sometimes they returned 0 .

The procs parseutils.parseBin , parseutils.parseOct and parseutils.parseHex were not clearing their var parameter number and used to push its value to the left when storing the parsed string into it. Now they always set the value of the parameter to 0 before storing the result of the parsing, unless the string to parse is not valid (then the value of number is not changed).

streams.StreamObject now restricts its fields to only raise system.Defect , system.IOError and system.OSError . This change only affects custom stream implementations.

nre’s RegexMatch.{captureBounds,captures}[] no longer return Option or nil / "" , respectively. Use the newly added n in p.captures method to check if a group is captured, otherwise you’ll receive an exception.

nre’s RegexMatch.{captureBounds,captures}.toTable no longer accept a default parameter. Instead uncaptured entries are left empty. Use Table.getOrDefault() if you need defaults.

nre’s RegexMatch.captures.{items,toSeq} now returns an Option[string] instead of a string . With the removal of nil strings, this is the only way to indicate a missing match. Inside your loops, instead of capture == "" or capture == nil , use capture.isSome to check if a capture is present, and capture.get to get its value.

nre’s replace() no longer throws ValueError when the replacement string has missing captures. It instead throws KeyError for named captures, and IndexError for unnamed captures. This is consistent with RegexMatch.{captureBounds,captures}[] .

splitFile now correctly handles edge cases, see #10047.

isNil is no longer false for undefined in the JavaScript backend: now it’s true for both nil and undefined. Use isNull or isUndefined if you need exact equality: isNil is consistent with === , isNull and isUndefined with == .

several deprecated modules were removed: ssl , matchers , httpserver , unsigned , actors , parseurl

two poorly documented and not used modules ( subexes , scgi ) were moved to graveyard (they are available as Nimble packages)

procs string.add(int) and string.add(float) which implicitly convert ints and floats to string have been deprecated. Use string.addInt(int) and string.addFloat(float) instead.

case object branch transitions via system.reset are deprecated. Compile your code with -d:nimOldCaseObjects for a transition period.

base64 module: The default parameter newLine for the encode procs was changed from "\13\10" to the empty string "" .

Breaking changes in the compiler

The compiler now implements the “generic symbol prepass” for when statements in generics, see bug #8603. This means that code like this does not compile anymore:

proc enumToString * ( enums : openArray [ enum ] ): string = # typo: 'e' instead 'enums' when e . low . ord >= 0 and e . high . ord < 256 : result = newString ( enums . len ) else : result = newString ( enums . len * 2 )

discard x is now illegal when x is a function symbol.

Implicit imports via --import: module in a config file are now restricted to the main package.

Library additions

There is a new stdlib module std/editdistance as a replacement for the deprecated strutils.editDistance .

There is a new stdlib module std/wordwrap as a replacement for the deprecated strutils.wordwrap .

Added split , splitWhitespace , size , alignLeft , align , strip , repeat procs and iterators to unicode.nim .

Added or for NimNode in macros .

Added system.typeof for more control over how type expressions can be deduced.

Added macros.isInstantiationOf for checking if the proc symbol is instantiation of generic proc symbol.

Added the parameter isSorted for the sequtils.deduplicate proc.

Added os.relativePath .

Added parseopt.remainingArgs .

Added os.getCurrentCompilerExe (implemented as getAppFilename at CT), can be used to retrieve the currently executing compiler.

Added xmltree.toXmlAttributes .

Added std/sums module for fast summation functions.

Added Rusage , getrusage , wait4 to the posix interface.

Added the posix_utils module.

Added system.default .

Added sequtils.items for closure iterators, allows closure iterators to be used by the mapIt , filterIt , allIt , anyIt , etc.

Library changes

The string output of macros.lispRepr proc has been tweaked slightly. The dumpLisp macro in this module now outputs an indented proper Lisp, devoid of commas.

Added macros.signatureHash that returns a stable identifier derived from the signature of a symbol.

In strutils empty strings now no longer match as substrings.

The Complex type is now a generic object and not a tuple anymore.

The ospaths module is now deprecated, use os instead. Note that os is available in a NimScript environment but unsupported operations produce a compile-time error.

The parseopt module now supports a new flag allowWhitespaceAfterColon (default value: true) that can be set to false for better Posix interoperability. (Bug #9619.)

os.joinPath and os.normalizePath handle edge cases like "a/b/../../.." differently.

securehash was moved to lib/deprecated .

The switch -d:useWinAnsi is not supported anymore.

In times module, procs format and parse accept a new optional DateTimeLocale argument for formatting/parsing dates in other languages.

Language additions

Vm support for float32<->int32 and float64<->int64 casts was added.

There is a new pragma block noSideEffect that works like the gcsafe pragma block.

that works like the pragma block. added os.getCurrentProcessId .

. User defined pragmas are now allowed in the pragma blocks.

Pragma blocks are no longer eliminated from the typed AST tree to preserve pragmas for further analysis by macros.

Custom pragmas are now supported for var and let symbols.

and symbols. Tuple unpacking is now supported for constants and for loop variables.

Case object branches can be initialized with a runtime discriminator if possible discriminator values are constrained within a case statement.

Language changes

The standard extension for SCF (source code filters) files was changed from .tmpl to .nimf , it’s more recognizable and allows tools like Github to recognize it as Nim, see #9647. The previous extension will continue to work.

Pragma syntax is now consistent. Previous syntax where type pragmas did not follow the type name is now deprecated. Also pragma before generic parameter list is deprecated to be consistent with how pragmas are used with a proc. See #8514 and #1872 for further details.

Hash sets and tables are initialized by default. The explicit initHashSet , initTable , etc. are not needed anymore.

Tool changes

jsondoc now includes a moduleDescription field with the module description. jsondoc0 shows comments as its own objects as shown in the documentation.

now includes a field with the module description. shows comments as its own objects as shown in the documentation. nimpretty : –backup now defaults to off instead of on and the flag was undocumented; use git instead of relying on backup files.

: –backup now defaults to instead of and the flag was undocumented; use instead of relying on backup files. koch now defaults to build the latest stable Nimble version unless you explicitly ask for the latest master version via --latest .

Compiler changes

The deprecated fmod proc is now unavailable on the VM.

proc is now unavailable on the VM. A new --outdir option was added.

option was added. The compiled JavaScript file for the project produced by executing nim js will no longer be placed in the nimcache directory.

will no longer be placed in the nimcache directory. The --hotCodeReloading has been implemented for the native targets. The compiler also provides a new more flexible API for handling the hot code reloading events in the code.

has been implemented for the native targets. The compiler also provides a new more flexible API for handling the hot code reloading events in the code. The compiler now supports a --expandMacro:macroNameHere switch for easy introspection into what a macro expands into.

switch for easy introspection into what a macro expands into. The -d:release switch now does not disable runtime checks anymore. For a release build that also disables runtime checks use -d:release -d:danger or simply -d:danger .

Bugfixes