diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 8406f607f27e0..0145138650503 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -71,5 +71,6 @@ body: which should give more context on a compiler crash. - If it's a regression, you can help us by identifying which version introduced the bug, see [Bisecting for regressions](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions), - or at least try known past releases (eg `choosenim 1.2.0`). + or at least try known past releases (e.g. `choosenim 2.0.0`). The Nim repo also supports online bisecting + via making a comment, which contains a code block starting by `!nim c`, `!nim js` etc. , see [nimrun-action](https://github.com/juancarlospaco/nimrun-action). - [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html) diff --git a/.github/workflows/bisects.yml b/.github/workflows/bisects.yml new file mode 100644 index 0000000000000..e73736d2996a2 --- /dev/null +++ b/.github/workflows/bisects.yml @@ -0,0 +1,26 @@ +# See https://github.com/juancarlospaco/nimrun-action/issues/3#issuecomment-1607344901 +name: issue comments bisects +on: + issue_comment: + types: created + +jobs: + bisects: + if: | + github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '!nim ') && github.event.issue.pull_request == null && github.event.comment.author_association != 'NONE' + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest, windows-latest, macos-latest] + name: ${{ matrix.platform }}-bisects + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v4 + + - uses: jiro4989/setup-nim-action@v1 + with: + nim-version: 'devel' + + - uses: juancarlospaco/nimrun-action@nim + with: + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci_bench.yml b/.github/workflows/ci_bench.yml index 68f0007228fe8..535b52355b44d 100644 --- a/.github/workflows/ci_bench.yml +++ b/.github/workflows/ci_bench.yml @@ -17,7 +17,7 @@ jobs: timeout-minutes: 60 # refs bug #18178 steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 @@ -60,7 +60,7 @@ jobs: run: nim c -r -d:release ci/action.nim - name: 'Checkout minimize' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: 'nim-lang/ci_bench' path: minimize diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index cde30e5fa8a58..32e8e050e4a00 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -2,8 +2,7 @@ name: Nim Docs CI on: push: paths: - - 'compiler/docgen.nim' - - 'compiler/renderverbatim.nim' + - 'compiler/**.nim' - 'config/nimdoc.cfg' - 'doc/**.rst' - 'doc/**.md' @@ -18,8 +17,7 @@ on: pull_request: # Run only on changes on these files. paths: - - 'compiler/docgen.nim' - - 'compiler/renderverbatim.nim' + - 'compiler/**.nim' - 'config/nimdoc.cfg' - 'doc/**.rst' - 'doc/**.md' @@ -55,7 +53,7 @@ jobs: steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index 0bec4cc212b87..1dabd60771402 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -28,7 +28,7 @@ jobs: NIM_TESTAMENT_BATCH: ${{ matrix.batch }} steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 diff --git a/.github/workflows/ci_publish.yml b/.github/workflows/ci_publish.yml index d6b628d8cef40..26c617a8e3e85 100644 --- a/.github/workflows/ci_publish.yml +++ b/.github/workflows/ci_publish.yml @@ -17,7 +17,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: 'Checkout' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 2 diff --git a/changelog.md b/changelog.md index 100d12fd7207d..72d0a2a2dd7a3 100644 --- a/changelog.md +++ b/changelog.md @@ -8,23 +8,33 @@ [//]: # "Changes:" +- Changed `std/osfiles.copyFile` to allow to specify `bufferSize` instead of a hardcoded one. +- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads. [//]: # "Additions:" +- Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content. +- Added `setLenUninit` to system, which doesn't initalize +slots when enlarging a sequence. +- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value. +- Added Viewport API for the JavaScript targets in the `dom` module. + [//]: # "Deprecations:" +- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`. [//]: # "Removals:" ## Language changes - +- `noInit` can be used in types and fields to disable member initializers in the C++ backend. ## Compiler changes - - +- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory. ## Tool changes +- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow. + diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index f82cfe7cc2aac..457cc62a6b9cc 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -1,548 +1,330 @@ -# v2.0.0 - yyyy-mm-dd +# v2.0.0 - 2023-08-01 +Version 2.0 is a big milestone with too many changes to list them all here. -## Changes affecting backward compatibility -- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously it raised `ValueError`. +For a full list see [details](changelog_2_0_0_details.html). -- `addr` is now available for all addressable locations, - `unsafeAddr` is now deprecated and an alias for `addr`. -- Certain definitions from the default `system` module have been moved to - the following new modules: +## New features - - `std/syncio` - - `std/assertions` - - `std/formatfloat` - - `std/objectdollar` - - `std/widestrs` - - `std/typedthreads` - - `std/sysatomics` +### Better tuple unpacking - In the future, these definitions will be removed from the `system` module, - and their respective modules will have to be imported to use them. - Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option - may be used. +Tuple unpacking for variables is now treated as syntax sugar that directly +expands into multiple assignments. Along with this, tuple unpacking for +variables can now be nested. -- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated - symbols in the `system` module: - - Aliases with `Error` suffix to exception types that have a `Defect` suffix - (see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)): - `ArithmeticError`, `DivByZeroError`, `OverflowError`, - `AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`, - `FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`, - `ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`, - `FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`, - `DeadThreadError`, `NilAccessError` - - `addQuitProc`, replaced by `exitprocs.addExitProc` - - Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32` - - `TaintedString`, formerly a distinct alias to `string` - - `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to - `ptr int32`, `ptr int64`, `ptr float32`, `ptr float64` +```nim +proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3) -- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in - in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command "nimble install threading" and import `threading/channels`. +# Now nesting is supported! +let (x, (_, y), _, z) = returnsNestedTuple() -- Enabling `-d:nimPreviewCstringConversion`, `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` don't support conversion to cstring anymore. +``` -- Enabling `-d:nimPreviewProcConversion`, `proc` does not support conversion to - `pointer`. `cast` may be used instead. +### Improved type inference -- The `gc:v2` option is removed. +A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) has been implemented for a variety of basic cases. -- The `mainmodule` and `m` options are removed. +For example, code like the following now compiles: -- The `threads:on` option is now the default. +```nim +let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")] +``` -- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via - `experimental:flexibleOptionalParams`. +### Forbidden Tags -- Automatic dereferencing (experimental feature) is removed. +[Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition +of forbidden tags by the `.forbids` pragma which can be used to disable certain effects in proc types. -- The `Math.trunc` polyfill for targeting Internet Explorer was - previously included in most JavaScript output files. - Now, it is only included with `-d:nimJsMathTruncPolyfill`. - If you are targeting Internet Explorer, you may choose to enable this option - or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma). - Nim uses `Math.trunc` for the division and modulo operators for integers. +For example: -- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and -`sink` for optimization purposes. +```nim -- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fullfill its promises. +type IO = object ## input/output effect +proc readLine(): string {.tags: [IO].} = discard +proc echoLine(): void = discard -- The `{.this.}` pragma, deprecated since 0.19, has been removed. -- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead. - ```nim - type Foo = distinct ptr int +proc no_IO_please() {.forbids: [IO].} = + # this is OK because it didn't define any tag: + echoLine() + # the compiler prevents this: + let y = readLine() - # Before: - var x: Foo = nil - # After: - var x: Foo = Foo(nil) - ``` -- Removed two type pragma syntaxes deprecated since 0.20, namely - `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. +``` -- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent - with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`. - This decision was made with the assumption that the old syntax was used rarely; - if your code used the old syntax, please be aware of this change. +### New standard library modules -- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators - are no longer experimental. +The famous `os` module got an overhaul. Several of its features are available +under a new interface that introduces a `Path` abstraction. A `Path` is +a `distinct string`, which improves the type safety when dealing with paths, files +and directories. -- Removed the `nimIncrSeqV3` define. +Use: -- `macros.getImpl` for `const` symbols now returns the full definition node - (as `nnkConstDef`) rather than the AST of the constant value. +- `std/oserrors` for OS error reporting. +- `std/envvars` for environment variables handling. +- `std/paths` for path handling. +- `std/dirs` for directory creation/deletion/traversal. +- `std/files` for file existence checking, file deletions and moves. +- `std/symlinks` for symlink handling. +- `std/appdirs` for accessing configuration/home/temp directories. +- `std/cmdline` for reading command line parameters. -- Lock levels are deprecated, now a noop. +### Consistent underscore handling -- ORC is now the default memory management strategy. Use - `--mm:refc` for a transition period. +The underscore identifier (`_`) is now generally not added to scope when +used as the name of a definition. While this was already the case for +variables, it is now also the case for routine parameters, generic +parameters, routine declarations, type declarations, etc. This means that the following code now does not compile: -- `strictEffects` are no longer experimental. - Use `legacy:laxEffects` to keep backward compatibility. +```nim +proc foo(_: int): int = _ + 1 +echo foo(1) -- The `gorge`/`staticExec` calls will now return a descriptive message in the output - if the execution fails for whatever reason. To get back legacy behaviour use `-d:nimLegacyGorgeErrors`. +proc foo[_](t: typedesc[_]): seq[_] = @[default(_)] +echo foo[int]() -- Pointer to `cstring` conversion now triggers a `[PtrToCstringConv]` warning. - This warning will become an error in future versions! Use a `cast` operation - like `cast[cstring](x)` instead. +proc _() = echo "_" +_() -- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`. +type _ = int +let x: _ = 3 +``` -- Redefining templates with the same signature was previously - allowed to support certain macro code. To do this explicitly, the - `{.redefine.}` pragma has been added. Note that this is only for templates. - Implicit redefinition of templates is now deprecated and will give an error in the future. +Whereas the following code now compiles: -- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. +```nim +proc foo(_, _: int): int = 123 +echo foo(1, 2) -- Several Standard libraries are moved to nimble packages, use `nimble` to install them: - - `std/punycode` => `punycode` - - `std/asyncftpclient` => `asyncftpclient` - - `std/smtp` => `smtp` - - `std/db_common` => `db_connector/db_common` - - `std/db_sqlite` => `db_connector/db_sqlite` - - `std/db_mysql` => `db_connector/db_mysql` - - `std/db_postgres` => `db_connector/db_postgres` - - `std/db_odbc` => `db_connector/db_odbc` - - `std/md5` => `checksums/md5` - - `std/sha1` => `checksums/sha1` +proc foo[_, _](): int = 123 +echo foo[int, bool]() -- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of - `foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`. - This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead. +proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U)) +echo foo(int, bool) -- When `--warning[BareExcept]:on` is enabled, if no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered. +proc _() = echo "one" +proc _() = echo "two" -- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection. +type _ = int +type _ = float +``` -- The underscore identifier (`_`) is now generally not added to scope when - used as the name of a definition. While this was already the case for - variables, it is now also the case for routine parameters, generic - parameters, routine declarations, type declarations, etc. This means that the following code now does not compile: +### JavaScript codegen improvement - ```nim - proc foo(_: int): int = _ + 1 - echo foo(1) +The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) +for 64-bit integer types (`int64` and `uint64`) by default. As this affects +JS code generation, code using these types to interface with the JS backend +may need to be updated. Note that `int` and `uint` are not affected. - proc foo[_](t: typedesc[_]): seq[_] = @[default(_)] - echo foo[int]() +For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint) +and in the case of potential bugs with the new implementation, the +old behavior is currently still supported with the command line option +`--jsbigint64:off`. - proc _() = echo "_" - _() - type _ = int - let x: _ = 3 - ``` +## Docgen improvements - Whereas the following code now compiles: +`Markdown` is now the default markup language of doc comments (instead +of the legacy `RstMarkdown` mode). In this release we begin to separate +RST and Markdown features to better follow specification of each +language, with the focus on Markdown development. +See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html). - ```nim - proc foo(_, _: int): int = 123 - echo foo(1, 2) +* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to + select the markup language mode in the doc comments of the current `.nim` + file for processing by `nim doc`: - proc foo[_, _](): int = 123 - echo foo[int, bool]() + 1. `Markdown` (default) is basically CommonMark (standard Markdown) + + some Pandoc Markdown features + some RST features that are missing + in our current implementation of CommonMark and Pandoc Markdown. + 2. `RST` closely follows the RST spec with few additional Nim features. + 3. `RstMarkdown` is a maximum mix of RST and Markdown features, which + is kept for the sake of compatibility and ease of migration. - proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U)) - echo foo(int, bool) +* Added separate `md2html` and `rst2html` commands for processing + standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`). - proc _() = echo "one" - proc _() = echo "two" +* Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links. +* Docgen now supports concise syntax for referencing Nim symbols: + instead of specifying HTML anchors directly one can use original + Nim symbol declarations (adding the aforementioned link brackets + `[...]` around them). + * To use this feature across modules, a new `importdoc` directive was added. + Using this feature for referencing also helps to ensure that links + (inside one module or the whole project) are not broken. +* Added support for RST & Markdown quote blocks (blocks starting with `>`). +* Added a popular Markdown definition lists extension. +* Added Markdown indented code blocks (blocks indented by >= 4 spaces). +* Added syntax for additional parameters to Markdown code blocks: - type _ = int - type _ = float - ``` - -- Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages. - -- The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) - for 64-bit integer types (`int64` and `uint64`) by default. As this affects - JS code generation, code using these types to interface with the JS backend - may need to be updated. Note that `int` and `uint` are not affected. + ```nim test="nim c $1" + ... + ``` - For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint) - and in the case of potential bugs with the new implementation, the - old behavior is currently still supported with the command line option - `--jsbigint64:off`. -- The `proc` and `iterator` type classes now respectively only match - procs and iterators. Previously both type classes matched any of - procs or iterators. +## C++ interop enhancements - ```nim - proc prc(): int = - 123 +Nim 2.0 takes C++ interop to the next level. With the new [virtual](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma and the extended [constructor](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma. +Now one can define constructors and virtual procs that maps to C++ constructors and virtual methods, allowing one to further customize +the interoperability. There is also extended support for the [codeGenDecl](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-codegendecl-pragma) pragma, so that it works on types. - iterator iter(): int = - yield 123 +It's a common pattern in C++ to use inheritance to extend a library. Some even use multiple inheritance as a mechanism to make interfaces. - proc takesProc[T: proc](x: T) = discard - proc takesIter[T: iterator](x: T) = discard +Consider the following example: - # always compiled: - takesProc(prc) - takesIter(iter) - # no longer compiles: - takesProc(iter) - takesIter(prc) - ``` - -- The `proc` and `iterator` type classes now accept a calling convention pragma - (i.e. `proc {.closure.}`) that must be shared by matching proc or iterator - types. Previously pragmas were parsed but discarded if no parameter list - was given. - - This is represented in the AST by an `nnkProcTy`/`nnkIteratorTy` node with - an `nnkEmpty` node in the place of the `nnkFormalParams` node, and the pragma - node in the same place as in a concrete `proc` or `iterator` type node. This - state of the AST may be unexpected to existing code, both due to the - replacement of the `nnkFormalParams` node as well as having child nodes - unlike other type class AST. - -- Signed integer literals in `set` literals now default to a range type of - `0..255` instead of `0..65535` (the maximum size of sets). - -- Case statements with else branches put before elif/of branches in macros - are rejected with "invalid order of case branches". - -- Destructors now default to `.raises: []` (i.e. destructors must not raise - unlisted exceptions) and explicitly raising destructors are implementation - defined behavior. - -- The very old, undocumented deprecated pragma statement syntax for - deprecated aliases is now a no-op. The regular deprecated pragma syntax is - generally sufficient instead. - - ```nim - # now does nothing: - {.deprecated: [OldName: NewName].} - - # instead use: - type OldName* {.deprecated: "use NewName instead".} = NewName - const oldName* {.deprecated: "use newName instead".} = newName - ``` - - `defined(nimalias)` can be used to check for versions when this syntax was - available; however since code that used this syntax is usually very old, - these deprecated aliases are likely not used anymore and it may make sense - to simply remove these statements. - -- `getProgramResult` and `setProgramResult` in `std/exitprocs` are no longer - declared when they are not available on the backend. Previously it would call - `doAssert false` at runtime despite the condition being compile-time. - -- Custom destructors now supports non-var parameters, e.g. `proc =destroy[T: object](x: T)` is valid. `proc =destroy[T: object](x: var T)` is deprecated. - -- Relative imports will not resolve to searched paths anymore, e.g. `import ./tables` now reports an error properly. - -## Standard library additions and changes - -[//]: # "Changes:" -- OpenSSL 3 is now supported. -- `macros.parseExpr` and `macros.parseStmt` now accept an optional - filename argument for more informative errors. -- Module `colors` expanded with missing colors from the CSS color standard. - `colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard. -- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). -- The `md5` module now works at compile time and in JavaScript. -- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef` to support `const` tables. -- `strutils.find` now uses and defaults to `last = -1` for whole string searches, - making limiting it to just the first char (`last = 0`) valid. -- `strutils.split` and `strutils.rsplit` now return a source string as a single element for an empty separator. -- `random.rand` now works with `Ordinal`s. -- Undeprecated `os.isvalidfilename`. -- `std/oids` now uses `int64` to store time internally (before it was int32). -- `std/uri.Uri` dollar `$` improved, precalculates the `string` result length from the `Uri`. -- `std/uri.Uri.isIpv6` is now exported. -- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2. - - -- `std/net.IpAddress` dollar `$` improved, uses a fixed capacity for the `string` result based from the `IpAddressFamily`. -- `std/jsfetch.newFetchOptions` now has default values for all parameters -- `std/jsformdata` now accepts `Blob` data type. - -- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433). - -- New compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on linux `getrandom` syscall. - - This compile flag only affects linux builds and is necessary if either compiling on a linux kernel version < 3.17, or if code built will be executing on kernel < 3.17. - - On linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall. - - When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be: - ```sh - $ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details - $ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8 - $ sh build.sh # per unix install instructions - $ bin/nim c koch # per unix install instructions - $ ./koch boot -d:release # per unix install instructions - $ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall - ``` - - This is necessary to pass when building nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for stdlib (sysrand in particular). - -[//]: # "Additions:" -- Added ISO 8601 week date utilities in `times`: - - Added `IsoWeekRange`, a range type for weeks in a week-based year. - - Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year. - - Added a `initDateTime` overload to create a datetime from an ISO week date. - - Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime. - - Added `getIsoWeeksInYear` to return the number of weeks in a week-based year. -- Added new modules which were part of `std/os`: - - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. - - Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`. - - Added `std/cmdline` for reading command line parameters. -- Added `sep` parameter in `std/uri` to specify the query separator. -- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`. -- Added `complex.sgn` for obtaining the phase of complex numbers. -- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`, - `after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`, - `hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`, - `replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`, - `toggleAttribute`, and `matches` to `std/dom`. -- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices) -- Added `capacity` for `string` and `seq` to return the current capacity, see https://github.com/nim-lang/RFCs/issues/460 -- Added `openArray[char]` overloads for `std/parseutils` allowing more code reuse. -- Added `openArray[char]` overloads for `std/unicode` allowing more code reuse. -- Added `safe` parameter to `base64.encodeMime`. -- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes. -- Added `minmax` to `sequtils`, as a more efficient `(min(_), max(_))` over sequences. -- `std/jscore` for JavaScript targets: - + Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) - and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask). - + Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`. -- Added `BackwardsIndex` overload for `CacheSeq`. -- Added support for nested `with` blocks in `std/with`. - - -[//]: # "Deprecations:" -- Deprecated `selfExe` for Nimscript. -- Deprecated `std/sums`. -- Deprecated `std/base64.encode` for collections of arbitrary integer element type. - Now only `byte` and `char` are supported. - -[//]: # "Removals:" -- Removed deprecated module `parseopt2`. -- Removed deprecated module `sharedstrings`. -- Removed deprecated module `dom_extensions`. -- Removed deprecated module `LockFreeHash`. -- Removed deprecated module `events`. -- Removed deprecated `oids.oidToString`. -- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. -- Removed deprecated `jsre.test` and `jsre.toString`. -- Removed deprecated `math.c_frexp`. -- Removed deprecated `` httpcore.`==` ``. -- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. -- Removed deprecated `osproc.poDemon`, symbol with typo. -- Removed deprecated `tables.rightSize`. - - -- Removed deprecated `posix.CLONE_STOPPED`. - - -## Language changes - -- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition of forbidden tags by the `.forbids` pragma - which can be used to disable certain effects in proc types. -- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental, - meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. -- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed - for the right-hand side of type definitions in type sections. Previously - they would error with "invalid indentation". - -- Compile-time define changes: - - `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`. - In the command line, this is defined as `-d:a.b.c`. Older versions can - use accents as in ``defined(`a.b.c`)`` to access such defines. - - [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas) - now support a string argument for qualified define names. - - ```nim - # -d:package.FooBar=42 - const FooBar {.intdefine: "package.FooBar".}: int = 5 - echo FooBar # 42 - ``` - - This was added to help disambiguate similar define names for different packages. - In older versions, this could only be achieved with something like the following: - - ```nim - const FooBar = block: - const `package.FooBar` {.intdefine.}: int = 5 - `package.FooBar` - ``` - - A generic `define` pragma for constants has been added that interprets - the value of the define based on the type of the constant value. - See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-nimdefine-pragma) - for a list of supported types. - -- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes: - - Templates now accept macro pragmas. - - Macro pragmas for var/let/const sections have been redesigned in a way that works - similarly to routine macro pragmas. The new behavior is documented in the - [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas). - - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, - allowing multiple type definitions to be injected in place of the original type definition. - - ```nim - import macros - macro multiply(amount: static int, s: untyped): untyped = - let name = $s[0].basename - result = newNimNode(nnkTypeSection) - for i in 1 .. amount: - result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2])) - type - Foo = object - Bar {.multiply: 3.} = object - x, y, z: int - Baz = object - # becomes - type - Foo = object - Bar1 = object - x, y, z: int - Bar2 = object - x, y, z: int - Bar3 = object - x, y, z: int - Baz = object - ``` - -- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) - has been implemented for a variety of basic cases. For example, code like the following now compiles: - - ```nim - let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")] - ``` - -- `cstring` is now accepted as a selector in `case` statements, removing the - need to convert to `string`. On the JS backend, this is translated directly - to a `switch` statement. - -- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters). -- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`. - -- IBM Z architecture and macOS m1 arm64 architecture are supported. - -- `=wasMoved` can be overridden by users. - -- Tuple unpacking for variables is now treated as syntax sugar that directly - expands into multiple assignments. Along with this, tuple unpacking for - variables can now be nested. - - ```nim - proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3) - - let (x, (_, y), _, z) = returnsNestedTuple() - # roughly becomes - let - tmpTup1 = returnsNestedTuple() - x = tmpTup1[0] - tmpTup2 = tmpTup1[1] - y = tmpTup2[1] - z = tmpTup1[3] - ``` - - As a result `nnkVarTuple` nodes in variable sections will no longer be - reflected in `typed` AST. - -- C++ interoperability: - - New [`virtual`](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma added. - - Improvements to [`constructor`](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma. - -## Compiler changes - -- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the - reality better. (Nim moved away from all techniques based on "tracing".) - -- Defines the `gcRefc` symbol which allows writing specific code for the refc GC. - -- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`, - without requiring `-d:nimVersion140` which is now a noop. - -- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package. - -- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and - related functions produced on the backend. This prevents conflicts with other Nim - static libraries. - -- When compiling for Release the flag `-fno-math-errno` is used for GCC. -- Removed deprecated `LineTooLong` hint. -- Line numbers and filenames of source files work correctly inside templates for JavaScript targets. - -- Removed support for LCC (Local C), Pelles C, Digital Mars, Watcom compilers. - - -## Docgen - -- `Markdown` is now default markup language of doc comments (instead - of legacy `RstMarkdown` mode). In this release we begin to separate - RST and Markdown features to better follow specification of each - language, with the focus on Markdown development. - - * So we add `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to - select the markup language mode in the doc comments of current `.nim` - file for processing by `nim doc`: - - 1. `Markdown` (default) is basically CommonMark (standard Markdown) + - some Pandoc Markdown features + some RST features that are missing - in our current implementation of CommonMark and Pandoc Markdown. - 2. `RST` closely follows RST spec with few additional Nim features. - 3. `RstMarkdown` is a maximum mix of RST and Markdown features, which - is kept for the sake of compatibility and ease of migration. +```cpp - * and we add separate `md2html` and `rst2html` commands for processing - standalone `.md` and `.rst` files respectively (and also `md2tex/rst2tex`). +struct Base { + int someValue; + Base(int inValue) { + someValue = inValue; + }; +}; -- Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links. -- Docgen now supports concise syntax for referencing Nim symbols: - instead of specifying HTML anchors directly one can use original - Nim symbol declarations (adding the aforementioned link brackets - `[...]` around them). - * to use this feature across modules a new `importdoc` directive is added. - Using this feature for referencing also helps to ensure that links - (inside one module or the whole project) are not broken. -- Added support for RST & Markdown quote blocks (blocks starting from `>`). -- Added a popular Markdown definition lists extension. -- Added Markdown indented code blocks (blocks indented by >= 4 spaces). -- Added syntax for additional parameters to Markdown code blocks: +class IPrinter { +public: + virtual void print() = 0; +}; +``` + +```nim + +type + Base* {.importcpp, inheritable.} = object + someValue*: int32 + IPrinter* {.importcpp.} = object + +const objTemplate = """ + struct $1 : public $3, public IPrinter { + $2 + }; +"""; + +type NimChild {.codegenDecl: objTemplate .} = object of Base + +proc makeNimChild(val: int32): NimChild {.constructor: "NimClass('1 #1) : Base(#1)".} = + echo "It calls the base constructor passing " & $this.someValue + this.someValue = val * 2 # Notice how we can access `this` inside the constructor. It's of the type `ptr NimChild`. + +proc print*(self: NimChild) {.virtual.} = + echo "Some value is " & $self.someValue + +let child = makeNimChild(10) +child.print() +``` + +It outputs: + +``` +It calls the base constructor passing 10 +Some value is 20 +``` + + +## ARC/ORC refinements + +With the 2.0 release, the ARC/ORC model got refined once again and is now finally complete: + +1. Programmers now have control over the "item was moved from" state as `=wasMoved` is overridable. +2. There is a new `=dup` hook which is more efficient than the old combination of `=wasMoved(tmp); =copy(tmp, x)` operations. +3. Destructors now take a parameter of the attached object type `T` directly and don't have to take a `var T` parameter. + +With these important optimizations we improved the runtime of the compiler and important benchmarks by 0%! Wait ... what? +Yes, unfortunately it turns out that for a modern optimizer like in GCC or LLVM there is no difference. + +But! This refined model is more efficient once separate compilation enters the picture. In other words, as we think of +providing a stable ABI it is important not to lose any efficiency in the calling conventions. - ```nim test="nim c $1" - ... - ``` ## Tool changes -- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs`). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop. -- nimgrep added option `--inContext` (and `--notInContext`), which - allows to filter only matches with context block containing a given pattern. +- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop. +- nimgrep now offers the option `--inContext` (and `--notInContext`), which + allows to filter only matches with the context block containing a given pattern. - nimgrep: names of options containing "include/exclude" are deprecated, e.g. instead of `--includeFile` and `--excludeFile` we have `--filename` and `--notFilename` respectively. - Also the semantics become consistent for such positive/negative filters. -- koch now supports the `--skipIntegrityCheck` option. The command `koch --skipIntegrityCheck boot -d:release` always builds the compiler twice. + Also, the semantics are now consistent for such positive/negative filters. +- Nim now ships with an alternative package manager called Atlas. More on this in upcoming versions. + + +## Porting guide + +### Block and Break + +Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. In other words, turn: + +```nim + +block: + a() + if cond: + break + b() + +``` + +Into: + +```nim + +block maybePerformB: + a() + if cond: + break maybePerformB + b() + +``` + +### Strict funcs + +The definition of `"strictFuncs"` was changed. +The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter". +The new definition is: "A store to a ref/ptr deref is forbidden." + +This new definition is much easier to understand, the price is some expressitivity. The following code used to be +accepted: + +```nim + +{.experimental: "strictFuncs".} + +type Node = ref object + s: string + +func create(s: string): Node = + result = Node() + result.s = s # store to result[] + +``` + +Now it has to be rewritten to: + +```nim + +{.experimental: "strictFuncs".} + +type Node = ref object + s: string + +func create(s: string): Node = + result = Node(s: s) + +``` + +### Standard library + +Several standard library modules have been moved to nimble packages, use `nimble` or `atlas` to install them: + +- `std/punycode` => `punycode` +- `std/asyncftpclient` => `asyncftpclient` +- `std/smtp` => `smtp` +- `std/db_common` => `db_connector/db_common` +- `std/db_sqlite` => `db_connector/db_sqlite` +- `std/db_mysql` => `db_connector/db_mysql` +- `std/db_postgres` => `db_connector/db_postgres` +- `std/db_odbc` => `db_connector/db_odbc` +- `std/md5` => `checksums/md5` +- `std/sha1` => `checksums/sha1` +- `std/sums` => `sums` diff --git a/changelogs/changelog_2_0_0_details.md b/changelogs/changelog_2_0_0_details.md new file mode 100644 index 0000000000000..a38f2b40b7015 --- /dev/null +++ b/changelogs/changelog_2_0_0_details.md @@ -0,0 +1,560 @@ +# v2.0.0 - 2023-08-01 + + +## Changes affecting backward compatibility + +- ORC is now the default memory management strategy. Use + `--mm:refc` for a transition period. + +- The `threads:on` option is now the default. + +- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache's `HttpClient` (Java), `http` (go) and .NET `HttpWebResponse` (C#) behaviors. Previously it raised a `ValueError`. + +- `addr` is now available for all addressable locations, + `unsafeAddr` is now deprecated and an alias for `addr`. + +- Certain definitions from the default `system` module have been moved to + the following new modules: + + - `std/syncio` + - `std/assertions` + - `std/formatfloat` + - `std/objectdollar` + - `std/widestrs` + - `std/typedthreads` + - `std/sysatomics` + + In the future, these definitions will be removed from the `system` module, + and their respective modules will have to be imported to use them. + Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option + may be used. + +- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated + symbols in the `system` module: + - Aliases with an `Error` suffix to exception types that have a `Defect` suffix + (see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)): + `ArithmeticError`, `DivByZeroError`, `OverflowError`, + `AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`, + `FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`, + `ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`, + `FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`, + `DeadThreadError`, `NilAccessError` + - `addQuitProc`, replaced by `exitprocs.addExitProc` + - Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32` + - `TaintedString`, formerly a distinct alias to `string` + - `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to + `ptr int32`, `ptr int64`, `ptr float32`, `ptr float64` + +- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in + in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command `nimble install threading` and import `threading/channels`. + +- Enabling `-d:nimPreviewCstringConversion` causes `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` to not support conversion to `cstring` anymore. + +- Enabling `-d:nimPreviewProcConversion` causes `proc` to not support conversion to + `pointer` anymore. `cast` may be used instead. + +- The `gc:v2` option is removed. + +- The `mainmodule` and `m` options are removed. + +- Optional parameters in combination with `: body` syntax ([RFC #405](https://github.com/nim-lang/RFCs/issues/405)) + are now opt-in via `experimental:flexibleOptionalParams`. + +- Automatic dereferencing (experimental feature) is removed. + +- The `Math.trunc` polyfill for targeting Internet Explorer was + previously included in most JavaScript output files. + Now, it is only included with `-d:nimJsMathTruncPolyfill`. + If you are targeting Internet Explorer, you may choose to enable this option + or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma). + Nim uses `Math.trunc` for the division and modulo operators for integers. + +- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and +`sink` for optimization purposes. + +- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fullfill its promises. + +- The `{.this.}` pragma, deprecated since 0.19, has been removed. +- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead. + ```nim + type Foo = distinct ptr int + + # Before: + var x: Foo = nil + # After: + var x: Foo = Foo(nil) + ``` +- Removed two type pragma syntaxes deprecated since 0.20, namely + `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. Instead, + use `type Foo[T] {.final.} = object`. + +- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent + with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`. + This decision was made with the assumption that the old syntax was used rarely; + if your code used the old syntax, please be aware of this change. + +- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators + are no longer experimental. + +- `macros.getImpl` for `const` symbols now returns the full definition node + (as `nnkConstDef`) rather than the AST of the constant value. + +- Lock levels are deprecated, now a noop. + +- `strictEffects` are no longer experimental. + Use `legacy:laxEffects` to keep backward compatibility. + +- The `gorge`/`staticExec` calls will now return a descriptive message in the output + if the execution fails for whatever reason. To get back legacy behaviour, use `-d:nimLegacyGorgeErrors`. + +- Pointer to `cstring` conversions now trigger a `[PtrToCstringConv]` warning. + This warning will become an error in future versions! Use a `cast` operation + like `cast[cstring](x)` instead. + +- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`. + +- Redefining templates with the same signature was previously + allowed to support certain macro code. To do this explicitly, the + `{.redefine.}` pragma has been added. Note that this is only for templates. + Implicit redefinition of templates is now deprecated and will give an error in the future. + +- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. + +- Several Standard libraries have been moved to nimble packages, use `nimble` to install them: + - `std/punycode` => `punycode` + - `std/asyncftpclient` => `asyncftpclient` + - `std/smtp` => `smtp` + - `std/db_common` => `db_connector/db_common` + - `std/db_sqlite` => `db_connector/db_sqlite` + - `std/db_mysql` => `db_connector/db_mysql` + - `std/db_postgres` => `db_connector/db_postgres` + - `std/db_odbc` => `db_connector/db_odbc` + - `std/md5` => `checksums/md5` + - `std/sha1` => `checksums/sha1` + - `std/sums` => `std/sums` + +- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of + `foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`. + This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead. + +- When `--warning[BareExcept]:on` is enabled, if an `except` specifies no exception or any exception not inheriting from `Defect` or `CatchableError`, a `warnBareExcept` warning will be triggered. For example, the following code will emit a warning: + ```nim + try: + discard + except: # Warning: The bare except clause is deprecated; use `except CatchableError:` instead [BareExcept] + discard + ``` + +- The experimental `strictFuncs` feature now disallows a store to the heap via a `ref` or `ptr` indirection. + +- The underscore identifier (`_`) is now generally not added to scope when + used as the name of a definition. While this was already the case for + variables, it is now also the case for routine parameters, generic + parameters, routine declarations, type declarations, etc. This means that the following code now does not compile: + + ```nim + proc foo(_: int): int = _ + 1 + echo foo(1) + + proc foo[_](t: typedesc[_]): seq[_] = @[default(_)] + echo foo[int]() + + proc _() = echo "_" + _() + + type _ = int + let x: _ = 3 + ``` + + Whereas the following code now compiles: + + ```nim + proc foo(_, _: int): int = 123 + echo foo(1, 2) + + proc foo[_, _](): int = 123 + echo foo[int, bool]() + + proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U)) + echo foo(int, bool) + + proc _() = echo "one" + proc _() = echo "two" + + type _ = int + type _ = float + ``` + +- Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages. + +- The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) + for 64-bit integer types (`int64` and `uint64`) by default. As this affects + JS code generation, code using these types to interface with the JS backend + may need to be updated. Note that `int` and `uint` are not affected. + + For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint) + and in the case of potential bugs with the new implementation, the + old behavior is currently still supported with the command line option + `--jsbigint64:off`. + +- The `proc` and `iterator` type classes now respectively only match + procs and iterators. Previously both type classes matched any of + procs or iterators. + + ```nim + proc prc(): int = + 123 + + iterator iter(): int = + yield 123 + + proc takesProc[T: proc](x: T) = discard + proc takesIter[T: iterator](x: T) = discard + + # always compiled: + takesProc(prc) + takesIter(iter) + # no longer compiles: + takesProc(iter) + takesIter(prc) + ``` + +- The `proc` and `iterator` type classes now accept a calling convention pragma + (i.e. `proc {.closure.}`) that must be shared by matching proc or iterator + types. Previously, pragmas were parsed but discarded if no parameter list + was given. + + This is represented in the AST by an `nnkProcTy`/`nnkIteratorTy` node with + an `nnkEmpty` node in the place of the `nnkFormalParams` node, and the pragma + node in the same place as in a concrete `proc` or `iterator` type node. This + state of the AST may be unexpected to existing code, both due to the + replacement of the `nnkFormalParams` node as well as having child nodes + unlike other type class AST. + +- Signed integer literals in `set` literals now default to a range type of + `0..255` instead of `0..65535` (the maximum size of sets). + +- `case` statements with `else` branches put before `elif`/`of` branches in macros + are rejected with "invalid order of case branches". + +- Destructors now default to `.raises: []` (i.e. destructors must not raise + unlisted exceptions) and explicitly raising destructors are implementation + defined behavior. + +- The very old, undocumented `deprecated` pragma statement syntax for + deprecated aliases is now a no-op. The regular deprecated pragma syntax is + generally sufficient instead. + + ```nim + # now does nothing: + {.deprecated: [OldName: NewName].} + + # instead use: + type OldName* {.deprecated: "use NewName instead".} = NewName + const oldName* {.deprecated: "use newName instead".} = newName + ``` + + `defined(nimalias)` can be used to check for versions when this syntax was + available; however since code that used this syntax is usually very old, + these deprecated aliases are likely not used anymore and it may make sense + to simply remove these statements. + +- `getProgramResult` and `setProgramResult` in `std/exitprocs` are no longer + declared when they are not available on the backend. Previously it would call + `doAssert false` at runtime despite the condition being checkable at compile-time. + +- Custom destructors now supports non-var parameters, e.g. ``proc `=destroy`[T: object](x: T)`` is valid. ``proc `=destroy`[T: object](x: var T)`` is deprecated. + +- Relative imports will not resolve to searched paths anymore, e.g. `import ./tables` now reports an error properly. + +## Standard library additions and changes + +[//]: # "Changes:" +- OpenSSL 3 is now supported. +- `macros.parseExpr` and `macros.parseStmt` now accept an optional + `filename` argument for more informative errors. +- The `colors` module is expanded with missing colors from the CSS color standard. + `colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard. +- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). +- The `md5` module now works at compile time and in JavaScript. +- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef`, to support `const` tables. +- `strutils.find` now uses and defaults to `last = -1` for whole string searches, + making limiting it to just the first char (`last = 0`) valid. +- `strutils.split` and `strutils.rsplit` now return the source string as a single element for an empty separator. +- `random.rand` now works with `Ordinal`s. +- Undeprecated `os.isvalidfilename`. +- `std/oids` now uses `int64` to store time internally (before, it was int32). +- `std/uri.Uri` dollar (`$`) improved, precalculates the `string` result length from the `Uri`. +- `std/uri.Uri.isIpv6` is now exported. +- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2. + + +- `std/jsfetch.newFetchOptions` now has default values for all parameters. +- `std/jsformdata` now accepts the `Blob` data type. + +- `std/sharedlist` and `std/sharedtables` are now deprecated, see [RFC #433](https://github.com/nim-lang/RFCs/issues/433). + +- There is a new compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove the dependency on the Linux `getrandom` syscall. + + This compile flag only affects Linux builds and is necessary if either compiling on a Linux kernel version < 3.17, or if code built will be executing on kernel < 3.17. + + On Linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since Linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall. + + When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be: + ```sh + $ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details + $ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8 + $ sh build.sh # per unix install instructions + $ bin/nim c koch # per unix install instructions + $ ./koch boot -d:release # per unix install instructions + $ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall + ``` + + This is necessary to pass when building Nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for the stdlib (`sysrand` in particular). + +[//]: # "Additions:" +- Added ISO 8601 week date utilities in `times`: + - Added `IsoWeekRange`, a range type for weeks in a week-based year. + - Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year. + - Added an `initDateTime` overload to create a `DateTime` from an ISO week date. + - Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime. + - Added `getIsoWeeksInYear` to return the number of weeks in a week-based year. +- Added new modules which were previously part of `std/os`: + - Added `std/oserrors` for OS error reporting. + - Added `std/envvars` for environment variables handling. + - Added `std/cmdline` for reading command line parameters. + - Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`. +- Added `sep` parameter in `std/uri` to specify the query separator. +- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`. +- Added `complex.sgn` for obtaining the phase of complex numbers. +- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`, + `after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`, + `hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`, + `replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`, + `toggleAttribute`, and `matches` to `std/dom`. +- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices). +- Added `capacity` for `string` and `seq` to return the current capacity, see [RFC #460](https://github.com/nim-lang/RFCs/issues/460). +- Added `openArray[char]` overloads for `std/parseutils` and `std/unicode`, allowing for more code reuse. +- Added a `safe` parameter to `base64.encodeMime`. +- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes. +- Added `minmax` to `sequtils`, as a more efficient `(min(_), max(_))` over sequences. +- `std/jscore` for the JavaScript target: + + Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) + and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask). + + Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`. +- Added `BackwardsIndex` overload for `CacheSeq`. +- Added support for nested `with` blocks in `std/with`. +- Added `ensureMove` to the system module. It ensures that the passed argument is moved, otherwise an error is given at the compile time. + + +[//]: # "Deprecations:" +- Deprecated `selfExe` for Nimscript. +- Deprecated `std/base64.encode` for collections of arbitrary integer element type. + Now only `byte` and `char` are supported. + +[//]: # "Removals:" +- Removed deprecated module `parseopt2`. +- Removed deprecated module `sharedstrings`. +- Removed deprecated module `dom_extensions`. +- Removed deprecated module `LockFreeHash`. +- Removed deprecated module `events`. +- Removed deprecated `oids.oidToString`. +- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. +- Removed deprecated `jsre.test` and `jsre.toString`. +- Removed deprecated `math.c_frexp`. +- Removed deprecated `` httpcore.`==` ``. +- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that take wrong argument types. +- Removed deprecated `osproc.poDemon`, symbol with typo. +- Removed deprecated `tables.rightSize`. + + +- Removed deprecated `posix.CLONE_STOPPED`. + + +## Language changes + +- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition of forbidden tags by the `.forbids` pragma + which can be used to disable certain effects in proc types. +- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental, + meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. +- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed + for the right-hand side of type definitions in type sections. Previously + they would error with "invalid indentation". + +- Compile-time define changes: + - `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`. + In the command line, this is defined as `-d:a.b.c`. Older versions can + use backticks as in ``defined(`a.b.c`)`` to access such defines. + - [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas) + now support a string argument for qualified define names. + + ```nim + # -d:package.FooBar=42 + const FooBar {.intdefine: "package.FooBar".}: int = 5 + echo FooBar # 42 + ``` + + This was added to help disambiguate similar define names for different packages. + In older versions, this could only be achieved with something like the following: + + ```nim + const FooBar = block: + const `package.FooBar` {.intdefine.}: int = 5 + `package.FooBar` + ``` + - A generic `define` pragma for constants has been added that interprets + the value of the define based on the type of the constant value. + See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-nimdefine-pragma) + for a list of supported types. + +- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes: + - Templates now accept macro pragmas. + - Macro pragmas for var/let/const sections have been redesigned in a way that works + similarly to routine macro pragmas. The new behavior is documented in the + [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas). + - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, + allowing multiple type definitions to be injected in place of the original type definition. + + ```nim + import macros + + macro multiply(amount: static int, s: untyped): untyped = + let name = $s[0].basename + result = newNimNode(nnkTypeSection) + for i in 1 .. amount: + result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2])) + + type + Foo = object + Bar {.multiply: 3.} = object + x, y, z: int + Baz = object + # becomes + type + Foo = object + Bar1 = object + x, y, z: int + Bar2 = object + x, y, z: int + Bar3 = object + x, y, z: int + Baz = object + ``` + +- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) + has been implemented for a variety of basic cases. For example, code like the following now compiles: + + ```nim + let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")] + ``` + +- `cstring` is now accepted as a selector in `case` statements, removing the + need to convert to `string`. On the JS backend, this is translated directly + to a `switch` statement. + +- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters). +- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`. + +- IBM Z architecture and macOS m1 arm64 architecture are supported. + +- `=wasMoved` can now be overridden by users. + +- There is a new pragma called [quirky](https://nim-lang.github.io/Nim/manual_experimental.html#quirky-routines) that can be used to affect the code + generation of goto based exception handling. It can improve the produced code size but its effects can be subtle so use it with care. + +- Tuple unpacking for variables is now treated as syntax sugar that directly + expands into multiple assignments. Along with this, tuple unpacking for + variables can now be nested. + + ```nim + proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3) + + let (x, (_, y), _, z) = returnsNestedTuple() + # roughly becomes + let + tmpTup1 = returnsNestedTuple() + x = tmpTup1[0] + tmpTup2 = tmpTup1[1] + y = tmpTup2[1] + z = tmpTup1[3] + ``` + + As a result `nnkVarTuple` nodes in variable sections will no longer be + reflected in `typed` AST. + +- C++ interoperability: + - New [`virtual`](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma added. + - Improvements to [`constructor`](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma. + +## Compiler changes + +- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the + reality better. (Nim moved away from all techniques based on "tracing".) + +- Defines the `gcRefc` symbol which allows writing specific code for the refc GC. + +- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`, + without requiring `-d:nimVersion140` which is now a noop. + +- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package. + +- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and + related functions produced on the backend. This prevents conflicts with other Nim + static libraries. + +- When compiling for release, the flag `-fno-math-errno` is used for GCC. +- Removed deprecated `LineTooLong` hint. +- Line numbers and file names of source files work correctly inside templates for JavaScript targets. + +- Removed support for LCC (Local C), Pelles C, Digital Mars and Watcom compilers. + + +## Docgen + +- `Markdown` is now the default markup language of doc comments (instead + of the legacy `RstMarkdown` mode). In this release we begin to separate + RST and Markdown features to better follow specification of each + language, with the focus on Markdown development. + See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html). + + * Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to + select the markup language mode in the doc comments of the current `.nim` + file for processing by `nim doc`: + + 1. `Markdown` (default) is basically CommonMark (standard Markdown) + + some Pandoc Markdown features + some RST features that are missing + in our current implementation of CommonMark and Pandoc Markdown. + 2. `RST` closely follows the RST spec with few additional Nim features. + 3. `RstMarkdown` is a maximum mix of RST and Markdown features, which + is kept for the sake of compatibility and ease of migration. + + * Added separate `md2html` and `rst2html` commands for processing + standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`). + +- Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links. +- Docgen now supports concise syntax for referencing Nim symbols: + instead of specifying HTML anchors directly one can use original + Nim symbol declarations (adding the aforementioned link brackets + `[...]` around them). + * To use this feature across modules, a new `importdoc` directive was added. + Using this feature for referencing also helps to ensure that links + (inside one module or the whole project) are not broken. +- Added support for RST & Markdown quote blocks (blocks starting with `>`). +- Added a popular Markdown definition lists extension. +- Added Markdown indented code blocks (blocks indented by >= 4 spaces). +- Added syntax for additional parameters to Markdown code blocks: + + ```nim test="nim c $1" + ... + ``` + +## Tool changes + +- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop. +- nimgrep added the option `--inContext` (and `--notInContext`), which + allows to filter only matches with the context block containing a given pattern. +- nimgrep: names of options containing "include/exclude" are deprecated, + e.g. instead of `--includeFile` and `--excludeFile` we have + `--filename` and `--notFilename` respectively. + Also the semantics are now consistent for such positive/negative filters. +- koch now supports the `--skipIntegrityCheck` option. The command `koch --skipIntegrityCheck boot -d:release` always builds the compiler twice. diff --git a/changelogs/changelog_2_2_0.md b/changelogs/changelog_2_2_0.md new file mode 100644 index 0000000000000..0a293d35fbd14 --- /dev/null +++ b/changelogs/changelog_2_2_0.md @@ -0,0 +1,12 @@ +# v2.2.0 - 2023-mm-dd + +## Changes affecting backward compatibility + +## Standard library additions and changes + +## Language changes + +## Compiler changes + +## Tool changes + diff --git a/ci/action.nim b/ci/action.nim index 5d3a50fda24d9..ad0f4df0b65ff 100644 --- a/ci/action.nim +++ b/ci/action.nim @@ -3,7 +3,7 @@ import std/[strutils, os, osproc, parseutils, strformat] proc main() = var msg = "" - const cmd = "./koch boot --gc:orc -d:release" + const cmd = "./koch boot --mm:orc -d:release" let (output, exitCode) = execCmdEx(cmd) diff --git a/compiler/aliasanalysis.nim b/compiler/aliasanalysis.nim index f14b8152438e7..e24c6d8e260c0 100644 --- a/compiler/aliasanalysis.nim +++ b/compiler/aliasanalysis.nim @@ -74,7 +74,7 @@ proc aliases*(obj, field: PNode): AliasKind = # x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant # x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant template collectImportantNodes(result, n) = - var result: seq[PNode] + var result: seq[PNode] = @[] var n = n while true: case n.kind diff --git a/compiler/aliases.nim b/compiler/aliases.nim index 4b50fdb28294a..fa9824c41e472 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -114,6 +114,8 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = # use expensive type check: if isPartOf(a.sym.typ, b.sym.typ) != arNo: result = arMaybe + else: + result = arNo of nkBracketExpr: result = isPartOf(a[0], b[0]) if a.len >= 2 and b.len >= 2: @@ -149,7 +151,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = result = isPartOf(a[1], b[1]) of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: result = isPartOf(a[0], b[0]) - else: discard + else: result = arNo # Calls return a new location, so a default of ``arNo`` is fine. else: # go down recursively; this is quite demanding: @@ -165,6 +167,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = of DerefKinds: # a* !<| b[] iff + result = arNo if isPartOf(a.typ, b.typ) != arNo: result = isPartOf(a, b[0]) if result == arNo: result = arMaybe @@ -186,7 +189,9 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = if isPartOf(a.typ, b.typ) != arNo: result = isPartOf(a[0], b) if result == arNo: result = arMaybe - else: discard + else: + result = arNo + else: result = arNo of nkObjConstr: result = arNo for i in 1.. 0: result = isPartOf(a, b[0]) - else: discard + else: + result = arNo + else: result = arNo diff --git a/compiler/ast.nim b/compiler/ast.nim index 0be26039149bb..3017aedcf6e83 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -10,7 +10,7 @@ # abstract syntax tree + symbol table import - lineinfos, hashes, options, ropes, idents, int128, tables + lineinfos, hashes, options, ropes, idents, int128, tables, wordrecg from strutils import toLowerAscii when defined(nimPreviewSlimSystem): @@ -314,6 +314,8 @@ type # an infinite loop, this flag is used as a sentinel to stop it. sfVirtual # proc is a C++ virtual function sfByCopy # param is marked as pass bycopy + sfMember # proc is a C++ member of a type + sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl TSymFlags* = set[TSymFlag] @@ -346,6 +348,7 @@ const sfBase* = sfDiscriminant sfCustomPragma* = sfRegister # symbol is custom pragma template sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template + sfCppMember* = { sfVirtual, sfMember, sfConstructor } # proc is a C++ member, meaning it will be attached to the type definition const # getting ready for the future expr/stmt merge @@ -588,6 +591,7 @@ type tfEffectSystemWorkaround tfIsOutParam tfSendable + tfImplicitStatic TTypeFlags* = set[TTypeFlag] @@ -637,7 +641,7 @@ const skError* = skUnknown var - eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam, tfSendable} + eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam} ## type flags that are essential for type equality. ## This is now a variable because for emulation of version:1.0 we ## might exclude {tfGcSafe, tfNoSideEffect}. @@ -689,7 +693,7 @@ type mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, - mMove, mWasMoved, mDup, mDestroy, mTrace, + mMove, mEnsureMove, mWasMoved, mDup, mDestroy, mTrace, mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, @@ -932,6 +936,7 @@ type # it won't cause problems # for skModule the string literal to output for # deprecated modules. + instantiatedFrom*: PSym # for instances, the generic symbol where it came from. when defined(nimsuggest): allUsages*: seq[TLineInfo] @@ -954,7 +959,7 @@ type kind*: TTypeKind # kind of type callConv*: TCallingConvention # for procs flags*: TTypeFlags # flags of the type - sons*: TTypeSeq # base types, etc. + sons: TTypeSeq # base types, etc. n*: PNode # node for types: # for range types a nkRange node # for record types a nkRecord node @@ -1035,6 +1040,8 @@ proc comment*(n: PNode): string = if nfHasComment in n.flags and not gconfig.useIc: # IC doesn't track comments, see `packed_ast`, so this could fail result = gconfig.comments[n.nodeId] + else: + result = "" proc `comment=`*(n: PNode, a: string) = let id = n.nodeId @@ -1221,6 +1228,7 @@ proc getDeclPragma*(n: PNode): PNode = case n.kind of routineDefs: if n[pragmasPos].kind != nkEmpty: result = n[pragmasPos] + else: result = nil of nkTypeDef: #[ type F3*{.deprecated: "x3".} = int @@ -1240,6 +1248,8 @@ proc getDeclPragma*(n: PNode): PNode = ]# if n[0].kind == nkPragmaExpr: result = n[0][1] + else: + result = nil else: # support as needed for `nkIdentDefs` etc. result = nil @@ -1255,6 +1265,12 @@ proc extractPragma*(s: PSym): PNode = if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1: # s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma] result = s.ast[0][1] + else: + result = nil + else: + result = nil + else: + result = nil assert result == nil or result.kind == nkPragma proc skipPragmaExpr*(n: PNode): PNode = @@ -1484,7 +1500,7 @@ proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode = result = newNode(nkIntLit) of tyStatic: # that's a pre-existing bug, will fix in another PR result = newNode(nkIntLit) - else: doAssert false, $kind + else: raiseAssert $kind result.intVal = intVal result.typ = typ @@ -1522,15 +1538,31 @@ proc `$`*(s: PSym): string = else: result = "" -proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType = +iterator items*(t: PType): PType = + for i in 0.. 0 and a[alen] != '`': dec(alen) @@ -226,10 +227,11 @@ proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym = ## Named parameters are special because a named parameter can be ## gensym'ed and then they have '\`' suffix that we need to ## ignore, see compiler / evaltempl.nim, snippet: - ## ``` + ## ```nim ## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id), ## if c.instLines: actual.info else: templ.info) ## ``` + result = nil for i in 1.. 0: result.add ", " diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim index 67598f9cae22f..7d142b01d301d 100644 --- a/compiler/bitsets.nim +++ b/compiler/bitsets.nim @@ -87,5 +87,11 @@ const populationCount: array[uint8, uint8] = block: arr proc bitSetCard*(x: TBitSet): BiggestInt = + result = 0 for it in x: result.inc int(populationCount[it]) + +proc bitSetToWord*(s: TBitSet; size: int): BiggestUInt = + result = 0 + for j in 0.. 8: var res = "{\n" @@ -173,7 +168,9 @@ proc getStorageLoc(n: PNode): TStorageLoc = of tyVar, tyLent: result = OnUnknown of tyPtr: result = OnStack of tyRef: result = OnHeap - else: doAssert(false, "getStorageLoc") + else: + result = OnUnknown + doAssert(false, "getStorageLoc") of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv: result = getStorageLoc(n[0]) else: result = OnUnknown @@ -215,9 +212,12 @@ proc asgnComplexity(n: PNode): int = # 'case objects' are too difficult to inline their assignment operation: result = 100 of nkRecList: + result = 0 for t in items(n): result += asgnComplexity(t) - else: discard + else: result = 0 + else: + result = 0 proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc = assert field != "" @@ -343,8 +343,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "$1 = #copyString($2);$n", [dest.rdLoc, src.rdLoc]) elif dest.storage == OnHeap: # we use a temporary to care for the dreaded self assignment: - var tmp: TLoc - getTemp(p, ty, tmp) + var tmp: TLoc = getTemp(p, ty) linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n", [dest.rdLoc, src.rdLoc, tmp.rdLoc]) linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", [tmp.rdLoc]) @@ -426,8 +425,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = proc genDeepCopy(p: BProc; dest, src: TLoc) = template addrLocOrTemp(a: TLoc): Rope = if a.k == locExpr: - var tmp: TLoc - getTemp(p, a.t, tmp) + var tmp: TLoc = getTemp(p, a.t) genAssignment(p, tmp, a, {}) addrLoc(p.config, tmp) else: @@ -474,10 +472,9 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) = d = s # ``d`` is free, so fill it with ``s`` proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) = - var a: TLoc if d.k != locNone: + var a: TLoc = initLoc(locData, n, OnStatic) # need to generate an assignment here - initLoc(a, locData, n, OnStatic) a.r = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) @@ -489,10 +486,9 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) = d.r = r proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = - var a: TLoc if d.k != locNone: # need to generate an assignment here - initLoc(a, locExpr, n, s) + var a: TLoc = initLoc(locExpr, n, s) a.r = r if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {}) else: genAssignment(p, d, a, {needToCopy}) @@ -504,49 +500,42 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) = d.r = r proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: string) = - var a, b: TLoc if d.k != locNone: internalError(p.config, e.info, "binaryStmt") - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) lineCg(p, cpsStmts, "$1 $2 $3;$n", [rdLoc(a), op, rdLoc(b)]) proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, cpname: string) = - var a, b: TLoc if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr") - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) lineCg(p, cpsStmts, "#$1($2, $3);$n", [cpname, byRefLoc(p, a), rdLoc(b)]) template unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a: TLoc if d.k != locNone: internalError(p.config, e.info, "unaryStmt") - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) lineCg(p, cpsStmts, frmt, [rdLoc(a)]) template binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a, b: TLoc assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)])) template binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a, b: TLoc assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) putIntoDest(p, d, e, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc])) template unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a: TLoc - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a)])) template unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a: TLoc - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) putIntoDest(p, d, e, ropecg(p.module, frmt, [rdCharLoc(a)])) template binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc; @@ -586,11 +575,10 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = "nimAddInt64", "nimSubInt64" ] opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"] - var a, b: TLoc assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) # skipping 'range' is correct here as we'll generate a proper range check # later via 'chckRange' let t = e.typ.skipTypes(abstractRange) @@ -620,11 +608,9 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = putIntoDest(p, d, e, res) proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = - var - a: TLoc - t: PType + var t: PType assert(e[1].typ != nil) - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) t = skipTypes(e.typ, abstractRange) if optOverflowCheck in p.options: var first = newRopeAppender() @@ -646,12 +632,11 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var - a, b: TLoc - s, k: BiggestInt + s, k: BiggestInt = 0 assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) # BUGFIX: cannot use result-type here, as it may be a boolean s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8 k = getSize(p.config, a.t) * 8 @@ -705,11 +690,10 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = assert(false, $op) proc genEqProc(p: BProc, e: PNode, d: var TLoc) = - var a, b: TLoc assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) if a.t.skipTypes(abstractInstOwned).callConv == ccClosure: putIntoDest(p, d, e, "($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)]) @@ -725,10 +709,9 @@ proc genIsNil(p: BProc, e: PNode, d: var TLoc) = proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var - a: TLoc t: PType assert(e[1].typ != nil) - initLocExpr(p, e[1], a) + var a = initLocExpr(p, e[1]) t = skipTypes(e.typ, abstractRange) template applyFormat(frmt: untyped) = @@ -768,10 +751,10 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = typ = typ.lastSon typ = typ.skipTypes(abstractInstOwned) if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr: - initLocExprSingleUse(p, e[0][0], d) + d = initLocExprSingleUse(p, e[0][0]) return else: - initLocExprSingleUse(p, e[0], a) + a = initLocExprSingleUse(p, e[0]) if d.k == locNone: # dest = *a; <-- We do not know that 'dest' is on the heap! # It is completely wrong to set 'd.storage' here, unless it's not yet @@ -808,8 +791,7 @@ proc cowBracket(p: BProc; n: PNode) = if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions: let strCandidate = n[0] if strCandidate.typ.skipTypes(abstractInst).kind == tyString: - var a: TLoc - initLocExpr(p, strCandidate, a) + var a: TLoc = initLocExpr(p, strCandidate) linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) proc cow(p: BProc; n: PNode) {.inline.} = @@ -818,31 +800,28 @@ proc cow(p: BProc; n: PNode) {.inline.} = proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}: - var a: TLoc - initLocExpr(p, e[0], a) + var a: TLoc = initLocExpr(p, e[0]) putIntoDest(p, d, e, "&" & a.r, a.storage) #Message(e.info, warnUser, "HERE NEW &") elif mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) == ctArray or isCppRef(p, e.typ): expr(p, e[0], d) else: - var a: TLoc - initLocExpr(p, e[0], a) + var a: TLoc = initLocExpr(p, e[0]) putIntoDest(p, d, e, addrLoc(p.config, a), a.storage) template inheritLocation(d: var TLoc, a: TLoc) = if d.k == locNone: d.storage = a.storage -proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) = - initLocExpr(p, e[0], a) +proc genRecordFieldAux(p: BProc, e: PNode, d: var TLoc, a: var TLoc) = + a = initLocExpr(p, e[0]) if e[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux") d.inheritLocation(a) discard getTypeDesc(p.module, a.t) # fill the record's fields.loc proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = var - a: TLoc - i: int - initLocExpr(p, e[0], a) + i: int = 0 + var a: TLoc = initLocExpr(p, e[0]) let tupType = a.t.skipTypes(abstractInst+{tyVar}) assert tupType.kind == tyTuple d.inheritLocation(a) @@ -856,6 +835,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; resTyp: ptr PType = nil): PSym = + result = nil var ty = ty assert r != "" while ty != nil: @@ -870,7 +850,7 @@ proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; if result == nil: internalError(p.config, field.info, "genCheckedRecordField") proc genRecordField(p: BProc, e: PNode, d: var TLoc) = - var a: TLoc + var a: TLoc = default(TLoc) genRecordFieldAux(p, e, d, a) var r = rdLoc(a) var f = e[1].sym @@ -882,7 +862,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) = r.add rope(f.position) putIntoDest(p, d, e, r, a.storage) else: - var rtyp: PType + var rtyp: PType = nil let field = lookupFieldAgain(p, ty, f, r, addr rtyp) if field.loc.r == "" and rtyp != nil: fillObjectFields(p.module, rtyp) if field.loc.r == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty)) @@ -903,9 +883,9 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = if op.magic == mNot: it = it[1] let disc = it[2].skipConv assert(disc.kind == nkSym) - initLoc(test, locNone, it, OnStack) - initLocExpr(p, it[1], u) - initLoc(v, locExpr, disc, OnUnknown) + test = initLoc(locNone, it, OnStack) + u = initLocExpr(p, it[1]) + v = initLoc(locExpr, disc, OnUnknown) v.r = newRopeAppender() v.r.add obj v.r.add(".") @@ -938,7 +918,7 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = let toStrProc = getToStringProc(p.module.g.graph, base) # XXX need to modify this logic for IC. # need to analyze nkFieldCheckedExpr and marks procs "used" like range checks in dce - var toStr: TLoc + var toStr: TLoc = default(TLoc) expr(p, newSymNode(toStrProc), toStr) let enumStr = "$1($2)" % [rdLoc(toStr), rdLoc(v)] linefmt(p, cpsStmts, code, [strLit, enumStr]) @@ -961,7 +941,7 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = assert e[0].kind == nkDotExpr if optFieldCheck in p.options: - var a: TLoc + var a: TLoc = default(TLoc) genRecordFieldAux(p, e[0], d, a) let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses) var r = rdLoc(a) @@ -979,17 +959,15 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = genRecordField(p, e[0], d) proc genUncheckedArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = - var a, b: TLoc - initLocExpr(p, x, a) - initLocExpr(p, y, b) + var a = initLocExpr(p, x) + var b = initLocExpr(p, y) d.inheritLocation(a) putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = - var a, b: TLoc - initLocExpr(p, x, a) - initLocExpr(p, y, b) + var a = initLocExpr(p, x) + var b = initLocExpr(p, y) var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses) var first = newRopeAppender() intLiteral(firstOrd(p.config, ty), first) @@ -1022,9 +1000,8 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = ropecg(p.module, "$1[($2)- $3]", [rdLoc(a), rdCharLoc(b), first]), a.storage) proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) = - var a, b: TLoc - initLocExpr(p, x, a) - initLocExpr(p, y, b) + var a = initLocExpr(p, x) + var b = initLocExpr(p, y) inheritLocation(d, a) putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) @@ -1070,9 +1047,8 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = else: discard proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = - var a, b: TLoc - initLocExpr(p, x, a) - initLocExpr(p, y, b) + var a = initLocExpr(p, x) + var b = initLocExpr(p, y) if not reifiedOpenArray(x): # emit range check: if optBoundsCheck in p.options: @@ -1096,9 +1072,8 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = ropecg(p.module, "$1.Field0[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = - var a, b: TLoc - initLocExpr(p, x, a) - initLocExpr(p, y, b) + var a = initLocExpr(p, x) + var b = initLocExpr(p, y) var ty = skipTypes(a.t, abstractVarRange) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: @@ -1146,8 +1121,7 @@ proc isSimpleExpr(n: PNode): bool = if n[i].kind notin {nkCommentStmt, nkEmpty}: return false result = isSimpleExpr(n.lastSon) else: - if n.isAtom: - result = true + result = n.isAtom proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # how to generate code? @@ -1172,11 +1146,10 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = # a = tmp when false: #if isSimpleExpr(e) and p.module.compileToCpp: - var tmpA, tmpB: TLoc #getTemp(p, e.typ, tmpA) #getTemp(p, e.typ, tmpB) - initLocExprSingleUse(p, e[1], tmpA) - initLocExprSingleUse(p, e[2], tmpB) + var tmpA = initLocExprSingleUse(p, e[1]) + var tmpB = initLocExprSingleUse(p, e[2]) tmpB.k = locExpr if m == mOr: tmpB.r = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))" @@ -1189,8 +1162,7 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: var L: TLabel - tmp: TLoc - getTemp(p, e.typ, tmp) # force it into a temp! + var tmp: TLoc = getTemp(p, e.typ) # force it into a temp! inc p.splitDecls expr(p, e[1], tmp) L = getLabel(p) @@ -1218,7 +1190,7 @@ proc genEcho(p: BProc, n: PNode) = if it.skipConv.kind == nkNilLit: args.add(", \"\"") elif n.len != 0: - initLocExpr(p, it, a) + a = initLocExpr(p, it) if i > 0: args.add(", ") case detectStrVersion(p.module) @@ -1233,8 +1205,7 @@ proc genEcho(p: BProc, n: PNode) = if n.len == 0: linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", [n.len]) else: - var a: TLoc - initLocExpr(p, n, a) + var a: TLoc = initLocExpr(p, n) linefmt(p, cpsStmts, "#echoBinSafe($1, $2);$n", [a.rdLoc, n.len]) when false: p.module.includeHeader("") @@ -1268,14 +1239,14 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = # appendChar(tmp0, 'z'); # asgn(s, tmp0); # } - var a, tmp: TLoc - getTemp(p, e.typ, tmp) + var a: TLoc + var tmp: TLoc = getTemp(p, e.typ) var L = 0 var appends: Rope = "" var lens: Rope = "" for i in 0.. # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; - var a, b, dest, tmpL, call: TLoc - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) let seqType = skipTypes(e[1].typ, {tyVar}) - initLoc(call, locCall, e, OnHeap) + var call = initLoc(locCall, e, OnHeap) if not p.module.compileToCpp: const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)" call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), @@ -1361,16 +1331,15 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = genRefAssign(p, a, call) #if bt != b.t: # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) - initLoc(dest, locExpr, e[2], OnHeap) - getIntTemp(p, tmpL) + var dest = initLoc(locExpr, e[2], OnHeap) + var tmpL = getIntTemp(p) lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)]) dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)]) genAssignment(p, dest, b, {needToCopy}) gcUsage(p.config, e) proc genReset(p: BProc, n: PNode) = - var a: TLoc - initLocExpr(p, n[1], a) + var a: TLoc = initLocExpr(p, n[1]) specializeReset(p, a) when false: linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", @@ -1378,14 +1347,13 @@ proc genReset(p: BProc, n: PNode) = genTypeInfoV1(p.module, skipTypes(a.t, {tyVar}), n.info)]) proc genDefault(p: BProc; n: PNode; d: var TLoc) = - if d.k == locNone: getTemp(p, n.typ, d, needsInit=true) + if d.k == locNone: d = getTemp(p, n.typ, needsInit=true) else: resetLoc(p, d) proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = var sizeExpr = sizeExpr let typ = a.t - var b: TLoc - initLoc(b, locExpr, a.lode, OnHeap) + var b: TLoc = initLoc(locExpr, a.lode, OnHeap) let refType = typ.skipTypes(abstractInstOwned) assert refType.kind == tyRef let bt = refType.lastSon @@ -1411,8 +1379,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = localError(p.module.config, a.lode.info, "the destructor that is turned into a finalizer needs " & "to have the 'nimcall' calling convention") - var f: TLoc - initLocExpr(p, newSymNode(op), f) + var f: TLoc = initLocExpr(p, newSymNode(op)) p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) if a.storage == OnHeap and usesWriteBarrier(p.config): @@ -1437,12 +1404,10 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = genObjectInit(p, cpsStmts, bt, a, constructRefObj) proc genNew(p: BProc, e: PNode) = - var a: TLoc - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) # 'genNew' also handles 'unsafeNew': if e.len == 3: - var se: TLoc - initLocExpr(p, e[2], se) + var se: TLoc = initLocExpr(p, e[2]) rawGenNew(p, a, se.rdLoc, needsInit = true) else: rawGenNew(p, a, "", needsInit = true) @@ -1450,8 +1415,7 @@ proc genNew(p: BProc, e: PNode) = proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = let seqtype = skipTypes(dest.t, abstractVarRange) - var call: TLoc - initLoc(call, locExpr, dest.lode, OnHeap) + var call: TLoc = initLoc(locExpr, dest.lode, OnHeap) if dest.storage == OnHeap and usesWriteBarrier(p.config): if canFormAcycle(p.module.g.graph, dest.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [dest.rdLoc]) @@ -1476,9 +1440,8 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = genAssignment(p, dest, call, {}) proc genNewSeq(p: BProc, e: PNode) = - var a, b: TLoc - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) if optSeqDestructors in p.config.globalOptions: let seqtype = skipTypes(e[1].typ, abstractVarRange) linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n", @@ -1492,15 +1455,15 @@ proc genNewSeq(p: BProc, e: PNode) = proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) - var a: TLoc - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) if optSeqDestructors in p.config.globalOptions: - if d.k == locNone: getTemp(p, e.typ, d, needsInit=false) - linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n", + if d.k == locNone: d = getTemp(p, e.typ, needsInit=false) + linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3));$n", [d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon), getSeqPayloadType(p.module, seqtype), ]) else: + if d.k == locNone: d = getTemp(p, e.typ, needsInit=false) # bug #22560 putIntoDest(p, d, e, ropecg(p.module, "($1)#nimNewSeqOfCap($2, $3)", [ getTypeDesc(p.module, seqtype), @@ -1526,8 +1489,27 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = else: result = false + +proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, check: PNode; d: var TLoc; r: Rope; info: TLineInfo) = + var tmp2: TLoc = default(TLoc) + tmp2.r = r + let field = lookupFieldAgain(p, ty, nField.sym, tmp2.r) + if field.loc.r == "": fillObjectFields(p.module, ty) + if field.loc.r == "": internalError(p.config, info, "genFieldObjConstr") + if check != nil and optFieldCheck in p.options: + genFieldCheck(p, check, r, field) + tmp2.r.add(".") + tmp2.r.add(field.loc.r) + if useTemp: + tmp2.k = locTemp + tmp2.storage = if isRef: OnHeap else: OnStack + else: + tmp2.k = d.k + tmp2.storage = if isRef: OnHeap else: d.storage + tmp2.lode = val + expr(p, val, tmp2) + proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = - #echo renderTree e, " ", e.isDeepConstExpr # inheritance in C++ does not allow struct initialization so # we skip this step here: if not p.module.compileToCpp and optSeqDestructors notin p.config.globalOptions: @@ -1548,42 +1530,33 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = (d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or (isPartOf(d.lode, e) != arNo) - var tmp: TLoc + var tmp: TLoc = default(TLoc) var r: Rope + let needsZeroMem = p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc} or nfAllFieldsSet notin e.flags if useTemp: - getTemp(p, t, tmp) + tmp = getTemp(p, t) r = rdLoc(tmp) if isRef: rawGenNew(p, tmp, "", needsInit = nfAllFieldsSet notin e.flags) t = t.lastSon.skipTypes(abstractInstOwned) r = "(*$1)" % [r] gcUsage(p.config, e) - else: + elif needsZeroMem: constructLoc(p, tmp) + else: + genObjectInit(p, cpsStmts, t, tmp, constructObj) else: - resetLoc(p, d) + if needsZeroMem: resetLoc(p, d) + else: genObjectInit(p, cpsStmts, d.t, d, if isRef: constructRefObj else: constructObj) r = rdLoc(d) discard getTypeDesc(p.module, t) let ty = getUniqueType(t) for i in 1..finalizer = (void*)$2;$n", [ti, rdLoc(f)]) b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ @@ -1710,8 +1684,7 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Ro appcg(p.module, result, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache]) proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = - var a: TLoc - initLocExpr(p, x, a) + var a: TLoc = initLocExpr(p, x) var dest = skipTypes(typ, typedescPtrs) var r = rdLoc(a) var nilCheck: Rope = "" @@ -1752,8 +1725,7 @@ proc genOf(p: BProc, n: PNode, d: var TLoc) = proc genRepr(p: BProc, e: PNode, d: var TLoc) = if optTinyRtti in p.config.globalOptions: localError(p.config, e.info, "'repr' is not available for --newruntime") - var a: TLoc - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) var t = skipTypes(e[1].typ, abstractVarRange) case t.kind of tyInt..tyInt64, tyUInt..tyUInt64: @@ -1775,7 +1747,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [ addrLoc(p.config, a), genTypeInfoV1(p.module, t, e.info)]), a.storage) of tyOpenArray, tyVarargs: - var b: TLoc + var b: TLoc = default(TLoc) case skipTypes(a.t, abstractVarRange).kind of tyOpenArray, tyVarargs: putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage) @@ -1833,8 +1805,7 @@ proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = # ordinary static type information putIntoDest(p, d, e, genTypeInfoV2(p.module, t, e.info)) else: - var a: TLoc - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) var nilCheck = "" # use the dynamic type stored at offset 0: var rt = newRopeAppender() @@ -1842,8 +1813,7 @@ proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e, rt) proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) = - var a: TLoc - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) var nilCheck = "" # use the dynamic type stored at offset 0: var rt = newRopeAppender() @@ -1851,11 +1821,10 @@ proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) = putIntoDest(p, d, e, rt) template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = - var a: TLoc - initLocExpr(p, n[1], a) + var a: TLoc = initLocExpr(p, n[1]) a.r = ropecg(p.module, frmt, [rdLoc(a)]) a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR) - if d.k == locNone: getTemp(p, n.typ, d) + if d.k == locNone: d = getTemp(p, n.typ) genAssignment(p, d, a, {}) gcUsage(p.config, n) @@ -1868,11 +1837,9 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = # Bug #9279, len(toOpenArray()) has to work: if a.kind in nkCallKinds and a[0].kind == nkSym and a[0].sym.magic == mSlice: # magic: pass slice to openArray: - var m: TLoc - var b, c: TLoc - initLocExpr(p, a[1], m) - initLocExpr(p, a[2], b) - initLocExpr(p, a[3], c) + var m = initLocExpr(p, a[1]) + var b = initLocExpr(p, a[2]) + var c = initLocExpr(p, a[3]) if optBoundsCheck in p.options: genBoundsCheck(p, m, b, c) if op == mHigh: @@ -1896,19 +1863,17 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: unaryExpr(p, e, d, "$1.Field1") of tyCstring: - if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)") - else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") + if op == mHigh: unaryExpr(p, e, d, "(#nimCStrLen($1)-1)") + else: unaryExpr(p, e, d, "#nimCStrLen($1)") of tyString: - var a: TLoc - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) var x = lenExpr(p, a) if op == mHigh: x = "($1-1)" % [x] putIntoDest(p, d, e, x) of tySequence: # we go through a temporary here because people write bullshit code. - var a, tmp: TLoc - initLocExpr(p, e[1], a) - getIntTemp(p, tmp) + var tmp: TLoc = getIntTemp(p) + var a = initLocExpr(p, e[1]) var x = lenExpr(p, a) if op == mHigh: x = "($1-1)" % [x] lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.r, x]) @@ -1919,31 +1884,19 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ))) else: internalError(p.config, e.info, "genArrayLen()") -proc makePtrType(baseType: PType; idgen: IdGenerator): PType = - result = newType(tyPtr, nextTypeId idgen, baseType.owner) - addSonSkipIntLit(result, baseType, idgen) - -proc makeAddr(n: PNode; idgen: IdGenerator): PNode = - if n.kind == nkHiddenAddr: - result = n - else: - result = newTree(nkHiddenAddr, n) - result.typ = makePtrType(n.typ, idgen) - proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = if optSeqDestructors in p.config.globalOptions: e[1] = makeAddr(e[1], p.module.idgen) genCall(p, e, d) return - var a, b, call: TLoc assert(d.k == locNone) var x = e[1] if x.kind in {nkAddr, nkHiddenAddr}: x = x[0] - initLocExpr(p, x, a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, x) + var b = initLocExpr(p, e[2]) let t = skipTypes(e[1].typ, {tyVar}) - initLoc(call, locCall, e, OnHeap) + var call = initLoc(locCall, e, OnHeap) if not p.module.compileToCpp: const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)" call.r = ropecg(p.module, setLenPattern, [ @@ -1963,12 +1916,11 @@ proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = if optSeqDestructors in p.config.globalOptions: binaryStmtAddr(p, e, d, "setLengthStrV2") else: - var a, b, call: TLoc if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr") - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) - initLoc(call, locCall, e, OnHeap) + var call = initLoc(locCall, e, OnHeap) call.r = ropecg(p.module, "#setLengthStr($1, $2)", [ rdLoc(a), rdLoc(b)]) genAssignment(p, a, call, {}) @@ -1981,10 +1933,9 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) = # b = temp cowBracket(p, e[1]) cowBracket(p, e[2]) - var a, b, tmp: TLoc - getTemp(p, skipTypes(e[1].typ, abstractVar), tmp) - initLocExpr(p, e[1], a) # eval a - initLocExpr(p, e[2], b) # eval b + var tmp: TLoc = getTemp(p, skipTypes(e[1].typ, abstractVar)) + var a = initLocExpr(p, e[1]) # eval a + var b = initLocExpr(p, e[2]) # eval b genAssignment(p, tmp, a, {}) genAssignment(p, a, b, {}) genAssignment(p, b, tmp, {}) @@ -2026,10 +1977,9 @@ proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)") template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) = - var a, b: TLoc assert(d.k == locNone) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) var elem = newRopeAppender() rdSetElemLoc(p.config, b, a.t, elem) lineF(p, cpsStmts, frmt, [rdLoc(a), elem]) @@ -2045,19 +1995,19 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = e[2][0] else: e[2] - initLocExpr(p, ea, a) - initLoc(b, locExpr, e, OnUnknown) + a = initLocExpr(p, ea) + b = initLoc(locExpr, e, OnUnknown) if e[1].len > 0: b.r = rope("(") for i in 0..= $2 && $1 <= $3", [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)]) else: - initLocExpr(p, it, x) + x = initLocExpr(p, it) b.r.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) if i < e[1].len - 1: b.r.add(" || ") b.r.add(")") @@ -2068,8 +2018,8 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) = else: assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + a = initLocExpr(p, e[1]) + b = initLocExpr(p, e[2]) genInExprAux(p, e, a, b, d) proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = @@ -2085,7 +2035,8 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = "&", "|", "& ~"] - var a, b, i: TLoc + var a, b: TLoc + var i: TLoc var setType = skipTypes(e[1].typ, abstractVar) var size = int(getSize(p.config, setType)) case size @@ -2122,14 +2073,13 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n") of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] &= ~(1U<<($2&7U));$n") of mCard: - var a: TLoc - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [addrLoc(p.config, a), size])) of mLtSet, mLeSet: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) - if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyBool), d) + i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter + a = initLocExpr(p, e[1]) + b = initLocExpr(p, e[2]) + if d.k == locNone: d = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyBool)) if op == mLtSet: linefmt(p, cpsStmts, lookupOpr[mLtSet], [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)]) @@ -2137,18 +2087,17 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = linefmt(p, cpsStmts, lookupOpr[mLeSet], [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)]) of mEqSet: - var a, b: TLoc assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) putIntoDest(p, d, e, ropecg(p.module, "(#nimCmpMem($1, $2, $3)==0)", [a.rdCharLoc, b.rdCharLoc, size])) of mMulSet, mPlusSet, mMinusSet: # we inline the simple for loop for better code generation: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) - if d.k == locNone: getTemp(p, setType, d) + i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter + a = initLocExpr(p, e[1]) + b = initLocExpr(p, e[2]) + if d.k == locNone: d = getTemp(p, setType) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) $n" & " $3[$1] = $4[$1] $6 $5[$1];$n", [ @@ -2165,8 +2114,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs, tyUncheckedArray} # we use whatever C gives us. Except if we have a value-type, we need to go # through its address: - var a: TLoc - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) let etyp = skipTypes(e.typ, abstractRange+{tyOwned}) let srcTyp = skipTypes(e[1].typ, abstractRange) if etyp.kind in ValueTypes and lfIndirect notin a.flags: @@ -2203,10 +2151,17 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = # 'cast' and some float type involved? --> use a union. inc(p.labels) var lbl = p.labels.rope - var tmp: TLoc + var tmp: TLoc = default(TLoc) tmp.r = "LOC$1.source" % [lbl] - linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n", - [getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl]) + let destsize = getSize(p.config, destt) + let srcsize = getSize(p.config, srct) + + if destsize > srcsize: + linefmt(p, cpsLocals, "union { $1 dest; $2 source; } LOC$3;$n #nimZeroMem(&LOC$3, sizeof(LOC$3));$n", + [getTypeDesc(p.module, e.typ), getTypeDesc(p.module, e[1].typ), lbl]) + else: + linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n", + [getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl]) tmp.k = locExpr tmp.lode = lodeTyp srct tmp.storage = OnStack @@ -2219,9 +2174,8 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = genSomeCast(p, e, d) proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = - var a: TLoc + var a: TLoc = initLocExpr(p, n[0]) var dest = skipTypes(n.typ, abstractVar) - initLocExpr(p, n[0], a) if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures): discard "no need to generate a check because it was disabled" @@ -2274,16 +2228,14 @@ proc genConv(p: BProc, e: PNode, d: var TLoc) = genSomeCast(p, e, d) proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = - var a: TLoc - initLocExpr(p, n[0], a) + var a: TLoc = initLocExpr(p, n[0]) putIntoDest(p, d, n, ropecg(p.module, "#nimToCStringConv($1)", [rdLoc(a)]), # "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc], a.storage) proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = - var a: TLoc - initLocExpr(p, n[0], a) + var a: TLoc = initLocExpr(p, n[0]) putIntoDest(p, d, n, ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.storage) @@ -2294,11 +2246,11 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = var a = e[1] var b = e[2] if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "": - initLocExpr(p, e[2], x) + x = initLocExpr(p, e[2]) putIntoDest(p, d, e, ropecg(p.module, "($1 == 0)", [lenExpr(p, x)])) elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "": - initLocExpr(p, e[1], x) + x = initLocExpr(p, e[1]) putIntoDest(p, d, e, ropecg(p.module, "($1 == 0)", [lenExpr(p, x)])) else: @@ -2307,11 +2259,10 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = if {optNaNCheck, optInfCheck} * p.options != {}: const opr: array[mAddF64..mDivF64, string] = ["+", "-", "*", "/"] - var a, b: TLoc assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) putIntoDest(p, d, e, ropecg(p.module, "(($4)($2) $1 ($4)($3))", [opr[m], rdLoc(a), rdLoc(b), getSimpleTypeDesc(p.module, e[1].typ)])) @@ -2328,34 +2279,54 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: binaryArith(p, e, d, m) -proc skipAddr(n: PNode): PNode = - result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n - proc genWasMoved(p: BProc; n: PNode) = var a: TLoc let n1 = n[1].skipAddr if p.withinBlockLeaveActions > 0 and notYetAlive(n1): discard else: - initLocExpr(p, n1, a, {lfEnforceDeref}) + a = initLocExpr(p, n1, {lfEnforceDeref}) resetLoc(p, a) #linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", # [addrLoc(p.config, a), getTypeDesc(p.module, a.t)]) proc genMove(p: BProc; n: PNode; d: var TLoc) = - var a: TLoc - initLocExpr(p, n[1].skipAddr, a) + var a: TLoc = initLocExpr(p, n[1].skipAddr) if n.len == 4: # generated by liftdestructors: - var src: TLoc - initLocExpr(p, n[2], src) + var src: TLoc = initLocExpr(p, n[2]) linefmt(p, cpsStmts, "if ($1.p != $2.p) {", [rdLoc(a), rdLoc(src)]) genStmts(p, n[3]) linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)]) else: - if d.k == locNone: getTemp(p, n.typ, d) - genAssignment(p, d, a, {}) - if p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc}: + if d.k == locNone: d = getTemp(p, n.typ) + if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: + genAssignment(p, d, a, {}) + var op = getAttachedOp(p.module.g.graph, n.typ, attachedWasMoved) + if op == nil: + resetLoc(p, a) + else: + var b = initLocExpr(p, newSymNode(op)) + case skipTypes(a.t, abstractVar+{tyStatic}).kind + of tyOpenArray, tyVarargs: # todo fixme generated `wasMoved` hooks for + # openarrays, but it probably shouldn't? + var s: string + if reifiedOpenArray(a.lode): + if a.t.kind in {tyVar, tyLent}: + s = "$1->Field0, $1->Field1" % [rdLoc(a)] + else: + s = "$1.Field0, $1.Field1" % [rdLoc(a)] + else: + s = "$1, $1Len_0" % [rdLoc(a)] + linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), s]) + else: + if p.module.compileToCpp: + linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), rdLoc(a)]) + else: + linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)]) + else: + let flags = if not canMove(p, n[1], d): {needToCopy} else: {} + genAssignment(p, d, a, flags) resetLoc(p, a) proc genDestroy(p: BProc; n: PNode) = @@ -2364,8 +2335,7 @@ proc genDestroy(p: BProc; n: PNode) = let t = arg.typ.skipTypes(abstractInst) case t.kind of tyString: - var a: TLoc - initLocExpr(p, arg, a) + var a: TLoc = initLocExpr(p, arg) if optThreads in p.config.globalOptions: linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & " #deallocShared($1.p);$n" & @@ -2375,8 +2345,7 @@ proc genDestroy(p: BProc; n: PNode) = " #dealloc($1.p);$n" & "}$n", [rdLoc(a)]) of tySequence: - var a: TLoc - initLocExpr(p, arg, a) + var a: TLoc = initLocExpr(p, arg) linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" & "}$n", @@ -2393,8 +2362,7 @@ proc genDispose(p: BProc; n: PNode) = when false: let elemType = n[1].typ.skipTypes(abstractVar).lastSon - var a: TLoc - initLocExpr(p, n[1].skipAddr, a) + var a: TLoc = initLocExpr(p, n[1].skipAddr) if isFinal(elemType): if elemType.destructor != nil: @@ -2411,7 +2379,7 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) = prepareForMutation = e[1].kind == nkHiddenDeref and e[1].typ.skipTypes(abstractInst).kind == tyString and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}) - if d.k == locNone: getTemp(p, e.typ, d) + if d.k == locNone: d = getTemp(p, e.typ) linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), x, y]) when false: localError(p.config, e.info, "invalid context for 'toOpenArray'; " & @@ -2446,11 +2414,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}: binaryStmt(p, e, d, opr[op]) else: - var a, b: TLoc assert(e[1].typ != nil) assert(e[2].typ != nil) - initLocExpr(p, e[1], a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) let ranged = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyDistinct}) let res = binaryArithOverflowRaw(p, ranged, a, b, @@ -2464,10 +2431,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if optSeqDestructors in p.config.globalOptions: binaryStmtAddr(p, e, d, "nimAddCharV1") else: - var dest, b, call: TLoc - initLoc(call, locCall, e, OnHeap) - initLocExpr(p, e[1], dest) - initLocExpr(p, e[2], b) + var call = initLoc(locCall, e, OnHeap) + var dest = initLocExpr(p, e[1]) + var b = initLocExpr(p, e[2]) call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) genAssignment(p, dest, call, {}) of mAppendStrStr: genStrAppend(p, e, d) @@ -2502,8 +2468,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mNew: genNew(p, e) of mNewFinalize: if optTinyRtti in p.config.globalOptions: - var a: TLoc - initLocExpr(p, e[1], a) + var a: TLoc = initLocExpr(p, e[1]) rawGenNew(p, a, "", needsInit = true) gcUsage(p.config, e) else: @@ -2528,6 +2493,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = elif e[1].kind == nkCheckedFieldExpr: dotExpr = e[1][0] else: + dotExpr = nil internalError(p.config, e.info, "unknown ast") let t = dotExpr[0].typ.skipTypes({tyTypeDesc}) let tname = getTypeDesc(p.module, t, dkVar) @@ -2540,8 +2506,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: genArrayLen(p, e, d, op) - of mGCref: unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n") - of mGCunref: unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n") + of mGCref: + # only a magic for the old GCs + unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n") + of mGCunref: + # only a magic for the old GCs + unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n") of mSetLengthStr: genSetLengthStr(p, e, d) of mSetLengthSeq: genSetLengthSeq(p, e, d) of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet, @@ -2594,12 +2564,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mDeepCopy: if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions: localError(p.config, e.info, - "for --gc:arc|orc 'deepcopy' support has to be enabled with --deepcopy:on") + "for --mm:arc|atomicArc|orc 'deepcopy' support has to be enabled with --deepcopy:on") - var a, b: TLoc let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] - initLocExpr(p, x, a) - initLocExpr(p, e[2], b) + var a = initLocExpr(p, x) + var b = initLocExpr(p, e[2]) genDeepCopy(p, a, b) of mDotDot, mEqCString: genCall(p, e, d) of mWasMoved: genWasMoved(p, e) @@ -2609,6 +2578,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mAccessTypeField: genAccessTypeField(p, e, d) of mSlice: genSlice(p, e, d) of mTrace: discard "no code to generate" + of mEnsureMove: + expr(p, e[1], d) else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind @@ -2620,22 +2591,23 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); var - a, b, idx: TLoc + a, b: TLoc + var idx: TLoc if nfAllConst in e.flags: var elem = newRopeAppender() genSetNode(p, e, elem) putIntoDest(p, d, e, elem) else: - if d.k == locNone: getTemp(p, e.typ, d) + if d.k == locNone: d = getTemp(p, e.typ) if getSize(p.config, e.typ) > 8: # big set: linefmt(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n", [rdLoc(d), getTypeDesc(p.module, e.typ)]) for it in e.sons: if it.kind == nkRange: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter - initLocExpr(p, it[0], a) - initLocExpr(p, it[1], b) + idx = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter + a = initLocExpr(p, it[0]) + b = initLocExpr(p, it[1]) var aa = newRopeAppender() rdSetElemLoc(p.config, a, e.typ, aa) var bb = newRopeAppender() @@ -2644,7 +2616,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d), aa, bb]) else: - initLocExpr(p, it, a) + a = initLocExpr(p, it) var aa = newRopeAppender() rdSetElemLoc(p.config, a, e.typ, aa) lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n", @@ -2655,9 +2627,9 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)]) for it in e.sons: if it.kind == nkRange: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter - initLocExpr(p, it[0], a) - initLocExpr(p, it[1], b) + idx = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter + a = initLocExpr(p, it[0]) + b = initLocExpr(p, it[1]) var aa = newRopeAppender() rdSetElemLoc(p.config, a, e.typ, aa) var bb = newRopeAppender() @@ -2667,7 +2639,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = "$2 |=(($5)(1)<<(($1)%(sizeof($5)*8)));$n", [ rdLoc(idx), rdLoc(d), aa, bb, rope(ts)]) else: - initLocExpr(p, it, a) + a = initLocExpr(p, it) var aa = newRopeAppender() rdSetElemLoc(p.config, a, e.typ, aa) lineF(p, cpsStmts, @@ -2680,19 +2652,19 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized - var tmp: TLoc + var tmp: TLoc = default(TLoc) # bug #16331 let doesAlias = lhsDoesAlias(d.lode, n) let dest = if doesAlias: addr(tmp) else: addr(d) if doesAlias: - getTemp(p, n.typ, tmp) + tmp = getTemp(p, n.typ) elif d.k == locNone: - getTemp(p, n.typ, d) + d = getTemp(p, n.typ) for i in 0..Sup" else: ".Sup") for i in 2..abs(inheritanceDiff(dest, src)): r.add(".Sup") putIntoDest(p, d, n, if isRef: "&" & r else: r, a.storage) @@ -3034,7 +3003,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = let op = n[0] if n.typ.isNil: # discard the value: - var a: TLoc + var a: TLoc = default(TLoc) if op.kind == nkSym and op.sym.magic != mNone: genMagicExpr(p, n, a, op.sym.magic) else: @@ -3128,8 +3097,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = let ex = n[0] if ex.kind != nkEmpty: genLineDir(p, n) - var a: TLoc - initLocExprSingleUse(p, ex, a) + var a: TLoc = initLocExprSingleUse(p, ex) line(p, cpsStmts, "(void)(" & a.r & ");\L") of nkAsmStmt: genAsmStmt(p, n) of nkTryStmt, nkHiddenTryStmt: @@ -3225,9 +3193,9 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = result.add "}" of tyArray: result.add "{" - for i in 0.. 0: result.add ", " - getDefaultValue(p, t.sons[1], info, result) + getDefaultValue(p, t[1], info, result) result.add "}" #result = rope"{}" of tyOpenArray, tyVarargs: @@ -3238,21 +3206,6 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = else: globalError(p.config, info, "cannot create null element for: " & $t.kind) -proc caseObjDefaultBranch(obj: PNode; branch: Int128): int = - for i in 1 ..< obj.len: - for j in 0 .. obj[i].len - 2: - if obj[i][j].kind == nkRange: - let x = getOrdValue(obj[i][j][0]) - let y = getOrdValue(obj[i][j][1]) - if branch >= x and branch <= y: - return i - elif getOrdValue(obj[i][j]) == branch: - return i - if obj[i].len == 1: - # else branch - return i - assert(false, "unreachable") - proc isEmptyCaseObjectBranch(n: PNode): bool = for it in n: if it.kind == nkSym and not isEmptyType(it.sym.typ): return false @@ -3449,12 +3402,10 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul if n[0].kind == nkNilLit: result.add "{NIM_NIL,NIM_NIL}" else: - var d: TLoc - initLocExpr(p, n[0], d) + var d: TLoc = initLocExpr(p, n[0]) result.add "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)] else: - var d: TLoc - initLocExpr(p, n, d) + var d: TLoc = initLocExpr(p, n) result.add rdLoc(d) of tyArray, tyVarargs: genConstSimpleList(p, n, isConst, result) @@ -3482,10 +3433,8 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString: genStringLiteralV2Const(p.module, n, isConst, result) else: - var d: TLoc - initLocExpr(p, n, d) + var d: TLoc = initLocExpr(p, n) result.add rdLoc(d) else: - var d: TLoc - initLocExpr(p, n, d) + var d: TLoc = initLocExpr(p, n) result.add rdLoc(d) diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim index 5e6456704dd2a..47b6a1e15e7eb 100644 --- a/compiler/ccgreset.nim +++ b/compiler/ccgreset.nim @@ -57,8 +57,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = specializeResetT(p, accessor, lastSon(typ)) of tyArray: let arraySize = lengthOrd(p.config, typ[0]) - var i: TLoc - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) + var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, arraySize]) specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.r]), typ[1]) @@ -95,12 +94,12 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = of ctInt8, ctInt16, ctInt32, ctInt64: lineCg(p, cpsStmts, "$1 = 0;$n", [accessor]) else: - doAssert false, "unexpected set type kind" - of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation, + raiseAssert "unexpected set type kind" + of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation, tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs, tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, - tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}: + tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable: discard proc specializeReset(p: BProc, a: TLoc) = diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index f536a82c13808..45a2343320503 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -50,6 +50,7 @@ proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} = result = true proc inExceptBlockLen(p: BProc): int = + result = 0 for x in p.nestedTryStmts: if x.inExcept: result.inc @@ -71,7 +72,6 @@ template startBlock(p: BProc, start: FormatStr = "{$n", proc endBlock(p: BProc) proc genVarTuple(p: BProc, n: PNode) = - var tup, field: TLoc if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple") # if we have a something that's been captured, use the lowering instead: @@ -83,7 +83,7 @@ proc genVarTuple(p: BProc, n: PNode) = # check only the first son var forHcr = treatGlobalDifferentlyForHCR(p.module, n[0].sym) let hcrCond = if forHcr: getTempName(p.module) else: "" - var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]] + var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]] = @[] # determine if the tuple is constructed at top-level scope or inside of a block (if/while/block) let isGlobalInBlock = forHcr and p.blocks.len > 2 # do not close and reopen blocks if this is a 'global' but inside of a block (if/while/block) @@ -95,7 +95,7 @@ proc genVarTuple(p: BProc, n: PNode) = startBlock(p) genLineDir(p, n) - initLocExpr(p, n[^1], tup) + var tup = initLocExpr(p, n[^1]) var t = tup.t.skipTypes(abstractInst) for i in 0.. 0: blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope]) if p.blocks[topBlock].label.len != 0: @@ -244,8 +244,7 @@ proc genGotoState(p: BProc, n: PNode) = # switch (x.state) { # case 0: goto STATE0; # ... - var a: TLoc - initLocExpr(p, n[0], a) + var a: TLoc = initLocExpr(p, n[0]) lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)]) p.flags.incl beforeRetNeeded lineF(p, cpsStmts, "case -1:$n", []) @@ -264,13 +263,13 @@ proc genGotoState(p: BProc, n: PNode) = proc genBreakState(p: BProc, n: PNode, d: var TLoc) = var a: TLoc - initLoc(d, locExpr, n, OnUnknown) + d = initLoc(locExpr, n, OnUnknown) if n[0].kind == nkClosure: - initLocExpr(p, n[0][1], a) + a = initLocExpr(p, n[0][1]) d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)] else: - initLocExpr(p, n[0], a) + a = initLocExpr(p, n[0]) # the environment is guaranteed to contain the 'state' field at offset 1: d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)] @@ -290,14 +289,22 @@ proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) = #echo "New code produced for ", v.name.s, " ", p.config $ value.info genBracedInit(p, value, isConst = false, v.typ, result) -proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) = - var params = newRopeAppender() +proc genCppParamsForCtor(p: BProc; call: PNode): string = + result = "" var argsCounter = 0 - let typ = skipTypes(value[0].typ, abstractInst) + let typ = skipTypes(call[0].typ, abstractInst) assert(typ.kind == tyProc) - for i in 1.. 0: result.add "," + result.add genCppInitializer(p.module, p, call[i][0].sym.typ) + else: + genOtherArg(p, call, i, typ, result, argsCounter) + +proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope) = + let params = genCppParamsForCtor(p, call) if params.len == 0: decl = runtimeFormat("$#;\n", [decl]) else: @@ -315,7 +322,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = var targetProc = p var valueAsRope = "" potentialValueInit(p, v, value, valueAsRope) - if sfGlobal in v.flags: + if sfGlobal in v.flags: if v.flags * {sfImportc, sfExportc} == {sfImportc} and value.kind == nkEmpty and v.loc.flags * {lfHeader, lfNoDecl} != {}: @@ -341,7 +348,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = # Only do this for complex types that may need a call to `objectInit` if sfThread in v.flags and emulatedThreadVars(p.config) and isComplexValueType(v.typ): - initLocExprSingleUse(p.module.preInitProc, vn, loc) + loc = initLocExprSingleUse(p.module.preInitProc, vn) genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, constructObj) # Alternative construction using default constructor (which may zeromem): # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc) @@ -359,10 +366,10 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = var decl = localVarDecl(p, vn) var tmp: TLoc if isCppCtorCall: - genCppVarForCtor(p, v, vn, value, decl) + genCppVarForCtor(p, value, decl) line(p, cpsStmts, decl) else: - initLocExprSingleUse(p, value, tmp) + tmp = initLocExprSingleUse(p, value) lineF(p, cpsStmts, "$# = $#;\n", [decl, tmp.rdLoc]) return assignLocalVar(p, vn) @@ -408,8 +415,7 @@ proc genSingleVar(p: BProc, a: PNode) = proc genClosureVar(p: BProc, a: PNode) = var immediateAsgn = a[2].kind != nkEmpty - var v: TLoc - initLocExpr(p, a[0], v) + var v: TLoc = initLocExpr(p, a[0]) genLineDir(p, a) if immediateAsgn: loadInto(p, a[0], a[2], v) @@ -444,7 +450,7 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) = a: TLoc lelse: TLabel if not isEmptyType(n.typ) and d.k == locNone: - getTemp(p, n.typ, d) + d = getTemp(p, n.typ) genLineDir(p, n) let lend = getLabel(p) for it in n.sons: @@ -452,7 +458,7 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) = if d.k == locTemp and isEmptyType(n.typ): d.k = locNone if it.len == 2: startBlock(p) - initLocExprSingleUse(p, it[0], a) + a = initLocExprSingleUse(p, it[0]) lelse = getLabel(p) inc(p.labels) lineF(p, cpsStmts, "if (!$1) goto $2;$n", @@ -520,7 +526,7 @@ proc genComputedGoto(p: BProc; n: PNode) = # wrapped inside stmt lists by inject destructors won't be recognised let n = n.flattenStmts() var casePos = -1 - var arraySize: int + var arraySize: int = 0 for i in 0.. 0: genIfForCaseUntil(p, n, d, rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n", eqFormat = "if ($1 == $2) goto $3;$n", @@ -971,15 +971,18 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) = hasDefault = true exprBlock(p, branch.lastSon, d) lineF(p, cpsStmts, "break;$n", []) - if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault: - lineF(p, cpsStmts, "default: __assume(0);$n", []) + if not hasDefault: + if hasBuiltinUnreachable in CC[p.config.cCompiler].props: + lineF(p, cpsStmts, "default: __builtin_unreachable();$n", []) + elif hasAssume in CC[p.config.cCompiler].props: + lineF(p, cpsStmts, "default: __assume(0);$n", []) lineF(p, cpsStmts, "}$n", []) if lend != "": fixLabel(p, lend) proc genCase(p: BProc, t: PNode, d: var TLoc) = genLineDir(p, t) if not isEmptyType(t.typ) and d.k == locNone: - getTemp(p, t.typ, d) + d = getTemp(p, t.typ) case skipTypes(t[0].typ, abstractVarRange).kind of tyString: genStringCase(p, t, tyString, d) @@ -1030,7 +1033,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = p.module.includeHeader("") if not isEmptyType(t.typ) and d.k == locNone: - getTemp(p, t.typ, d) + d = getTemp(p, t.typ) genLineDir(p, t) inc(p.labels, 2) @@ -1050,7 +1053,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = expr(p, t[0], d) endBlock(p) - # First pass: handle Nim based exceptions: + # First pass: handle Nim based exceptions: lineCg(p, cpsStmts, "catch (#Exception* T$1_) {$n", [etmp+1]) genRestoreFrameAfterException(p) # an unhandled exception happened! @@ -1195,7 +1198,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) = expr(p, body, d) if not isEmptyType(t.typ) and d.k == locNone: - getTemp(p, t.typ, d) + d = getTemp(p, t.typ) genLineDir(p, t) cgsym(p.module, "popCurrentExceptionEx") let fin = if t[^1].kind == nkFinally: t[^1] else: nil @@ -1273,7 +1276,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) = p.flags.incl nimErrorFlagAccessed if not isEmptyType(t.typ) and d.k == locNone: - getTemp(p, t.typ, d) + d = getTemp(p, t.typ) expr(p, t[0], d) @@ -1308,7 +1311,8 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) = let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" if optTinyRtti in p.config.globalOptions: let checkFor = $getObjDepth(t[i][j].typ) - appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) + appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", + [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) else: let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) @@ -1379,7 +1383,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = # propagateCurrentException(); # if not isEmptyType(t.typ) and d.k == locNone: - getTemp(p, t.typ, d) + d = getTemp(p, t.typ) let quirkyExceptions = p.config.exc == excQuirky or (t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags) if not quirkyExceptions: @@ -1388,7 +1392,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = p.flags.incl noSafePoints genLineDir(p, t) cgsym(p.module, "Exception") - var safePoint: Rope + var safePoint: Rope = "" if not quirkyExceptions: safePoint = getTempName(p.module) linefmt(p, cpsLocals, "#TSafePoint $1;$n", [safePoint]) @@ -1453,7 +1457,8 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" if optTinyRtti in p.config.globalOptions: let checkFor = $getObjDepth(t[i][j].typ) - appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) + appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", + [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) else: let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) @@ -1491,8 +1496,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) = of nkSym: var sym = it.sym if sym.kind in {skProc, skFunc, skIterator, skMethod}: - var a: TLoc - initLocExpr(p, it, a) + var a: TLoc = initLocExpr(p, it) res.add($rdLoc(a)) elif sym.kind == skType: res.add($getTypeDesc(p.module, sym.typ)) @@ -1504,8 +1508,7 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) = res.add($getTypeDesc(p.module, it.typ)) else: discard getTypeDesc(p.module, skipTypes(it.typ, abstractPtrs)) - var a: TLoc - initLocExpr(p, it, a) + var a: TLoc = initLocExpr(p, it) res.add($a.rdLoc) if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props: @@ -1607,11 +1610,10 @@ when false: expr(p, call, d) proc asgnFieldDiscriminant(p: BProc, e: PNode) = - var a, tmp: TLoc var dotExpr = e[0] if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr[0] - initLocExpr(p, e[0], a) - getTemp(p, a.t, tmp) + var a = initLocExpr(p, e[0]) + var tmp: TLoc = getTemp(p, a.t) expr(p, e[1], tmp) if p.inUncheckedAssignSection == 0: let field = dotExpr[1].sym @@ -1629,9 +1631,8 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = else: let le = e[0] let ri = e[1] - var a: TLoc + var a: TLoc = initLoc(locNone, le, OnUnknown) discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), dkVar) - initLoc(a, locNone, le, OnUnknown) a.flags.incl(lfEnforceDeref) a.flags.incl(lfPrepareForMutation) genLineDir(p, le) # it can be a nkBracketExpr, which may raise @@ -1643,7 +1644,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = loadInto(p, le, ri, a) proc genStmts(p: BProc, t: PNode) = - var a: TLoc + var a: TLoc = default(TLoc) let isPush = p.config.hasHint(hintExtendedContext) if isPush: pushInfoContext(p.config, t.info) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 96f5869b00606..adad9df3e6286 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -21,7 +21,7 @@ const proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) proc genCaseRange(p: BProc, branch: PNode) -proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) +proc getTemp(p: BProc, t: PType, needsInit=false): TLoc proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode; typ: PType) = @@ -74,8 +74,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = genTraverseProc(c, accessor, lastSon(typ)) of tyArray: let arraySize = lengthOrd(c.p.config, typ[0]) - var i: TLoc - getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i) + var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt)) var oldCode = p.s(cpsStmts) freeze oldCode linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", @@ -119,12 +118,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) = proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) = var p = c.p assert typ.kind == tySequence - var i: TLoc - getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i) + var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt)) var oldCode = p.s(cpsStmts) freeze oldCode - var a: TLoc - a.r = accessor + var a: TLoc = TLoc(r: accessor) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", [i.r, lenExpr(c.p, a)]) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index f808fa93ea3b3..a591ce93ba39a 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -18,12 +18,13 @@ type TypeDescKind = enum dkParam #skParam dkRefParam #param passed by ref when {.byref.} is used. Cpp only. C goes straight to dkParam and is handled as a regular pointer + dkRefGenericParam #param passed by ref when {.byref.} is used that is also a generic. Cpp only. C goes straight to dkParam and is handled as a regular pointer dkVar #skVar dkField #skField dkResult #skResult dkConst #skConst dkOther #skType, skTemp, skLet and skForVar so far - + proc descKindFromSymKind(kind: TSymKind): TypeDescKind = case kind of skParam: dkParam @@ -32,7 +33,7 @@ proc descKindFromSymKind(kind: TSymKind): TypeDescKind = of skResult: dkResult of skConst: dkConst else: dkOther - + proc isKeyword(w: PIdent): bool = # Nim and C++ share some keywords # it's more efficient to test the whole Nim keywords range @@ -205,8 +206,12 @@ proc mapType(conf: ConfigRef; typ: PType; isParam: bool): TCTypeKind = result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) of tyStatic: if typ.n != nil: result = mapType(conf, lastSon typ, isParam) - else: doAssert(false, "mapType: " & $typ.kind) - else: doAssert(false, "mapType: " & $typ.kind) + else: + result = ctVoid + doAssert(false, "mapType: " & $typ.kind) + else: + result = ctVoid + doAssert(false, "mapType: " & $typ.kind) proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind = @@ -321,7 +326,9 @@ proc getSimpleTypeDesc(m: BModule; typ: PType): Rope = of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ[0]) of tyStatic: if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ) - else: internalError(m.config, "tyStatic for getSimpleTypeDesc") + else: + result = "" + internalError(m.config, "tyStatic for getSimpleTypeDesc") of tyGenericInst, tyAlias, tySink, tyOwned: result = getSimpleTypeDesc(m, lastSon typ) else: result = "" @@ -457,7 +464,7 @@ proc multiFormat*(frmt: var string, chars : static openArray[char], args: openAr var num = 0 while i < frmt.len: if frmt[i] == c: - inc(i) + inc(i) case frmt[i] of c: res.add(c) @@ -470,11 +477,11 @@ proc multiFormat*(frmt: var string, chars : static openArray[char], args: openAr if i >= frmt.len or frmt[i] notin {'0'..'9'}: break num = j if j > high(arg) + 1: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt else: res.add(arg[j-1]) else: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt var start = i while i < frmt.len: if frmt[i] != c: inc(i) @@ -483,12 +490,15 @@ proc multiFormat*(frmt: var string, chars : static openArray[char], args: openAr res.add(substr(frmt, start, i - 1)) frmt = res -proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var string, +template cgDeclFrmt*(s: PSym): string = + s.constraint.strVal + +proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params: var string, check: var IntSet, declareEnvironment=true; weakDep=false;) = let t = prc.typ let isCtor = sfConstructor in prc.flags - if isCtor: + if isCtor or (name[0] == '~' and sfMember in prc.flags): #destructors cant have void rettype = "" elif t[0] == nil or isInvalidReturnType(m.config, t): rettype = "void" @@ -497,8 +507,8 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var rettype = getTypeDescAux(m, t[0], check, dkResult) else: rettype = runtimeFormat(rettype.replace("'0", "$1"), [getTypeDescAux(m, t[0], check, dkResult)]) - var types, names, args: seq[string] - if not isCtor: + var types, names, args: seq[string] = @[] + if not isCtor: var this = t.n[1].sym fillParamName(m, this) fillLoc(this.loc, locParam, t.n[1], @@ -511,12 +521,15 @@ proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, params: var types.add getTypeDescWeak(m, this.typ, check, dkParam) let firstParam = if isCtor: 1 else: 2 - for i in firstParam..= typ.len: - doAssert false, "invalid apostrophe type parameter index" + raiseAssert "invalid apostrophe type parameter index" result = typ[idx] for i in 1..stars: @@ -858,6 +906,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym) let sig = hashType(origTyp, m.config) + result = "" # todo move `result = getTypePre(m, t, sig)` here ? defer: # defer is the simplest in this case if isImportedType(t) and not m.typeABICache.containsOrIncl(sig): addAbiCheck(m, t, result) @@ -865,7 +914,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes result = getTypePre(m, t, sig) if result != "" and t.kind != tyOpenArray: excl(check, t.id) - if kind == dkRefParam: + if kind == dkRefParam or kind == dkRefGenericParam and origTyp.kind == tyGenericInst: result.add("&") return case t.kind @@ -938,7 +987,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes of tyProc: result = getTypeName(m, origTyp, sig) m.typeCache[sig] = result - var rettype, desc: Rope + var rettype, desc: Rope = "" genProcParams(m, t, rettype, desc, check, true, true) if not isImportedType(t): if t.callConv != ccClosure: # procedure vars may need a closure! @@ -1015,7 +1064,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes while i < cppName.len: if cppName[i] == '\'': var chunkEnd = i-1 - var idx, stars: int + var idx, stars: int = 0 if scanCppGenericSlot(cppName, i, idx, stars): result.add cppName.substr(chunkStart, chunkEnd) chunkStart = i @@ -1079,8 +1128,8 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes result = "" # fixes bug #145: excl(check, t.id) - - + + proc getTypeDesc(m: BModule; typ: PType; kind = dkParam): Rope = var check = initIntSet() result = getTypeDescAux(m, typ, check, kind) @@ -1095,7 +1144,7 @@ proc getClosureType(m: BModule; t: PType, kind: TClosureTypeKind): Rope = assert t.kind == tyProc var check = initIntSet() result = getTempName(m) - var rettype, desc: Rope + var rettype, desc: Rope = "" genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf) if not isImportedType(t): if t.callConv != ccClosure or kind != clFull: @@ -1125,11 +1174,19 @@ proc isReloadable(m: BModule; prc: PSym): bool = proc isNonReloadable(m: BModule; prc: PSym): bool = return m.hcrOn and sfNonReloadable in prc.flags -proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride: var bool; isCtor: bool) = - var afterParams: string +proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride, isMemberVirtual: var bool; isCtor: bool, isFunctor=false) = + var afterParams: string = "" if scanf(val, "$*($*)$s$*", name, params, afterParams): + if name.strip() == "operator" and params == "": #isFunctor? + parseVFunctionDecl(afterParams, name, params, retType, superCall, isFnConst, isOverride, isMemberVirtual, isCtor, true) + return isFnConst = afterParams.find("const") > -1 isOverride = afterParams.find("override") > -1 + isMemberVirtual = name.find("virtual ") > -1 + if isMemberVirtual: + name = name.replace("virtual ", "") + if isFunctor: + name = "operator ()" if isCtor: discard scanf(afterParams, ":$s$*", superCall) else: @@ -1138,28 +1195,28 @@ proc parseVFunctionDecl(val: string; name, params, retType, superCall: var strin params = "(" & params & ")" proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl : bool = false) = - assert {sfVirtual, sfConstructor} * prc.flags != {} + assert sfCppMember * prc.flags != {} let isCtor = sfConstructor in prc.flags - let isVirtual = not isCtor var check = initIntSet() fillBackendName(m, prc) fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown) var memberOp = "#." #only virtual var typ: PType if isCtor: - typ = prc.typ.sons[0] + typ = prc.typ[0] else: - typ = prc.typ.sons[1] + typ = prc.typ[1] if typ.kind == tyPtr: typ = typ[0] memberOp = "#->" var typDesc = getTypeDescWeak(m, typ, check, dkParam) let asPtrStr = rope(if asPtr: "_PTR" else: "") - var name, params, rettype, superCall: string - var isFnConst, isOverride: bool - parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isCtor) - genMemberProcParams(m, prc, superCall, rettype, params, check, true, false) - var fnConst, override: string + var name, params, rettype, superCall: string = "" + var isFnConst, isOverride, isMemberVirtual: bool = false + parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isMemberVirtual, isCtor) + genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false) + let isVirtual = sfVirtual in prc.flags or isMemberVirtual + var fnConst, override: string = "" if isCtor: name = typDesc if isFnConst: @@ -1167,28 +1224,28 @@ proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = if isFwdDecl: if isVirtual: rettype = "virtual " & rettype - if isOverride: + if isOverride: override = " override" superCall = "" else: - if isVirtual: + if not isCtor: prc.loc.r = "$1$2(@)" % [memberOp, name] elif superCall != "": superCall = " : " & superCall - + name = "$1::$2" % [typDesc, name] result.add "N_LIB_PRIVATE " result.addf("$1$2($3, $4)$5$6$7$8", [rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name, params, fnConst, override, superCall]) - + proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) = # using static is needed for inline procs var check = initIntSet() fillBackendName(m, prc) fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown) - var rettype, params: Rope + var rettype, params: Rope = "" genProcParams(m, prc.typ, rettype, params, check, true, false) # handle the 2 options for hotcodereloading codegen - function pointer # (instead of forward declaration) or header for function body with "_actual" postfix @@ -1198,7 +1255,7 @@ proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) name.add("_actual") # careful here! don't access ``prc.ast`` as that could reload large parts of # the object graph! - if prc.constraint.isNil: + if sfCodegenDecl notin prc.flags: if lfExportLib in prc.loc.flags: if isHeaderFile in m.flags: result.add "N_LIB_IMPORT " @@ -1428,7 +1485,7 @@ proc genEnumInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) = genTypeInfoAux(m, typ, typ, name, info) var nodePtrs = getTempName(m) & "_" & $typ.n.len genTNimNodeArray(m, nodePtrs, rope(typ.n.len)) - var enumNames, specialCases: Rope + var enumNames, specialCases: Rope = "" var firstNimNode = m.typeNodes var hasHoles = false for i in 0.. 0: decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)] if p.hcrOn: decl.add("static ") @@ -640,17 +646,17 @@ proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = else: decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) -proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) +proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope) proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode) = let s = vn.sym fillBackendName(p.module, s) fillLoc(s.loc, locGlobalVar, vn, OnHeap) - var decl: Rope + var decl: Rope = "" let td = getTypeDesc(p.module, vn.sym.typ, dkVar) genGlobalVarDecl(p, vn, td, "", decl) decl.add " " & $s.loc.r - genCppVarForCtor(p, v, vn, value, decl) + genCppVarForCtor(p, value, decl) p.module.s[cfsVars].add decl proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = @@ -701,7 +707,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = decl.addf(" $1 = $2;$n", [s.loc.r, value]) else: decl.addf(" $1;$n", [s.loc.r]) - + p.module.s[cfsVars].add(decl) if p.withinLoop > 0 and value == "": # fixes tests/run/tzeroarray: @@ -735,12 +741,12 @@ proc genLiteral(p: BProc, n: PNode; result: var Rope) proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int) proc raiseExit(p: BProc) -proc initLocExpr(p: BProc, e: PNode, result: var TLoc, flags: TLocFlags = {}) = - initLoc(result, locNone, e, OnUnknown, flags) +proc initLocExpr(p: BProc, e: PNode, flags: TLocFlags = {}): TLoc = + result = initLoc(locNone, e, OnUnknown, flags) expr(p, e, result) -proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) = - initLoc(result, locNone, e, OnUnknown) +proc initLocExprSingleUse(p: BProc, e: PNode): TLoc = + result = initLoc(locNone, e, OnUnknown) if e.kind in nkCallKinds and (e[0].kind != nkSym or e[0].sym.magic == mNone): # We cannot check for tfNoSideEffect here because of mutable parameters. discard "bug #8202; enforce evaluation order for nested calls for C++ too" @@ -831,8 +837,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = var p = newProc(nil, m) p.options.excl optStackTrace p.flags.incl nimErrorFlagDisabled - var dest: TLoc - initLoc(dest, locTemp, lib.path, OnStack) + var dest: TLoc = initLoc(locTemp, lib.path, OnStack) dest.r = getTempName(m) appcg(m, m.s[cfsDynLibInit],"$1 $2;$n", [getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)]) @@ -867,11 +872,10 @@ proc symInDynamicLib(m: BModule, sym: PSym) = inc(m.labels, 2) if isCall: let n = lib.path - var a: TLoc - initLocExpr(m.initProc, n[0], a) + var a: TLoc = initLocExpr(m.initProc, n[0]) var params = rdLoc(a) & "(" for i in 1.. 0 and n[0].kind == nkEmpty of nkSym: if n.sym.kind == skResult: result = true @@ -996,11 +1010,8 @@ proc containsResult(n: PNode): bool = for i in 0.. 0: closeNamespaceNim(m.s[cfsProcs]) m.s[cfsProcs].add "using namespace " & m.config.cppCustomNamespace & ";\L" @@ -1878,13 +1900,13 @@ proc genInitCode(m: BModule) = if beforeRetNeeded in m.initProc.flags: prc.add("\tBeforeRet_: ;\n") - if sfMainModule in m.module.flags and m.config.exc == excGoto: + if m.config.exc == excGoto: if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil: m.appcg(prc, "\t#nimTestErrorFlag();$n", []) if optStackTrace in m.initProc.options and preventStackTrace notin m.flags: prc.add(deinitFrame(m.initProc)) - elif sfMainModule in m.module.flags and m.config.exc == excGoto: + elif m.config.exc == excGoto: if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil: m.appcg(prc, "\t#nimTestErrorFlag();$n", []) @@ -1984,7 +2006,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule result.preInitProc = newProc(nil, result) result.preInitProc.flags.incl nimErrorFlagDisabled result.preInitProc.labels = 100_000 # little hack so that unique temporaries are generated - initNodeTable(result.dataCache) + result.dataCache = initNodeTable() result.typeStack = @[] result.typeNodesName = getTempName(result) result.nimTypesName = getTempName(result) @@ -2161,6 +2183,7 @@ proc updateCachedModule(m: BModule) = proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode): PNode = ## Also called from IC. + result = nil if sfMainModule in m.module.flags: # phase ordering problem here: We need to announce this # dependency to 'nimTestErrorFlag' before system.c has been written to disk. diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index e1309e0fdd777..9cc146c4db5f7 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -193,15 +193,17 @@ proc initBlock*(): TBlock = result.sections[i] = newRopeAppender() proc newProc*(prc: PSym, module: BModule): BProc = - new(result) - result.prc = prc - result.module = module - result.options = if prc != nil: prc.options - else: module.config.options - result.blocks = @[initBlock()] - result.nestedTryStmts = @[] - result.finallySafePoints = @[] - result.sigConflicts = initCountTable[string]() + result = BProc( + prc: prc, + module: module, + optionsStack: if module.initProc != nil: module.initProc.optionsStack + else: @[], + options: if prc != nil: prc.options + else: module.config.options, + blocks: @[initBlock()], + sigConflicts: initCountTable[string]()) + if optQuirky in result.options: + result.flags = {nimErrorFlagDisabled} proc newModuleList*(g: ModuleGraph): BModuleList = BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](), diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index cc37691fdf3cc..ed8f33630ef08 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -44,6 +44,8 @@ proc getDispatcher*(s: PSym): PSym = if dispatcherPos < s.ast.len: result = s.ast[dispatcherPos].sym doAssert sfDispatcher in result.flags + else: + result = nil proc methodCall*(n: PNode; conf: ConfigRef): PNode = result = n @@ -62,6 +64,7 @@ type MethodResult = enum No, Invalid, Yes proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult = + result = No if a.name.id != b.name.id: return if a.typ.len != b.typ.len: return @@ -149,7 +152,7 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) = disp.ast[resultPos] = copyTree(meth.ast[resultPos]) proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) = - var witness: PSym + var witness: PSym = nil for i in 0.. 1: - var cond: PNode + var cond: PNode = nil for i in 0.. ", e + echo "exception table:" + for i, e in ctx.exceptionTable: + echo i, " -> ", e diff --git a/compiler/commands.nim b/compiler/commands.nim index f881a4f576c55..f36d82306f3ef 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -184,7 +184,7 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, info: TLineInfo; orig: string; conf: ConfigRef) = var id = "" # arg = key or [key] or key:val or [key]:val; with val=on|off var i = 0 - var notes: set[TMsgKind] + var notes: set[TMsgKind] = {} var isBracket = false if i < arg.len and arg[i] == '[': isBracket = true @@ -205,7 +205,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, # unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit let x = findStr(noteMin, noteMax, id, errUnknown) if x != errUnknown: notes = {TNoteKind(x)} - else: localError(conf, info, "unknown $#: $#" % [name, id]) + else: + if isSomeHint: + message(conf, info, hintUnknownHint, id) + else: + localError(conf, info, "unknown $#: $#" % [name, id]) case id.normalize of "all": # other note groups would be easy to support via additional cases notes = if isSomeHint: {hintMin..hintMax} else: {warnMin..warnMax} @@ -240,7 +244,7 @@ proc processCompile(conf: ConfigRef; filename: string) = const errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'atomicArc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found" errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found" - errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found" + errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found" errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found" template warningOptionNoop(switch: string) = @@ -263,13 +267,17 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo of "none": result = conf.selectedGC == gcNone of "stack", "regions": result = conf.selectedGC == gcRegions of "atomicarc": result = conf.selectedGC == gcAtomicArc - else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) + else: + result = false + localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) of "opt": case arg.normalize of "speed": result = contains(conf.options, optOptimizeSpeed) of "size": result = contains(conf.options, optOptimizeSize) of "none": result = conf.options * {optOptimizeSpeed, optOptimizeSize} == {} - else: localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg) + else: + result = false + localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg) of "verbosity": result = $conf.verbosity == arg of "app": case arg.normalize @@ -279,7 +287,9 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo not contains(conf.globalOptions, optGenGuiApp) of "staticlib": result = contains(conf.globalOptions, optGenStaticLib) and not contains(conf.globalOptions, optGenGuiApp) - else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg) + else: + result = false + localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg) of "dynliboverride": result = isDynlibOverride(conf, arg) of "exceptions": @@ -288,8 +298,12 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo of "setjmp": result = conf.exc == excSetjmp of "quirky": result = conf.exc == excQuirky of "goto": result = conf.exc == excGoto - else: localError(conf, info, errInvalidExceptionSystem % arg) - else: invalidCmdLineOption(conf, passCmd1, switch, info) + else: + result = false + localError(conf, info, errInvalidExceptionSystem % arg) + else: + result = false + invalidCmdLineOption(conf, passCmd1, switch, info) proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool = case switch.normalize @@ -335,10 +349,14 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros") result = contains(conf.options, optTrMacros) of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace) - of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch) + of "nilseqs", "nilchecks", "taintmode": + warningOptionNoop(switch) + result = false of "panics": result = contains(conf.globalOptions, optPanics) of "jsbigint64": result = contains(conf.globalOptions, optJsBigInt64) - else: invalidCmdLineOption(conf, passCmd1, switch, info) + else: + result = false + invalidCmdLineOption(conf, passCmd1, switch, info) proc processPath(conf: ConfigRef; path: string, info: TLineInfo, notRelativeToProj = false): AbsoluteDir = @@ -380,7 +398,8 @@ proc makeAbsolute(s: string): AbsoluteFile = proc setTrackingInfo(conf: ConfigRef; dirty, file, line, column: string, info: TLineInfo) = ## set tracking info, common code for track, trackDirty, & ideTrack - var ln, col: int + var ln: int = 0 + var col: int = 0 if parseUtils.parseInt(line, ln) <= 0: localError(conf, info, errInvalidNumber % line) if parseUtils.parseInt(column, col) <= 0: @@ -527,10 +546,11 @@ proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef) = if conf.exc == excNone and conf.backend != backendCpp: conf.exc = excGoto -proc unregisterArcOrc(conf: ConfigRef) = +proc unregisterArcOrc*(conf: ConfigRef) = undefSymbol(conf.symbols, "gcdestructors") undefSymbol(conf.symbols, "gcarc") undefSymbol(conf.symbols, "gcorc") + undefSymbol(conf.symbols, "gcatomicarc") undefSymbol(conf.symbols, "nimSeqsV2") undefSymbol(conf.symbols, "nimV2") excl conf.globalOptions, optSeqDestructors @@ -588,10 +608,17 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass, defineSymbol(conf.symbols, "gcregions") else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) +proc pathRelativeToConfig(arg: string, pass: TCmdLinePass, conf: ConfigRef): string = + if pass == passPP and not isAbsolute(arg): + assert isAbsolute(conf.currentConfigDir), "something is wrong with currentConfigDir" + result = conf.currentConfigDir / arg + else: + result = arg + proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = - var - key, val: string + var key = "" + var val = "" case switch.normalize of "eval": expectArg(conf, switch, arg, pass, info) @@ -606,17 +633,17 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; for path in nimbleSubs(conf, arg): addPath(conf, if pass == passPP: processCfgPath(conf, path, info) else: processPath(conf, path, info), info) - of "nimblepath", "babelpath": - if switch.normalize == "babelpath": deprecatedAlias(switch, "nimblepath") + of "nimblepath": if pass in {passCmd2, passPP} and optNoNimblePath notin conf.globalOptions: expectArg(conf, switch, arg, pass, info) var path = processPath(conf, arg, info, notRelativeToProj=true) let nimbleDir = AbsoluteDir getEnv("NIMBLE_DIR") if not nimbleDir.isEmpty and pass == passPP: + path = nimbleDir / RelativeDir"pkgs2" + nimblePath(conf, path, info) path = nimbleDir / RelativeDir"pkgs" nimblePath(conf, path, info) - of "nonimblepath", "nobabelpath": - if switch.normalize == "nobabelpath": deprecatedAlias(switch, "nonimblepath") + of "nonimblepath": expectNoArg(conf, switch, arg, pass, info) disableNimblePath(conf) of "clearnimblepath": @@ -633,7 +660,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; # refs bug #18674, otherwise `--os:windows` messes up with `--nimcache` set # in config nims files, e.g. via: `import os; switch("nimcache", "/tmp/somedir")` if conf.target.targetOS == osWindows and DirSep == '/': arg = arg.replace('\\', '/') - conf.nimcacheDir = processPath(conf, arg, info, notRelativeToProj=true) + conf.nimcacheDir = processPath(conf, pathRelativeToConfig(arg, pass, conf), info, notRelativeToProj=true) of "out", "o": expectArg(conf, switch, arg, pass, info) let f = splitFile(processPath(conf, arg, info, notRelativeToProj=true).string) @@ -767,7 +794,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if conf.backend == backendJs or conf.cmd == cmdNimscript: discard else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info) #if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe) - of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info) + of "tlsemulation": + processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info) + if optTlsEmulation in conf.globalOptions: + conf.legacyFeatures.incl emitGenerics of "implicitstatic": processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info) of "patterns", "trmacros": @@ -799,7 +829,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "consoleapp") of "lib": incl(conf.globalOptions, optGenDynLib) - incl(conf.globalOptions, optNoMain) excl(conf.globalOptions, optGenGuiApp) defineSymbol(conf.symbols, "library") defineSymbol(conf.symbols, "dll") @@ -829,6 +858,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "header": if conf != nil: conf.headerFile = arg incl(conf.globalOptions, optGenIndex) + of "nimbasepattern": + if conf != nil: conf.nimbasePattern = arg of "index": case arg.normalize of "", "on": conf.globalOptions.incl {optGenIndex} @@ -874,15 +905,19 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "nodejs") of "maxloopiterationsvm": expectArg(conf, switch, arg, pass, info) - conf.maxLoopIterationsVM = parseInt(arg) + var value: int = 10_000_000 + discard parseSaturatedNatural(arg, value) + if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero") + conf.maxLoopIterationsVM = value of "errormax": expectArg(conf, switch, arg, pass, info) # Note: `nim check` (etc) can overwrite this. # `0` is meaningless, give it a useful meaning as in clang's -ferror-limit # If user doesn't set this flag and the code doesn't either, it'd # have the same effect as errorMax = 1 - let ret = parseInt(arg) - conf.errorMax = if ret == 0: high(int) else: ret + var value: int = 0 + discard parseSaturatedNatural(arg, value) + conf.errorMax = if value == 0: high(int) else: value of "verbosity": expectArg(conf, switch, arg, pass, info) let verbosity = parseInt(arg) @@ -896,7 +931,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf.mainPackageNotes = conf.notes of "parallelbuild": expectArg(conf, switch, arg, pass, info) - conf.numberOfProcessors = parseInt(arg) + var value: int = 0 + discard parseSaturatedNatural(arg, value) + conf.numberOfProcessors = value of "version", "v": expectNoArg(conf, switch, arg, pass, info) writeVersionInfo(conf, pass) @@ -1105,7 +1142,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; else: invalidCmdLineOption(conf, pass, switch, info) proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) = - var cmd, arg: string + var cmd = "" + var arg = "" splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo) processSwitch(cmd, arg, pass, gCmdLineInfo, config) @@ -1132,7 +1170,10 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser; config.projectName = unixToNativePath(p.key) config.arguments = cmdLineRest(p) result = true - elif pass != passCmd2: setCommandEarly(config, p.key) + elif pass != passCmd2: + setCommandEarly(config, p.key) + result = false + else: result = false else: if pass == passCmd1: config.commandArgs.add p.key if argsCount == 1: @@ -1143,4 +1184,6 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser; config.projectName = unixToNativePath(p.key) config.arguments = cmdLineRest(p) result = true + else: + result = false inc argsCount diff --git a/compiler/concepts.nim b/compiler/concepts.nim index 6a383a937ca56..55088740c888b 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -13,8 +13,6 @@ import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, intsets -from magicsys import addSonSkipIntLit - when defined(nimPreviewSlimSystem): import std/assertions @@ -33,18 +31,6 @@ proc declareSelf(c: PContext; info: TLineInfo) = s.typ.add newType(tyEmpty, nextTypeId(c.idgen), ow) addDecl(c, s, info) -proc isSelf*(t: PType): bool {.inline.} = - ## Is this the magical 'Self' type? - t.kind == tyTypeDesc and tfPacked in t.flags - -proc makeTypeDesc*(c: PContext, typ: PType): PType = - if typ.kind == tyTypeDesc and not isSelf(typ): - result = typ - else: - result = newTypeS(tyTypeDesc, c) - incl result.flags, tfCheckedForDestructor - result.addSonSkipIntLit(typ, c.idgen) - proc semConceptDecl(c: PContext; n: PNode): PNode = ## Recursive helper for semantic checking for the concept declaration. ## Currently we only support (possibly empty) lists of statements @@ -62,7 +48,7 @@ proc semConceptDecl(c: PContext; n: PNode): PNode = result[i] = n[i] result[^1] = semConceptDecl(c, n[^1]) of nkCommentStmt: - discard + result = n else: localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n)) result = n @@ -121,8 +107,11 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = for i in 0.. 0: #Ok, we are in a try, lets see which (if any) try's we break out from: + var trailingFinales: seq[PNode] = @[] + if c.inTryStmt > 0: + # Ok, we are in a try, lets see which (if any) try's we break out from: for b in countdown(c.blocks.high, i): if c.blocks[b].isTryBlock: trailingFinales.add c.blocks[b].finale @@ -385,7 +383,8 @@ proc genCall(c: var Con; n: PNode) = # Pass by 'out' is a 'must def'. Good enough for a move optimizer. genDef(c, n[i]) # every call can potentially raise: - if false: # c.inTryStmt > 0 and canRaiseConservative(n[0]): + if c.inTryStmt > 0 and canRaiseConservative(n[0]): + inc c.interestingInstructions # we generate the instruction sequence: # fork lab1 # goto exceptionHandler (except or finally) @@ -467,7 +466,7 @@ proc gen(c: var Con; n: PNode) = of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1: gen(c, n[1]) of nkVarSection, nkLetSection: genVarSection(c, n) - of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'" + of nkDefer: raiseAssert "dfa construction pass requires the elimination of 'defer'" else: discard when false: diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 81e23b0693acc..7edee1663f6b7 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -170,6 +170,7 @@ proc cmpDecimalsIgnoreCase(a, b: string): int = proc prettyString(a: object): string = # xxx pending std/prettyprint refs https://github.com/nim-lang/RFCs/issues/203#issuecomment-602534906 + result = "" for k, v in fieldPairs(a): result.add k & ": " & $v & "\n" @@ -215,12 +216,16 @@ proc whichType(d: PDoc; n: PNode): PSym = if n.kind == nkSym: if d.types.strTableContains(n.sym): result = n.sym + else: + result = nil else: + result = nil for i in 0.. 0: return @@ -474,9 +492,8 @@ proc externalDep(d: PDoc; module: PSym): string = proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string; renderFlags: TRenderFlags = {}; procLink: string) = - var r: TSrcGen + var r: TSrcGen = initTokRender(n, renderFlags) var literal = "" - initTokRender(r, n, renderFlags) var kind = tkEof var tokenPos = 0 var procTokenPos = 0 @@ -590,7 +607,9 @@ proc runAllExamples(d: PDoc) = rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string]) # removeFile(outp.changeFileExt(ExeExt)) # it's in nimcache, no need to remove -proc quoted(a: string): string = result.addQuoted(a) +proc quoted(a: string): string = + result = "" + result.addQuoted(a) proc toInstantiationInfo(conf: ConfigRef, info: TLineInfo): (string, int, int) = # xxx expose in compiler/lineinfos.nim @@ -716,7 +735,7 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var ItemPre, let (rdoccmd, code) = prepareExample(d, n, topLevel) var msg = "Example:" if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd - var s: string + var s: string = "" dispA(d.conf, s, "\n

$1

\n", "\n\n\\textbf{$1}\n", [msg]) dest.add s @@ -932,14 +951,17 @@ proc genDeprecationMsg(d: PDoc, n: PNode): string = if n[1].kind in {nkStrLit..nkTripleStrLit}: result = getConfigVar(d.conf, "doc.deprecationmsg") % [ "label", "Deprecated:", "message", xmltree.escape(n[1].strVal)] + else: + result = "" else: - doAssert false + raiseAssert "unreachable" type DocFlags = enum kDefault kForceExport proc genSeeSrc(d: PDoc, path: string, line: int): string = + result = "" let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc") if docItemSeeSrc.len > 0: let path = relativeTo(AbsoluteFile path, AbsoluteDir getCurrentDir(), '/') @@ -981,7 +1003,7 @@ proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol = result.symKind = k.toHumanStr if k in routineKinds: var - paramTypes: seq[string] + paramTypes: seq[string] = @[] renderParamTypes(paramTypes, n[paramsPos], toNormalize=true) let paramNames = renderParamNames(n[paramsPos], toNormalize=true) # In some rare cases (system.typeof) parameter type is not set for default: @@ -1006,8 +1028,7 @@ proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol = genNode = n[miscPos][1] # FIXME: what is index 1? if genNode != nil: var literal = "" - var r: TSrcGen - initTokRender(r, genNode, {renderNoBody, renderNoComments, + var r: TSrcGen = initTokRender(genNode, {renderNoBody, renderNoComments, renderNoPragmas, renderNoProcDefs, renderExpandUsing}) var kind = tkEof while true: @@ -1028,15 +1049,14 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx var result = "" var literal, plainName = "" var kind = tkEof - var comm: ItemPre + var comm: ItemPre = default(ItemPre) if n.kind in routineDefs: getAllRunnableExamples(d, n, comm) else: comm.add genRecComment(d, n) - var r: TSrcGen # Obtain the plain rendered string for hyperlink titles. - initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments, + var r: TSrcGen = initTokRender(n, {renderNoBody, renderNoComments, renderDocComments, renderNoPragmas, renderNoProcDefs, renderExpandUsing}) while true: getNextTok(r, kind, literal) @@ -1135,13 +1155,16 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonEx if k == skType and nameNode.kind == nkSym: d.types.strTableAdd nameNode.sym -proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem = +proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): JsonItem = if not isVisible(d, nameNode): return var name = getNameEsc(d, nameNode) comm = genRecComment(d, n) r: TSrcGen - initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing}) + renderFlags = {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing} + if nonExports: + renderFlags.incl renderNonExportedFields + r = initTokRender(n, renderFlags) result.json = %{ "name": %name, "type": %($k), "line": %n.info.line.int, "col": %n.info.col} if comm != nil: @@ -1173,9 +1196,9 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem = result.json["signature"]["genericParams"] = newJArray() for genericParam in n[genericParamsPos]: var param = %{"name": %($genericParam)} - if genericParam.sym.typ.sons.len > 0: + if genericParam.sym.typ.len > 0: param["types"] = newJArray() - for kind in genericParam.sym.typ.sons: + for kind in genericParam.sym.typ: param["types"].add %($kind) result.json["signature"]["genericParams"].add param if optGenIndex in d.conf.globalOptions: @@ -1270,6 +1293,8 @@ proc documentNewEffect(cache: IdentCache; n: PNode): PNode = let s = n[namePos].sym if tfReturnsNew in s.typ.flags: result = newIdentNode(getIdent(cache, "new"), n.info) + else: + result = nil proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode = let spec = effectSpec(x, effectType) @@ -1292,6 +1317,8 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id result = newTreeI(nkExprColonExpr, n.info, newIdentNode(getIdent(cache, $effectType), n.info), effects) + else: + result = nil proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode = let s = n[namePos].sym @@ -1305,6 +1332,8 @@ proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName if effects.len > 0: result = newTreeI(nkExprColonExpr, n.info, newIdentNode(getIdent(cache, pragmaName), n.info), effects) + else: + result = nil proc documentRaises*(cache: IdentCache; n: PNode) = if n[namePos].kind != nkSym: return @@ -1378,7 +1407,7 @@ proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept" of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0]) of nkCallKinds: - var comm: ItemPre + var comm: ItemPre = default(ItemPre) getAllRunnableExamples(d, n, comm) if comm.len != 0: d.modDescPre.add(comm) else: discard @@ -1487,7 +1516,7 @@ proc finishGenerateDoc*(d: var PDoc) = overloadChoices.sort(cmp) var nameContent = "" for item in overloadChoices: - var itemDesc: string + var itemDesc: string = "" renderItemPre(d, item.descRst, itemDesc) nameContent.add( getConfigVar(d.conf, "doc.item") % ( @@ -1513,7 +1542,7 @@ proc finishGenerateDoc*(d: var PDoc) = for i, entry in d.jEntriesPre: if entry.rst != nil: let resolved = resolveSubs(d.sharedState, entry.rst) - var str: string + var str: string = "" renderRstToOut(d[], resolved, str) entry.json[entry.rstField] = %str d.jEntriesPre[i].rst = nil @@ -1526,7 +1555,7 @@ proc finishGenerateDoc*(d: var PDoc) = proc add(d: PDoc; j: JsonItem) = if j.json != nil or j.rst != nil: d.jEntriesPre.add j -proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) = +proc generateJson*(d: PDoc, n: PNode, config: ConfigRef, includeComments: bool = true) = case n.kind of nkPragma: let doctypeNode = findPragma(n, wDoctype) @@ -1558,14 +1587,14 @@ proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) = if n[i].kind != nkCommentStmt: # order is always 'type var let const': d.add genJsonItem(d, n[i], n[i][0], - succ(skType, ord(n.kind)-ord(nkTypeSection))) + succ(skType, ord(n.kind)-ord(nkTypeSection)), optShowNonExportedFields in config.globalOptions) of nkStmtList: for i in 0..= x and branch <= y: + return i + elif getOrdValue(obj[i][j]) == branch: + return i + if obj[i].len == 1: + # else branch + return i + return 1 + +template newZero(t: PType; info: TLineInfo; k = nkIntLit): PNode = newNodeIT(k, info, t) + +proc expandDefault*(t: PType; info: TLineInfo): PNode + +proc expandField(s: PSym; info: TLineInfo): PNode = + result = newNodeIT(nkExprColonExpr, info, s.typ) + result.add newSymNode(s) + result.add expandDefault(s.typ, info) + +proc expandDefaultN(n: PNode; info: TLineInfo; res: PNode) = + case n.kind + of nkRecList: + for i in 0.. 0: + result = expandDefault(t.lastSon, info) + else: + result = newZero(t, info, nkEmpty) + of tyFromExpr: + if t.n != nil and t.n.typ != nil: + result = expandDefault(t.n.typ, info) + else: + result = newZero(t, info, nkEmpty) + of tyArray: + result = newZero(t, info, nkBracket) + let n = toInt64(lengthOrd(nil, t)) + for i in 0..= 5 else: @@ -580,7 +585,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, compilePattern = joinPath(conf.cCompilerPath, exe) else: - compilePattern = getCompilerExe(conf, c, isCpp) + compilePattern = exe includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)])) @@ -644,7 +649,7 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = let hashFile = toGeneratedFile(conf, conf.mangleModuleName(cfile.cname).AbsoluteFile, "sha1") let currentHash = footprint(conf, cfile) - var f: File + var f: File = default(File) if open(f, hashFile.string, fmRead): let oldHash = parseSecureHash(f.readLine()) close(f) @@ -779,6 +784,7 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body raise proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] = + result = @[] when defined(macosx): if optCDebug in conf.globalOptions and optGenStaticLib notin conf.globalOptions: # if needed, add an option to skip or override location @@ -834,7 +840,10 @@ proc linkViaResponseFile(conf: ConfigRef; cmd: string) = else: writeFile(linkerArgs, args) try: - execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs) + when defined(macosx): + execLinkCmd(conf, "xargs " & cmd.substr(0, last) & " < " & linkerArgs) + else: + execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs) finally: removeFile(linkerArgs) @@ -858,6 +867,7 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu result = conf.getNimcacheDir / RelativeFile(targetName) proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string = + result = "" if conf.hasHint(hintCC): if optListCmd in conf.globalOptions or conf.verbosity > 1: result = MsgKindToStr[hintCC] % (demangleModuleName(path.splitFile.name) & ": " & compileCmd) @@ -880,15 +890,15 @@ proc preventLinkCmdMaxCmdLen(conf: ConfigRef, linkCmd: string) = proc callCCompiler*(conf: ConfigRef) = var - linkCmd: string + linkCmd: string = "" extraCmds: seq[string] if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}: return # speed up that call if only compiling and no script shall be # generated #var c = cCompiler var script: Rope = "" - var cmds: TStringSeq - var prettyCmds: TStringSeq + var cmds: TStringSeq = default(TStringSeq) + var prettyCmds: TStringSeq = default(TStringSeq) let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) for idx, it in conf.toCompile: @@ -1019,8 +1029,9 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) = conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty) proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool = + result = false if not fileExists(jsonFile) or not fileExists(conf.absOutFile): return true - var bcache: BuildCache + var bcache: BuildCache = default(BuildCache) try: bcache.fromJson(jsonFile.string.parseFile) except IOError, OSError, ValueError: stderr.write "Warning: JSON processing failed for: $#\n" % jsonFile.string @@ -1036,7 +1047,7 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute if $secureHashFile(file) != hash: return true proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = - var bcache: BuildCache + var bcache: BuildCache = default(BuildCache) try: bcache.fromJson(jsonFile.string.parseFile) except ValueError, KeyError, JsonKindError: let e = getCurrentException() @@ -1049,7 +1060,8 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = globalError(conf, gCmdLineInfo, "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " % [outputCurrent, output, jsonFile.string]) - var cmds, prettyCmds: TStringSeq + var cmds: TStringSeq = default(TStringSeq) + var prettyCmds: TStringSeq= default(TStringSeq) let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) for (name, cmd) in bcache.compile: cmds.add cmd @@ -1059,6 +1071,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = for cmd in bcache.extraCmds: execExternalProgram(conf, cmd, hintExecuting) proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope = + result = "" for it in list: result.addf("--file:r\"$1\"$N", [rope(it.cname.string)]) diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 84ed9916450cb..d04388b96a0c7 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -201,17 +201,15 @@ proc parseLine(p: var TTmplParser) = proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream = - var p: TTmplParser - p.config = conf - p.info = newLineInfo(conf, filename, 0, 0) - p.outp = llStreamOpen("") - p.inp = stdin - p.subsChar = charArg(conf, call, "subschar", 1, '$') - p.nimDirective = charArg(conf, call, "metachar", 2, '#') - p.emit = strArg(conf, call, "emit", 3, "result.add") - p.conc = strArg(conf, call, "conc", 4, " & ") - p.toStr = strArg(conf, call, "tostring", 5, "$") - p.x = newStringOfCap(120) + var p = TTmplParser(config: conf, info: newLineInfo(conf, filename, 0, 0), + outp: llStreamOpen(""), inp: stdin, + subsChar: charArg(conf, call, "subschar", 1, '$'), + nimDirective: charArg(conf, call, "metachar", 2, '#'), + emit: strArg(conf, call, "emit", 3, "result.add"), + conc: strArg(conf, call, "conc", 4, " & "), + toStr: strArg(conf, call, "tostring", 5, "$"), + x: newStringOfCap(120) + ) # do not process the first line which contains the directive: if llStreamReadLine(p.inp, p.x): inc p.info.line diff --git a/compiler/filters.nim b/compiler/filters.nim index 8151c0b93801d..8d8af6b1c87ca 100644 --- a/compiler/filters.nim +++ b/compiler/filters.nim @@ -29,23 +29,30 @@ proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode = return n[i] proc charArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: char): char = + var x = getArg(conf, n, name, pos) if x == nil: result = default elif x.kind == nkCharLit: result = chr(int(x.intVal)) - else: invalidPragma(conf, n) + else: + result = default(char) + invalidPragma(conf, n) proc strArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: string): string = var x = getArg(conf, n, name, pos) if x == nil: result = default elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal - else: invalidPragma(conf, n) + else: + result = "" + invalidPragma(conf, n) proc boolArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: bool): bool = var x = getArg(conf, n, name, pos) if x == nil: result = default elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false - else: invalidPragma(conf, n) + else: + result = false + invalidPragma(conf, n) proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream = var pattern = strArg(conf, call, "startswith", 1, "") diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim index 558a6c9a3df1e..fb0fafc985302 100644 --- a/compiler/gorgeimpl.nim +++ b/compiler/gorgeimpl.nim @@ -29,10 +29,11 @@ proc readOutput(p: Process): (string, int) = proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) = let workingDir = parentDir(toFullPath(conf, info)) + result = ("", 0) if cache.len > 0: let h = secureHash(cmd & "\t" & input & "\t" & cache) let filename = toGeneratedFile(conf, AbsoluteFile("gorge_" & $h), "txt").string - var f: File + var f: File = default(File) if optForceFullMake notin conf.globalOptions and open(f, filename): result = (f.readAll, 0) f.close diff --git a/compiler/guards.nim b/compiler/guards.nim index 15c6a64e36fde..1366a2382c439 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -51,6 +51,10 @@ proc isLet(n: PNode): bool = elif n.sym.kind == skParam and skipTypes(n.sym.typ, abstractInst).kind notin {tyVar}: result = true + else: + result = false + else: + result = false proc isVar(n: PNode): bool = n.kind == nkSym and n.sym.kind in {skResult, skVar} and @@ -136,6 +140,8 @@ proc neg(n: PNode; o: Operators): PNode = result = a elif b != nil: result = b + else: + result = nil else: # leave not (a == 4) as it is result = newNodeI(nkCall, n.info, 2) @@ -330,6 +336,8 @@ proc usefulFact(n: PNode; o: Operators): PNode = result = n elif n[1].getMagic in someLen or n[2].getMagic in someLen: result = n + else: + result = nil of someLe+someLt: if isLetLocation(n[1], true) or isLetLocation(n[2], true): # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' @@ -337,12 +345,18 @@ proc usefulFact(n: PNode; o: Operators): PNode = elif n[1].getMagic in someLen or n[2].getMagic in someLen: # XXX Rethink this whole idea of 'usefulFact' for semparallel result = n + else: + result = nil of mIsNil: if isLetLocation(n[1], false) or isVar(n[1]): result = n + else: + result = nil of someIn: if isLetLocation(n[1], true): result = n + else: + result = nil of mAnd: let a = usefulFact(n[1], o) @@ -356,10 +370,14 @@ proc usefulFact(n: PNode; o: Operators): PNode = result = a elif b != nil: result = b + else: + result = nil of mNot: let a = usefulFact(n[1], o) if a != nil: result = a.neg(o) + else: + result = nil of mOr: # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything # with that knowledge... @@ -376,6 +394,8 @@ proc usefulFact(n: PNode; o: Operators): PNode = result[1] = a result[2] = b result = result.neg(o) + else: + result = nil elif n.kind == nkSym and n.sym.kind == skLet: # consider: # let a = 2 < x @@ -384,8 +404,12 @@ proc usefulFact(n: PNode; o: Operators): PNode = # We make can easily replace 'a' by '2 < x' here: if n.sym.astdef != nil: result = usefulFact(n.sym.astdef, o) + else: + result = nil elif n.kind == nkStmtListExpr: result = usefulFact(n.lastSon, o) + else: + result = nil type TModel* = object @@ -451,8 +475,9 @@ proc hasSubTree(n, x: PNode): bool = of nkEmpty..nkNilLit: result = n.sameTree(x) of nkFormalParams: - discard + result = false else: + result = false for i in 0.. unknown! if sameTree(fact[2], eq[val]): result = impYes elif valuesUnequal(fact[2], eq[val]): result = impNo + else: + result = impUnknown elif sameTree(fact[2], eq[loc]): if sameTree(fact[1], eq[val]): result = impYes elif valuesUnequal(fact[1], eq[val]): result = impNo + else: + result = impUnknown + else: + result = impUnknown of mInSet: # remember: mInSet is 'contains' so the set comes first! if sameTree(fact[2], eq[loc]) and isValue(eq[val]): if inSet(fact[1], eq[val]): result = impYes else: result = impNo - of mNot, mOr, mAnd: assert(false, "impliesEq") - else: discard + else: + result = impUnknown + of mNot, mOr, mAnd: + result = impUnknown + assert(false, "impliesEq") + else: result = impUnknown proc leImpliesIn(x, c, aSet: PNode): TImplication = if c.kind in {nkCharLit..nkUInt64Lit}: @@ -512,13 +549,19 @@ proc leImpliesIn(x, c, aSet: PNode): TImplication = var value = newIntNode(c.kind, firstOrd(nil, x.typ)) # don't iterate too often: if c.intVal - value.intVal < 1000: - var i, pos, neg: int + var i, pos, neg: int = 0 while value.intVal <= c.intVal: if inSet(aSet, value): inc pos else: inc neg inc i; inc value.intVal if pos == i: result = impYes elif neg == i: result = impNo + else: + result = impUnknown + else: + result = impUnknown + else: + result = impUnknown proc geImpliesIn(x, c, aSet: PNode): TImplication = if c.kind in {nkCharLit..nkUInt64Lit}: @@ -529,17 +572,23 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication = let max = lastOrd(nil, x.typ) # don't iterate too often: if max - getInt(value) < toInt128(1000): - var i, pos, neg: int + var i, pos, neg: int = 0 while value.intVal <= max: if inSet(aSet, value): inc pos else: inc neg inc i; inc value.intVal if pos == i: result = impYes elif neg == i: result = impNo + else: result = impUnknown + else: + result = impUnknown + else: + result = impUnknown proc compareSets(a, b: PNode): TImplication = if equalSets(nil, a, b): result = impYes elif intersectSets(nil, a, b).len == 0: result = impNo + else: result = impUnknown proc impliesIn(fact, loc, aSet: PNode): TImplication = case fact[0].sym.magic @@ -550,22 +599,32 @@ proc impliesIn(fact, loc, aSet: PNode): TImplication = elif sameTree(fact[2], loc): if inSet(aSet, fact[1]): result = impYes else: result = impNo + else: + result = impUnknown of mInSet: if sameTree(fact[2], loc): result = compareSets(fact[1], aSet) + else: + result = impUnknown of someLe: if sameTree(fact[1], loc): result = leImpliesIn(fact[1], fact[2], aSet) elif sameTree(fact[2], loc): result = geImpliesIn(fact[2], fact[1], aSet) + else: + result = impUnknown of someLt: if sameTree(fact[1], loc): result = leImpliesIn(fact[1], fact[2].pred, aSet) elif sameTree(fact[2], loc): # 4 < x --> 3 <= x result = geImpliesIn(fact[2], fact[1].pred, aSet) - of mNot, mOr, mAnd: assert(false, "impliesIn") - else: discard + else: + result = impUnknown + of mNot, mOr, mAnd: + result = impUnknown + assert(false, "impliesIn") + else: result = impUnknown proc valueIsNil(n: PNode): TImplication = if n.kind == nkNilLit: impYes @@ -577,13 +636,19 @@ proc impliesIsNil(fact, eq: PNode): TImplication = of mIsNil: if sameTree(fact[1], eq[1]): result = impYes + else: + result = impUnknown of someEq: if sameTree(fact[1], eq[1]): result = valueIsNil(fact[2].skipConv) elif sameTree(fact[2], eq[1]): result = valueIsNil(fact[1].skipConv) - of mNot, mOr, mAnd: assert(false, "impliesIsNil") - else: discard + else: + result = impUnknown + of mNot, mOr, mAnd: + result = impUnknown + assert(false, "impliesIsNil") + else: result = impUnknown proc impliesGe(fact, x, c: PNode): TImplication = assert isLocation(x) @@ -594,32 +659,57 @@ proc impliesGe(fact, x, c: PNode): TImplication = # fact: x = 4; question x >= 56? --> true iff 4 >= 56 if leValue(c, fact[2]): result = impYes else: result = impNo + else: + result = impUnknown elif sameTree(fact[2], x): if isValue(fact[1]) and isValue(c): if leValue(c, fact[1]): result = impYes else: result = impNo + else: + result = impUnknown + else: + result = impUnknown of someLt: if sameTree(fact[1], x): if isValue(fact[2]) and isValue(c): # fact: x < 4; question N <= x? --> false iff N <= 4 if leValue(fact[2], c): result = impNo + else: result = impUnknown # fact: x < 4; question 2 <= x? --> we don't know + else: + result = impUnknown elif sameTree(fact[2], x): # fact: 3 < x; question: N-1 < x ? --> true iff N-1 <= 3 if isValue(fact[1]) and isValue(c): if leValue(c.pred, fact[1]): result = impYes + else: result = impUnknown + else: + result = impUnknown + else: + result = impUnknown of someLe: if sameTree(fact[1], x): if isValue(fact[2]) and isValue(c): # fact: x <= 4; question x >= 56? --> false iff 4 <= 56 if leValue(fact[2], c): result = impNo # fact: x <= 4; question x >= 2? --> we don't know + else: + result = impUnknown + else: + result = impUnknown elif sameTree(fact[2], x): # fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3 if isValue(fact[1]) and isValue(c): if leValue(c, fact[1]): result = impYes - of mNot, mOr, mAnd: assert(false, "impliesGe") - else: discard + else: result = impUnknown + else: + result = impUnknown + else: + result = impUnknown + of mNot, mOr, mAnd: + result = impUnknown + assert(false, "impliesGe") + else: result = impUnknown proc impliesLe(fact, x, c: PNode): TImplication = if not isLocation(x): @@ -634,35 +724,59 @@ proc impliesLe(fact, x, c: PNode): TImplication = # fact: x = 4; question x <= 56? --> true iff 4 <= 56 if leValue(fact[2], c): result = impYes else: result = impNo + else: + result = impUnknown elif sameTree(fact[2], x): if isValue(fact[1]) and isValue(c): if leValue(fact[1], c): result = impYes else: result = impNo + else: + result = impUnknown + else: + result = impUnknown of someLt: if sameTree(fact[1], x): if isValue(fact[2]) and isValue(c): # fact: x < 4; question x <= N? --> true iff N-1 <= 4 if leValue(fact[2], c.pred): result = impYes + else: + result = impUnknown # fact: x < 4; question x <= 2? --> we don't know + else: + result = impUnknown elif sameTree(fact[2], x): # fact: 3 < x; question: x <= 1 ? --> false iff 1 <= 3 if isValue(fact[1]) and isValue(c): if leValue(c, fact[1]): result = impNo - + else: result = impUnknown + else: + result = impUnknown + else: + result = impUnknown of someLe: if sameTree(fact[1], x): if isValue(fact[2]) and isValue(c): # fact: x <= 4; question x <= 56? --> true iff 4 <= 56 if leValue(fact[2], c): result = impYes + else: result = impUnknown # fact: x <= 4; question x <= 2? --> we don't know + else: + result = impUnknown elif sameTree(fact[2], x): # fact: 3 <= x; question: x <= 2 ? --> false iff 2 < 3 if isValue(fact[1]) and isValue(c): if leValue(c, fact[1].pred): result = impNo + else:result = impUnknown + else: + result = impUnknown + else: + result = impUnknown - of mNot, mOr, mAnd: assert(false, "impliesLe") - else: discard + of mNot, mOr, mAnd: + result = impUnknown + assert(false, "impliesLe") + else: result = impUnknown proc impliesLt(fact, x, c: PNode): TImplication = # x < 3 same as x <= 2: @@ -674,6 +788,8 @@ proc impliesLt(fact, x, c: PNode): TImplication = let q = x.pred if q != x: result = impliesLe(fact, q, c) + else: + result = impUnknown proc `~`(x: TImplication): TImplication = case x @@ -725,6 +841,7 @@ proc factImplies(fact, prop: PNode): TImplication = proc doesImply*(facts: TModel, prop: PNode): TImplication = assert prop.kind in nkCallKinds + result = impUnknown for f in facts.s: # facts can be invalidated, in which case they are 'nil': if not f.isNil: @@ -900,6 +1017,7 @@ proc applyReplacements(n: PNode; rep: TReplacements): PNode = proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication = # now check for inferrable facts: a <= b and b <= c implies a <= c + result = impUnknown for i in 0..m.s.high: let fact = m.s[i] if fact != nil and fact.getMagic in someLe: @@ -981,7 +1099,7 @@ proc addFactLt*(m: var TModel; a, b: PNode) = proc settype(n: PNode): PType = result = newType(tySet, ItemId(module: -1, item: -1), n.typ.owner) - var idgen: IdGenerator + var idgen: IdGenerator = nil addSonSkipIntLit(result, n.typ, idgen) proc buildOf(it, loc: PNode; o: Operators): PNode = diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 1aab9d5fe9df0..9fdec38c0edb1 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -10,9 +10,6 @@ # This include implements the high level optimization pass. # included from sem.nim -when defined(nimPreviewSlimSystem): - import std/assertions - proc hlo(c: PContext, n: PNode): PNode proc evalPattern(c: PContext, n, orig: PNode): PNode = @@ -20,9 +17,11 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode = # we need to ensure that the resulting AST is semchecked. However, it's # awful to semcheck before macro invocation, so we don't and treat # templates and macros as immediate in this context. - var rule: string - if c.config.hasHint(hintPattern): - rule = renderTree(n, {renderNoComments}) + var rule: string = + if c.config.hasHint(hintPattern): + renderTree(n, {renderNoComments}) + else: + "" let s = n[0].sym case s.kind of skMacro: @@ -73,7 +72,7 @@ proc hlo(c: PContext, n: PNode): PNode = else: if n.kind in {nkFastAsgn, nkAsgn, nkSinkAsgn, nkIdentDefs, nkVarTuple} and n[0].kind == nkSym and - {sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}: + {sfGlobal, sfPure} <= n[0].sym.flags: # do not optimize 'var g {.global} = re(...)' again! return n result = applyPatterns(c, n) diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim index a5513105113af..edbaeb240ea65 100644 --- a/compiler/ic/bitabs.nim +++ b/compiler/ic/bitabs.nim @@ -13,6 +13,8 @@ type vals: seq[T] # indexed by LitId keys: seq[LitId] # indexed by hash(val) +proc initBiTable*[T](): BiTable[T] = BiTable[T](vals: @[], keys: @[]) + proc nextTry(h, maxHash: Hash): Hash {.inline.} = result = (h + 1) and maxHash diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index 21f69e48520a3..a1922c812d328 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -101,7 +101,7 @@ proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool var f2 = rodfiles.open(asymFile.string) f2.loadHeader() f2.loadSection aliveSymsSection - var oldData: seq[int32] + var oldData: seq[int32] = @[] f2.loadSeq(oldData) f2.close if f2.err == ok and oldData == s: diff --git a/compiler/ic/dce.nim b/compiler/ic/dce.nim index bc61a38dec477..ce64221010a43 100644 --- a/compiler/ic/dce.nim +++ b/compiler/ic/dce.nim @@ -40,10 +40,14 @@ proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): boo if ({sfExportc, sfCompilerProc} * flags != {}) or (symPtr.kind == skMethod): result = true + else: + result = false # XXX: This used to be a condition to: # (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or if sfCompilerProc in flags: c.compilerProcs[g[c.thisModule].fromDisk.strings[symPtr.name]] = (c.thisModule, symId) + else: + result = false template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index a72db57c5f129..8b0a3105438ad 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -359,7 +359,7 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI paddingAtEnd: t.paddingAtEnd) storeNode(p, t, n) p.typeInst = t.typeInst.storeType(c, m) - for kid in items t.sons: + for kid in items t: p.types.add kid.storeType(c, m) c.addMissing t.sym p.sym = t.sym.safeItemId(c, m) @@ -413,6 +413,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId p.annex = toPackedLib(s.annex, c, m) when hasFFI: p.cname = toLitId(s.cname, m) + p.instantiatedFrom = s.instantiatedFrom.safeItemId(c, m) # fill the reserved slot, nothing else: m.syms[s.itemId.item] = p @@ -813,6 +814,7 @@ proc loadProcHeader(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: proc loadProcBody(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; tree: PackedTree; n: NodePos): PNode = + result = nil var i = 0 for n0 in sonsReadonly(tree, n): if i == bodyPos: @@ -875,6 +877,7 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; if externalName != "": result.loc.r = rope externalName result.loc.flags = s.locFlags + result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom) proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym = if s == nilItemId: @@ -916,7 +919,7 @@ proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; result.attachedOps[op] = loadSym(c, g, si, item) result.typeInst = loadType(c, g, si, t.typeInst) for son in items t.types: - result.sons.add loadType(c, g, si, son) + result.addSon loadType(c, g, si, son) loadAstBody(t, n) when false: for gen, id in items t.methods: @@ -1147,6 +1150,8 @@ proc initRodIter*(it: var RodIter; config: ConfigRef, cache: IdentCache; if it.i < it.values.len: result = loadSym(it.decoder, g, int(module), it.values[it.i]) inc it.i + else: + result = nil proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: FileIndex, importHidden: bool): PSym = @@ -1164,11 +1169,15 @@ proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache; if it.i < it.values.len: result = loadSym(it.decoder, g, int(module), it.values[it.i]) inc it.i + else: + result = nil proc nextRodIter*(it: var RodIter; g: var PackedModuleGraph): PSym = if it.i < it.values.len: result = loadSym(it.decoder, g, it.module, it.values[it.i]) inc it.i + else: + result = nil iterator interfaceSymbols*(config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: FileIndex; @@ -1201,7 +1210,7 @@ proc searchForCompilerproc*(m: LoadedModule; name: string): int32 = # ------------------------- .rod file viewer --------------------------------- proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) = - var m: PackedModule + var m: PackedModule = PackedModule() let err = loadRodFile(rodfile, m, config, ignoreConfig=true) if err != ok: config.quitOrRaise "Error: could not load: " & $rodfile.string & " reason: " & $err diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim index cbba591c5a8f9..ab49b3b7a18d2 100644 --- a/compiler/ic/navigator.nim +++ b/compiler/ic/navigator.nim @@ -34,7 +34,11 @@ proc isTracked(current, trackPos: PackedLineInfo, tokenLen: int): bool = if current.file == trackPos.file and current.line == trackPos.line: let col = trackPos.col if col >= current.col and col < current.col+tokenLen: - return true + result = true + else: + result = false + else: + result = false proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool = result = s.name != LitId(0) and diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 0bf5cd4c31103..b87348c5a0497 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -71,6 +71,7 @@ type when hasFFI: cname*: LitId constraint*: NodeId + instantiatedFrom*: PackedItemId PackedType* = object kind*: TTypeKind @@ -305,6 +306,7 @@ proc sons3*(tree: PackedTree; n: NodePos): (NodePos, NodePos, NodePos) = result = (NodePos a, NodePos b, NodePos c) proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos = + result = default(NodePos) if tree.nodes[n.int].kind > nkNilLit: var count = 0 for child in sonsReadonly(tree, n): diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index e492624d043be..41e85084f1563 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -215,7 +215,7 @@ proc storeHeader*(f: var RodFile) = proc loadHeader*(f: var RodFile) = ## Loads the header which is described by `cookie`. if f.err != ok: return - var thisCookie: array[cookie.len, byte] + var thisCookie: array[cookie.len, byte] = default(array[cookie.len, byte]) if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len: setError f, ioFailure elif thisCookie != cookie: @@ -231,13 +231,14 @@ proc storeSection*(f: var RodFile; s: RodSection) = proc loadSection*(f: var RodFile; expected: RodSection) = ## read the bytes value of s, sets and error if the section is incorrect. if f.err != ok: return - var s: RodSection + var s: RodSection = default(RodSection) loadPrim(f, s) if expected != s and f.err == ok: setError f, wrongSection proc create*(filename: string): RodFile = ## create the file and open it for writing + result = default(RodFile) if not open(result.f, filename, fmWrite): setError result, cannotOpen @@ -245,5 +246,6 @@ proc close*(f: var RodFile) = close(f.f) proc open*(filename: string): RodFile = ## open the file for reading + result = default(RodFile) if not open(result.f, filename, fmRead): setError result, cannotOpen diff --git a/compiler/importer.nim b/compiler/importer.nim index 39a447b709376..dcdd0bb49e430 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -14,6 +14,7 @@ import semdata, modulepaths, sigmatch, lineinfos, sets, modulegraphs, wordrecg, tables from strutils import `%` +from sequtils import addUnique when defined(nimPreviewSlimSystem): import std/assertions @@ -113,6 +114,7 @@ proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) = proc splitPragmas(c: PContext, n: PNode): (PNode, seq[TSpecialWord]) = template bail = globalError(c.config, n.info, "invalid pragma") + result = (nil, @[]) if n.kind == nkPragmaExpr: if n.len == 2 and n[1].kind == nkPragma: result[0] = n[0] @@ -227,11 +229,6 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im else: for i in 0..n.safeLen-1: importForwarded(c, n[i], exceptSet, fromMod, importSet) - -proc addUnique[T](x: var seq[T], y: sink T) {.noSideEffect.} = - for i in 0..high(x): - if x[i] == y: return - x.add y proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym = result = realModule @@ -250,7 +247,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool) result.options.incl optImportHidden c.unusedImports.add((result, n.info)) c.importModuleMap[result.id] = realModule.id - c.importModuleLookup.mgetOrPut(realModule.name.id, @[]).addUnique realModule.id + c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] = var ret: typeof(result) @@ -307,6 +304,8 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = suggestSym(c.graph, n.info, result, c.graph.usageSym, false) importStmtResult.add newSymNode(result, n.info) #newStrNode(toFullPath(c.config, f), n.info) + else: + result = nil proc afterImport(c: PContext, m: PSym) = # fixes bug #17510, for re-exported symbols diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index c5920f4b34cbb..a5ec6c21a691e 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -36,6 +36,7 @@ type body: PNode otherUsage: TLineInfo inUncheckedAssignSection: int + inEnsureMove: int Scope = object # we do scope-based memory management. # a scope is comparable to an nkStmtListExpr like @@ -78,11 +79,11 @@ proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = proc nestedScope(parent: var Scope; body: PNode): Scope = Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent)) -proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode +proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode type MoveOrCopyFlag = enum - IsDecl, IsExplicitSink + IsDecl, IsExplicitSink, IsReturn proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; flags: set[MoveOrCopyFlag] = {}): PNode @@ -184,7 +185,9 @@ proc isCursor(n: PNode): bool = template isUnpackedTuple(n: PNode): bool = ## we move out all elements of unpacked tuples, ## hence unpacked tuples themselves don't need to be destroyed - (n.kind == nkSym and n.sym.kind == skTemp and n.sym.typ.kind == tyTuple) + ## except it's already a cursor + (n.kind == nkSym and n.sym.kind == skTemp and + n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags) proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFromCopy = false) = var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">" @@ -269,7 +272,7 @@ proc deepAliases(dest, ri: PNode): bool = proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode = if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or - isNoInit(dest): + isNoInit(dest) or IsReturn in flags: # optimize sink call into a bitwise memcopy result = newTree(nkFastAsgn, dest, ri) else: @@ -332,6 +335,9 @@ proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode = assert ri.typ != nil proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): PNode = + if c.inEnsureMove > 0: + localError(c.graph.config, ri.info, errFailedMove, "cannot move '" & $ri & + "', which introduces an implicit copy") let t = dest.typ if tfHasOwned in t.flags and ri.kind != nkNilLit: # try to improve the error message here: @@ -400,7 +406,7 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = # generate: (let tmp = v; reset(v); tmp) - if not hasDestructor(c, n.typ): + if (not hasDestructor(c, n.typ)) and c.inEnsureMove == 0: assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) result = copyTree(n) else: @@ -419,7 +425,8 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = result.add v let nn = skipConv(n) - c.genMarkCyclic(result, nn) + if hasDestructor(c, n.typ): + c.genMarkCyclic(result, nn) let wasMovedCall = c.genWasMoved(nn) result.add wasMovedCall result.add tempAsNode @@ -427,6 +434,7 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = proc isCapturedVar(n: PNode): bool = let root = getRoot(n) if root != nil: result = root.name.s[0] == ':' + else: result = false proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) @@ -462,6 +470,9 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = message(c.graph.config, n.info, hintPerformance, ("passing '$1' to a sink parameter introduces an implicit copy; " & "if possible, rearrange your program's control flow to prevent it") % $n) + if c.inEnsureMove > 0: + localError(c.graph.config, n.info, errFailedMove, + ("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n) else: if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: assert(not containsManagedMemory(n.typ)) @@ -725,7 +736,9 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false, result[^1] = maybeVoid(n[^1], s) dec c.inUncheckedAssignSection, inUncheckedAssignSection - else: assert(false) + else: + result = nil + assert(false) proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode = if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty: @@ -752,7 +765,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode = result.add copyNode(n[0]) s.needsTry = true -proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}): PNode = +proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode = if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt, nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}: template process(child, s): untyped = p(child, c, s, mode) @@ -765,7 +778,15 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing result = passCopyToSink(n, c, s) elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} + nkCallKinds + nkLiterals: - result = p(n, c, s, consumed) + if n.kind in nkCallKinds and n[0].kind == nkSym: + if n[0].sym.magic == mEnsureMove: + inc c.inEnsureMove + result = p(n[1], c, s, sinkArg) + dec c.inEnsureMove + else: + result = p(n, c, s, consumed) + else: + result = p(n, c, s, consumed) elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)): # Sinked params can be consumed only once. We need to reset the memory @@ -837,6 +858,12 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing if mode == normal and isRefConstr: result = ensureDestruction(result, n, c, s) of nkCallKinds: + if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove: + inc c.inEnsureMove + result = p(n[1], c, s, sinkArg) + dec c.inEnsureMove + return + let inSpawn = c.inSpawn if n[0].kind == nkSym and n[0].sym.magic == mSpawn: c.inSpawn.inc @@ -922,7 +949,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}: cycleCheck(n, c) assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn} - let flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {} + var flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {} + if inReturn: + flags.incl(IsReturn) result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags) elif isDiscriminantField(n[0]): result = c.genDiscriminantAsgn(s, n) @@ -1006,7 +1035,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing of nkReturnStmt: result = shallowCopy(n) for i in 0.. 0 and isDangerousSeq(ri.typ): + inc c.inEnsureMove, isEnsureMove result = c.genCopy(dest, ri, flags) + dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) else: @@ -1119,12 +1161,15 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) elif ri.sym.kind != skParam and ri.sym.owner == c.owner and - isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri): + isLastRead(ri, c, s) and canBeMoved(c, dest.typ) and not isCursor(ri) and + not ({sfGlobal, sfPure} <= ri.sym.flags): # Rule 3: `=sink`(x, z); wasMoved(z) let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: + inc c.inEnsureMove, isEnsureMove result = c.genCopy(dest, ri, flags) + dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast: @@ -1142,7 +1187,9 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopy let snk = c.genSink(s, dest, ri, flags) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: + inc c.inEnsureMove, isEnsureMove result = c.genCopy(dest, ri, flags) + dec c.inEnsureMove, isEnsureMove result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) diff --git a/compiler/installer.ini b/compiler/installer.ini index 49ee59b0d0c59..d12127bde91c9 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -71,6 +71,7 @@ Files: "testament" Files: "nimsuggest" Files: "nimsuggest/tests/*.nim" Files: "changelogs/*.md" +Files: "ci/funs.sh" [Lib] Files: "lib" diff --git a/compiler/int128.nim b/compiler/int128.nim index b0341eb37958d..18e751f989644 100644 --- a/compiler/int128.nim +++ b/compiler/int128.nim @@ -171,6 +171,7 @@ proc addToHex*(result: var string; arg: Int128) = i -= 1 proc toHex*(arg: Int128): string = + result = "" result.addToHex(arg) proc inc*(a: var Int128, y: uint32 = 1) = @@ -330,8 +331,8 @@ proc `*`*(a: Int128, b: int32): Int128 = if b < 0: result = -result -proc `*=`*(a: var Int128, b: int32): Int128 = - result = result * b +proc `*=`(a: var Int128, b: int32) = + a = a * b proc makeInt128(high, low: uint64): Int128 = result.udata[0] = cast[uint32](low) @@ -360,6 +361,7 @@ proc `*=`*(a: var Int128, b: Int128) = import bitops proc fastLog2*(a: Int128): int = + result = 0 if a.udata[3] != 0: return 96 + fastLog2(a.udata[3]) if a.udata[2] != 0: @@ -571,4 +573,4 @@ proc maskBytes*(arg: Int128, numbytes: int): Int128 {.noinit.} = of 8: return maskUInt64(arg) else: - assert(false, "masking only implemented for 1, 2, 4 and 8 bytes") + raiseAssert "masking only implemented for 1, 2, 4 and 8 bytes" diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index 273bfb7f9f2ba..5fd1b8d51b5cb 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -21,6 +21,7 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool = case n.kind of nkRecList: + result = false for i in 0..>> 0" - else: rope"" - + of 1: "& 0xff" + of 2: "& 0xffff" + of 4: ">>> 0" + else: "" -template unsignedTrimmer(size: BiggestInt): Rope = - size.unsignedTrimmerJS +proc signedTrimmer(size: BiggestInt): string = + # sign extension is done by shifting to the left and then back to the right + "<< $1 >> $1" % [$(32 - size * 8)] proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, reassign: static[bool] = false) = - var x, y: TCompRes + var x, y: TCompRes = default(TCompRes) gen(p, n[1], x) gen(p, n[2], y) let size = n[1].typ.skipTypes(abstractRange).size @@ -608,8 +611,8 @@ template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) = proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = var - x, y: TCompRes - xLoc, yLoc: Rope + x, y: TCompRes = default(TCompRes) + xLoc, yLoc: Rope = "" let i = ord(optOverflowCheck notin p.options) useMagic(p, jsMagics[op][i]) if n.len > 2: @@ -626,6 +629,13 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = template applyFormat(frmtA, frmtB) = if i == 0: applyFormat(frmtA) else: applyFormat(frmtB) + template bitwiseExpr(op: string) = + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.kind in {tyUInt, tyUInt32}: + r.res = "(($1 $2 $3) >>> 0)" % [xLoc, op, yLoc] + else: + r.res = "($1 $2 $3)" % [xLoc, op, yLoc] + case op of mAddI: if i == 0: @@ -666,13 +676,55 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = applyFormat("modInt64($1, $2)", "$1 % $2") else: applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)") - of mSucc: applyFormat("addInt($1, $2)", "($1 + $2)") - of mPred: applyFormat("subInt($1, $2)", "($1 - $2)") + of mSucc: + let typ = n[1].typ.skipTypes(abstractVarRange) + case typ.kind + of tyUInt..tyUInt32: + binaryUintExpr(p, n, r, "+") + of tyUInt64: + if optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asUintN(64, $1 + BigInt($2))") + else: binaryUintExpr(p, n, r, "+") + elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + if optOverflowCheck notin p.options: + applyFormat("BigInt.asIntN(64, $1 + BigInt($2))") + else: binaryExpr(p, n, r, "addInt64", "addInt64($1, BigInt($2))") + else: + if optOverflowCheck notin p.options: applyFormat("$1 + $2") + else: binaryExpr(p, n, r, "addInt", "addInt($1, $2)") + of mPred: + let typ = n[1].typ.skipTypes(abstractVarRange) + case typ.kind + of tyUInt..tyUInt32: + binaryUintExpr(p, n, r, "-") + of tyUInt64: + if optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asUintN(64, $1 - BigInt($2))") + else: binaryUintExpr(p, n, r, "-") + elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + if optOverflowCheck notin p.options: + applyFormat("BigInt.asIntN(64, $1 - BigInt($2))") + else: binaryExpr(p, n, r, "subInt64", "subInt64($1, BigInt($2))") + else: + if optOverflowCheck notin p.options: applyFormat("$1 - $2") + else: binaryExpr(p, n, r, "subInt", "subInt($1, $2)") of mAddF64: applyFormat("($1 + $2)", "($1 + $2)") of mSubF64: applyFormat("($1 - $2)", "($1 - $2)") of mMulF64: applyFormat("($1 * $2)", "($1 * $2)") of mDivF64: applyFormat("($1 / $2)", "($1 / $2)") - of mShrI: applyFormat("", "") + of mShrI: + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))") + elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: + applyFormat("($1 >> BigInt($2))") + else: + if typ.kind in {tyInt..tyInt32}: + let trimmerU = unsignedTrimmer(typ.size) + let trimmerS = signedTrimmer(typ.size) + r.res = "((($1 $2) >>> $3) $4)" % [xLoc, trimmerU, yLoc, trimmerS] + else: + applyFormat("($1 >>> $2)") of mShlI: let typ = n[1].typ.skipTypes(abstractVarRange) if typ.size == 8: @@ -683,21 +735,27 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = else: applyFormat("($1 * Math.pow(2, $2))") else: - applyFormat("($1 << $2)", "($1 << $2)") + if typ.kind in {tyUInt..tyUInt32}: + let trimmer = unsignedTrimmer(typ.size) + r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer] + else: + let trimmer = signedTrimmer(typ.size) + r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer] of mAshrI: let typ = n[1].typ.skipTypes(abstractVarRange) if typ.size == 8: - if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: - applyFormat("BigInt.asIntN(64, $1 >> BigInt($2))") - elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: - applyFormat("BigInt.asUintN(64, $1 >> BigInt($2))") + if optJsBigInt64 in p.config.globalOptions: + applyFormat("($1 >> BigInt($2))") else: applyFormat("Math.floor($1 / Math.pow(2, $2))") else: - applyFormat("($1 >> $2)", "($1 >> $2)") - of mBitandI: applyFormat("($1 & $2)", "($1 & $2)") - of mBitorI: applyFormat("($1 | $2)", "($1 | $2)") - of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)") + if typ.kind in {tyUInt..tyUInt32}: + applyFormat("($1 >>> $2)") + else: + applyFormat("($1 >> $2)") + of mBitandI: bitwiseExpr("&") + of mBitorI: bitwiseExpr("|") + of mBitxorI: bitwiseExpr("^") of mMinI: applyFormat("nimMin($1, $2)", "nimMin($1, $2)") of mMaxI: applyFormat("nimMax($1, $2)", "nimMax($1, $2)") of mAddU: applyFormat("", "") @@ -733,7 +791,16 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mAbsI: applyFormat("absInt($1)", "Math.abs($1)") of mNot: applyFormat("!($1)", "!($1)") of mUnaryPlusI: applyFormat("+($1)", "+($1)") - of mBitnotI: applyFormat("~($1)", "~($1)") + of mBitnotI: + let typ = n[1].typ.skipTypes(abstractVarRange) + if typ.kind in {tyUInt..tyUInt64}: + if typ.size == 8 and optJsBigInt64 in p.config.globalOptions: + applyFormat("BigInt.asUintN(64, ~($1))") + else: + let trimmer = unsignedTrimmer(typ.size) + r.res = "(~($1) $2)" % [xLoc, trimmer] + else: + applyFormat("~($1)") of mUnaryPlusF64: applyFormat("+($1)", "+($1)") of mUnaryMinusF64: applyFormat("-($1)", "-($1)") of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)") @@ -760,24 +827,13 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = arithAux(p, n, r, op) of mModI: arithAux(p, n, r, op) - of mShrI: - var x, y: TCompRes - gen(p, n[1], x) - gen(p, n[2], y) - let typ = n[1].typ.skipTypes(abstractVarRange) - if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: - r.res = "BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))" % [x.rdLoc, y.rdLoc] - elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: - r.res = "($1 >> BigInt($2))" % [x.rdLoc, y.rdLoc] - else: - r.res = "($1 >>> $2)" % [x.rdLoc, y.rdLoc] of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr: arithAux(p, n, r, op) of mEqRef: if mapType(n[1].typ) != etyBaseIndex: arithAux(p, n, r, op) else: - var x, y: TCompRes + var x, y: TCompRes = default(TCompRes) gen(p, n[1], x) gen(p, n[2], y) r.res = "($# == $# && $# == $#)" % [x.address, y.address, x.res, y.res] @@ -810,7 +866,7 @@ proc genLineDir(p: PProc, n: PNode) = p.previousFileName = currentFileName proc genWhileStmt(p: PProc, n: PNode) = - var cond: TCompRes + var cond: TCompRes = default(TCompRes) internalAssert p.config, isEmptyType(n.typ) genLineDir(p, n) inc(p.unique) @@ -905,6 +961,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = elif it.kind == nkType: throwObj = it else: + throwObj = nil internalError(p.config, n.info, "genTryStmt") if orExpr != "": orExpr.add("||") @@ -945,7 +1002,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = proc genRaiseStmt(p: PProc, n: PNode) = if n[0].kind != nkEmpty: - var a: TCompRes + var a: TCompRes = default(TCompRes) gen(p, n[0], a) let typ = skipTypes(n[0].typ, abstractPtrs) genLineDir(p, n) @@ -959,7 +1016,7 @@ proc genRaiseStmt(p: PProc, n: PNode) = proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var - a, b, cond, stmt: TCompRes + a, b, cond, stmt: TCompRes = default(TCompRes) genLineDir(p, n) gen(p, n[0], cond) let typeKind = skipTypes(n[0].typ, abstractVar).kind @@ -1093,7 +1150,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = if false: discard else: - var r: TCompRes + var r = default(TCompRes) gen(p, it, r) if it.typ.kind == tyPointer: @@ -1109,13 +1166,13 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) = p.body.add(r.rdLoc) else: - var r: TCompRes + var r: TCompRes = default(TCompRes) gen(p, it, r) p.body.add(r.rdLoc) p.body.add "\L" proc genIf(p: PProc, n: PNode, r: var TCompRes) = - var cond, stmt: TCompRes + var cond, stmt: TCompRes = default(TCompRes) var toClose = 0 if not isEmptyType(n.typ): r.kind = resVal @@ -1152,6 +1209,7 @@ proc generateHeader(p: PProc, typ: PType): Rope = result.add("_Idx") proc countJsParams(typ: PType): int = + result = 0 for i in 1..= 3: # echo "BEGIN generating code for: " & prc.name.s var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options) @@ -2646,7 +2718,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = else: returnStmt = "return $#;$n" % [a.res] - var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, dontUseCache) + var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, {}) if sfInjectDestructors in prc.flags: transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody) @@ -2698,7 +2770,7 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = # echo "END generated code for: " & prc.name.s proc genStmt(p: PProc, n: PNode) = - var r: TCompRes + var r: TCompRes = default(TCompRes) gen(p, n, r) if r.res != "": lineF(p, "$#;$n", [r.res]) @@ -2725,26 +2797,14 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = let fromInt = (src.kind in tyInt..tyInt32) let fromUint = (src.kind in tyUInt..tyUInt32) - if toUint and (fromInt or fromUint): - let trimmer = unsignedTrimmer(dest.size) - r.res = "($1 $2)" % [r.res, trimmer] - elif toUint and src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: - r.res = "Number(BigInt.asUintN($1, $2))" % [$(dest.size * 8), r.res] + if toUint: + if fromInt or fromUint: + r.res = "Number(BigInt.asUintN($1, BigInt($2)))" % [$(dest.size * 8), r.res] + elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: + r.res = "Number(BigInt.asUintN($1, $2))" % [$(dest.size * 8), r.res] elif toInt: - if fromInt: - return - elif fromUint: - if src.size == 4 and dest.size == 4: - # XXX prevent multi evaluations - r.res = "($1 | 0)" % [r.res] - else: - let trimmer = unsignedTrimmer(dest.size) - let minuend = case dest.size - of 1: "0xfe" - of 2: "0xfffe" - of 4: "0xfffffffe" - else: "" - r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer] + if fromInt or fromUint: + r.res = "Number(BigInt.asIntN($1, BigInt($2)))" % [$(dest.size * 8), r.res] elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions: r.res = "Number(BigInt.asIntN($1, $2))" % [$(dest.size * 8), r.res] elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions: @@ -2755,10 +2815,12 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = elif src.kind == tyUInt64: r.res = "BigInt.asIntN(64, $1)" % [r.res] elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions: - if fromInt or fromUint: + if fromUint or src.kind in {tyBool, tyChar, tyEnum}: r.res = "BigInt($1)" % [r.res] + elif fromInt: # could be negative + r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res] elif src.kind in {tyFloat..tyFloat64}: - r.res = "BigInt(Math.trunc($1))" % [r.res] + r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res] elif src.kind == tyInt64: r.res = "BigInt.asUintN(64, $1)" % [r.res] elif dest.kind in tyFloat..tyFloat64: @@ -2790,11 +2852,17 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = if optJsBigInt64 in p.config.globalOptions: r.res.add('n') of tyInt64: - r.res = rope(n.intVal) + let wrap = n.intVal < 0 # wrap negative integers with parens + if wrap: r.res.add '(' + r.res.addInt n.intVal if optJsBigInt64 in p.config.globalOptions: r.res.add('n') + if wrap: r.res.add ')' else: - r.res = rope(n.intVal) + let wrap = n.intVal < 0 # wrap negative integers with parens + if wrap: r.res.add '(' + r.res.addInt n.intVal + if wrap: r.res.add ')' r.kind = resExpr of nkNilLit: if isEmptyType(n.typ): @@ -2833,7 +2901,11 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = r.res = rope"Infinity" of fcNegInf: r.res = rope"-Infinity" - else: r.res = rope(f.toStrMaxPrecision) + else: + if n.typ.skipTypes(abstractVarRange).kind == tyFloat32: + r.res.addFloatRoundtrip(f.float32) + else: + r.res.addFloatRoundtrip(f) r.kind = resExpr of nkCallKinds: if isEmptyType(n.typ): @@ -2934,13 +3006,11 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = proc newModule(g: ModuleGraph; module: PSym): BModule = ## Create a new JS backend module node. - new(result) - result.module = module - result.sigConflicts = initCountTable[SigHash]() if g.backend == nil: g.backend = newGlobals() - result.graph = g - result.config = g.config + result = BModule(module: module, sigConflicts: initCountTable[SigHash](), + graph: g, config: g.config + ) if sfSystemModule in module.flags: PGlobals(g.backend).inSystem = true @@ -3013,6 +3083,7 @@ proc processJSCodeGen*(b: PPassContext, n: PNode): PNode = if m.module == nil: internalError(m.config, n.info, "myProcess") let globals = PGlobals(m.graph.backend) var p = newInitProc(globals, m) + m.initProc = p p.unique = globals.unique genModule(p, n) p.g.code.add(p.locals) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index ce36123b3a0e6..fdba7ba3d1478 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -187,6 +187,8 @@ proc getEnvParam*(routine: PSym): PSym = if hidden.kind == nkSym and hidden.sym.name.s == paramName: result = hidden.sym assert sfFromGeneric in result.flags + else: + result = nil proc interestingVar(s: PSym): bool {.inline.} = result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and @@ -199,6 +201,8 @@ proc illegalCapture(s: PSym): bool {.inline.} = proc isInnerProc(s: PSym): bool = if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone: result = s.skipGenericOwner.kind in routineKinds + else: + result = false proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = # Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would @@ -293,33 +297,34 @@ proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = let s = n.sym + let isEnv = s.name.id == getIdent(g.cache, ":env").id if illegalCapture(s): localError(g.config, n.info, ("'$1' is of type <$2> which cannot be captured as it would violate memory" & " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." & " Consider using a which can be captured.") % [s.name.s, typeToString(s.typ), g.config$s.info]) - elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags): + elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv): localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % [s.name.s, owner.name.s, $owner.typ.callConv]) incl(owner.typ.flags, tfCapturesEnv) - owner.typ.callConv = ccClosure + if not isEnv: + owner.typ.callConv = ccClosure type DetectionPass = object processed, capturedVars: IntSet ownerToType: Table[int, PType] somethingToDo: bool + inTypeOf: bool graph: ModuleGraph idgen: IdGenerator proc initDetectionPass(g: ModuleGraph; fn: PSym; idgen: IdGenerator): DetectionPass = - result.processed = initIntSet() - result.capturedVars = initIntSet() - result.ownerToType = initTable[int, PType]() - result.processed.incl(fn.id) - result.graph = g - result.idgen = idgen + result = DetectionPass(processed: toIntSet([fn.id]), + capturedVars: initIntSet(), ownerToType: initTable[int, PType](), + graph: g, idgen: idgen + ) discard """ proc outer = @@ -413,6 +418,9 @@ Consider: """ +proc isTypeOf(n: PNode): bool = + n.kind == nkSym and n.sym.magic in {mTypeOf, mType} + proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) = var cp = getEnvParam(fn) let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner @@ -441,15 +449,18 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if innerProc: if s.isIterator: c.somethingToDo = true if not c.processed.containsOrIncl(s.id): - let body = transformBody(c.graph, c.idgen, s, useCache) + let body = transformBody(c.graph, c.idgen, s, {useCache}) detectCapturedVars(body, s, c) let ow = s.skipGenericOwner + let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator + let interested = interestingVar(s) if ow == owner: if owner.isIterator: c.somethingToDo = true addClosureParam(c, owner, n.info) if interestingIterVar(s): - if not c.capturedVars.containsOrIncl(s.id): + if not c.capturedVars.contains(s.id): + if not c.inTypeOf: c.capturedVars.incl(s.id) let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) #let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr}) @@ -458,7 +469,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = else: discard addField(obj, s, c.graph.cache, c.idgen) # direct or indirect dependency: - elif (innerProc and not s.isIterator and s.typ.callConv == ccClosure) or interestingVar(s): + elif innerClosure or interested: discard """ proc outer() = var x: int @@ -475,10 +486,12 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = addClosureParam(c, owner, n.info) #echo "capturing ", n.info # variable 's' is actually captured: - if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id): - let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr}) - #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) - discard addField(obj, s, c.graph.cache, c.idgen) + if interestingVar(s): + if not c.capturedVars.contains(s.id): + if not c.inTypeOf: c.capturedVars.incl(s.id) + let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr}) + #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) + discard addField(obj, s, c.graph.cache, c.idgen) # create required upFields: var w = owner.skipGenericOwner if isInnerProc(w) or owner.isIterator: @@ -510,9 +523,14 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = detectCapturedVars(n[namePos], owner, c) of nkReturnStmt: detectCapturedVars(n[0], owner, c) + of nkIdentDefs: + detectCapturedVars(n[^1], owner, c) else: + if n.isCallExpr and n[0].isTypeOf: + c.inTypeOf = true for i in 0..= 0 and x[i] == ' ': dec(i) if i >= 0 and x[i] in s: result = true + else: + result = false const LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', @@ -104,6 +95,7 @@ proc continueLine(line: string, inTripleString: bool): bool {.inline.} = line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs)) proc countTriples(s: string): int = + result = 0 var i = 0 while i+2 < s.len: if s[i] == '"' and s[i+1] == '"' and s[i+2] == '"': diff --git a/compiler/lookups.nim b/compiler/lookups.nim index dce841e2f2228..2bdf3a1e07259 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -72,7 +72,7 @@ proc addUniqueSym*(scope: PScope, s: PSym): PSym = proc openScope*(c: PContext): PScope {.discardable.} = result = PScope(parent: c.currentScope, - symbols: newStrTable(), + symbols: initStrTable(), depthLevel: c.scopeDepth + 1) c.currentScope = result @@ -254,6 +254,41 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy if s.kind in filter: result.add s +proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): bool = + result = false + block outer: + for scope in allScopes(c.currentScope): + var ti: TIdentIter + var candidate = initIdentIter(ti, scope.symbols, s) + var scopeHasCandidate = false + while candidate != nil: + if candidate.kind in filter: + if scopeHasCandidate: + # 2 candidates in same scope, ambiguous + return true + else: + scopeHasCandidate = true + sym = candidate + candidate = nextIdentIter(ti, scope.symbols) + if scopeHasCandidate: + # scope had a candidate but wasn't ambiguous + return false + + var importsHaveCandidate = false + var marked = initIntSet() + for im in c.imports.mitems: + for s in symbols(im, marked, s, c.graph): + if s.kind in filter: + if importsHaveCandidate: + # 2 candidates among imports, ambiguous + return true + else: + importsHaveCandidate = true + sym = s + if importsHaveCandidate: + # imports had a candidate but wasn't ambiguous + return false + proc errorSym*(c: PContext, n: PNode): PSym = ## creates an error symbol to avoid cascading errors (for IDE support) var m = n @@ -298,7 +333,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = var it: TTabIter var s = initTabIter(it, scope.symbols) var missingImpls = 0 - var unusedSyms: seq[tuple[sym: PSym, key: string]] + var unusedSyms: seq[tuple[sym: PSym, key: string]] = @[] while s != nil: if sfForward in s.flags and s.kind notin {skType, skModule}: # too many 'implementation of X' errors are annoying @@ -404,7 +439,7 @@ proc openShadowScope*(c: PContext) = ## opens a shadow scope, just like any other scope except the depth is the ## same as the parent -- see `isShadowScope`. c.currentScope = PScope(parent: c.currentScope, - symbols: newStrTable(), + symbols: initStrTable(), depthLevel: c.scopeDepth) proc closeShadowScope*(c: PContext) = @@ -458,7 +493,7 @@ proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) = for (sym, depth, isLocal) in allSyms(c): let depth = -depth - 1 let dist = editDistance(name0, sym.name.s.nimIdentNormalize) - var msg: string + var msg: string = "" msg.add "\n ($1, $2): '$3'" % [$dist, $depth, sym.name.s] list.push SpellCandidate(dist: dist, depth: depth, msg: msg, sym: sym) @@ -488,6 +523,7 @@ proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PS var err = "ambiguous identifier: '" & s.name.s & "'" var i = 0 var ignoredModules = 0 + result = nil for candidate in importedItems(c, s.name): if i == 0: err.add " -- use one of the following:\n" else: err.add "\n" @@ -560,7 +596,7 @@ proc lookUp*(c: PContext, n: PNode): PSym = if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident) else: internalError(c.config, n.info, "lookUp") - return + return nil if amb: #contains(c.ambiguousSymbols, result.id): result = errorUseQualifier(c, n.info, result, amb) @@ -586,6 +622,8 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = amb = candidates.len > 1 if amb and checkAmbiguity in flags: errorUseQualifier(c, n.info, candidates) + else: + result = nil if result == nil: let candidates = allPureEnumFields(c, ident) if candidates.len > 0: @@ -623,6 +661,9 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = result = errorUndeclaredIdentifierHint(c, n[1], ident) elif n[1].kind == nkSym: result = n[1].sym + if result.owner != nil and result.owner != m and checkUndeclared in flags: + # dotExpr in templates can end up here + result = errorUndeclaredIdentifierHint(c, n[1], considerQuotedIdent(c, n[1])) elif checkUndeclared in flags and n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}: localError(c.config, n[1].info, "identifier expected, but got: " & @@ -638,6 +679,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = o.marked = initIntSet() case n.kind of nkIdent, nkAccQuoted: + result = nil var ident = considerQuotedIdent(c, n) var scope = c.currentScope o.mode = oimNoQualifier @@ -661,6 +703,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = result = n.sym o.mode = oimDone of nkDotExpr: + result = nil o.mode = oimOtherModule o.m = qualifiedLookUp(c, n[0], {checkUndeclared, checkModule}) if o.m != nil and o.m.kind == skModule: @@ -690,7 +733,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = o.symChoiceIndex = 1 o.marked = initIntSet() incl(o.marked, result.id) - else: discard + else: result = nil when false: if result != nil and result.kind == skStub: loadStub(result) @@ -705,6 +748,7 @@ proc lastOverloadScope*(o: TOverloadIter): int = else: result = -1 proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym = + result = nil assert o.currentScope == nil var idx = o.importIdx+1 o.importIdx = c.imports.len # assume the other imported modules lack this symbol too @@ -717,6 +761,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym inc idx proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym = + result = nil assert o.currentScope == nil while o.importIdx < c.imports.len: result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph) @@ -779,6 +824,8 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = break if result != nil: incl o.marked, result.id + else: + result = nil of oimSymChoiceLocalLookup: if o.currentScope != nil: result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked) @@ -802,13 +849,16 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = if result == nil: inc o.importIdx result = symChoiceExtension(o, c, n) + else: + result = nil when false: if result != nil and result.kind == skStub: loadStub(result) proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind]; flags: TSymFlags = {}): PSym = - var o: TOverloadIter + result = nil + var o: TOverloadIter = default(TOverloadIter) var a = initOverloadIter(o, c, n) while a != nil: if a.kind in kinds and flags <= a.flags: diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 3f67fc168eac6..42d0f1790c0d5 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -66,17 +66,8 @@ proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode = result = newNodeI(nkFastAsgn, le.info, 2) result[0] = le result[1] = newNodeIT(nkCall, ri.info, ri.typ) - if g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: - result[1].add newSymNode(getCompilerProc(g, "internalMove")) - result[1].add ri - result = newTreeI(nkStmtList, le.info, result, - newTree(nkCall, newSymNode( - getSysMagic(g, ri.info, "=wasMoved", mWasMoved)), - ri - )) - else: - result[1].add newSymNode(getSysMagic(g, ri.info, "move", mMove)) - result[1].add ri + result[1].add newSymNode(getSysMagic(g, ri.info, "move", mMove)) + result[1].add ri proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode = assert n.kind == nkVarTuple @@ -131,25 +122,6 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode = proc newTryFinally*(body, final: PNode): PNode = result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final)) -proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode = - let value = n.lastSon - result = newNodeI(nkStmtList, n.info) - - var temp = newSym(skTemp, getIdent(g.cache, "_"), idgen, owner, value.info, owner.options) - var v = newNodeI(nkLetSection, value.info) - let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info) - - var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3) - vpart[0] = tempAsNode - vpart[1] = newNodeI(nkTupleClassTy, value.info) - vpart[2] = value - v.add vpart - result.add(v) - - let lhs = n[0] - for i in 0.. 0: discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {}) @@ -196,7 +199,7 @@ proc commandScan(cache: IdentCache, config: ConfigRef) = if stream != nil: var L: Lexer - tok: Token + tok: Token = default(Token) initToken(tok) openLexer(L, f, stream, cache, config) while true: @@ -267,7 +270,7 @@ proc mainCommand*(graph: ModuleGraph) = # and it has added this define implictly, so we must undo that here. # A better solution might be to fix system.nim undefSymbol(conf.symbols, "useNimRtl") - of backendInvalid: doAssert false + of backendInvalid: raiseAssert "unreachable" proc compileToBackend() = customizeForBackend(conf.backend) @@ -277,7 +280,7 @@ proc mainCommand*(graph: ModuleGraph) = of backendCpp: commandCompileToC(graph) of backendObjc: commandCompileToC(graph) of backendJs: commandCompileToJS(graph) - of backendInvalid: doAssert false + of backendInvalid: raiseAssert "unreachable" template docLikeCmd(body) = when defined(leanCompiler): @@ -407,7 +410,7 @@ proc mainCommand*(graph: ModuleGraph) = wantMainModule(conf) commandView(graph) #msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!") - of cmdInteractive: commandInteractive(graph) + of cmdInteractive: commandInteractive(graph, isDefined(conf, "nir")) of cmdNimscript: if conf.projectIsCmd or conf.projectIsStdin: discard elif not fileExists(conf.projectFull): diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 08cdbfd0db36d..f6abb0a60895a 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -65,6 +65,7 @@ type CgenPass EvalPass InterpreterPass + NirReplPass GenDependPass Docgen2TexPass Docgen2JsonPass @@ -79,7 +80,8 @@ type procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId. attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc. methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods - memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual and ctor so far) + memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual,member and ctor so far). + initializersPerType*: Table[ItemId, PNode] # Type ID, AST call to the default ctor (c++ only) enumToStringProcs*: Table[ItemId, LazySym] emittedTypeInfo*: Table[string, FileIndex] @@ -98,6 +100,7 @@ type cache*: IdentCache vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will # be clarified in later compiler implementations. + repl*: RootRef # REPL state is shared project-wise. doStopCompile*: proc(): bool {.closure.} usageSym*: PSym # for nimsuggest owners*: seq[PSym] @@ -142,7 +145,7 @@ type isFrontend: bool] proc resetForBackend*(g: ModuleGraph) = - initStrTable(g.compilerprocs) + g.compilerprocs = initStrTable() g.typeInstCache.clear() g.procInstCache.clear() for a in mitems(g.attachedOps): @@ -196,8 +199,8 @@ template semtabAll*(g: ModuleGraph, m: PSym): TStrTable = g.ifaces[m.position].interfHidden proc initStrTables*(g: ModuleGraph, m: PSym) = - initStrTable(semtab(g, m)) - initStrTable(semtabAll(g, m)) + semtab(g, m) = initStrTable() + semtabAll(g, m) = initStrTable() proc strTableAdds*(g: ModuleGraph, m: PSym, s: PSym) = strTableAdd(semtab(g, m), s) @@ -368,6 +371,7 @@ proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) = setAttachedOp(g, module, dest, k, op) proc loadCompilerProc*(g: ModuleGraph; name: string): PSym = + result = nil if g.config.symbolFiles == disabledSf: return nil # slow, linear search, but the results are cached: @@ -458,7 +462,7 @@ proc initModuleGraphFields(result: ModuleGraph) = # A module ID of -1 means that the symbol is not attached to a module at all, # but to the module graph: result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32) - initStrTable(result.packageSyms) + result.packageSyms = initStrTable() result.deps = initIntSet() result.importDeps = initTable[FileIndex, seq[FileIndex]]() result.ifaces = @[] @@ -468,9 +472,9 @@ proc initModuleGraphFields(result: ModuleGraph) = result.suggestSymbols = initTable[FileIndex, seq[SymInfoPair]]() result.suggestErrors = initTable[FileIndex, seq[Suggest]]() result.methods = @[] - initStrTable(result.compilerprocs) - initStrTable(result.exposed) - initStrTable(result.packageTypes) + result.compilerprocs = initStrTable() + result.exposed = initStrTable() + result.packageTypes = initStrTable() result.emptyNode = newNode(nkEmpty) result.cacheSeqs = initTable[string, PNode]() result.cacheCounters = initTable[string, BiggestInt]() @@ -487,7 +491,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = initModuleGraphFields(result) proc resetAllModules*(g: ModuleGraph) = - initStrTable(g.packageSyms) + g.packageSyms = initStrTable() g.deps = initIntSet() g.ifaces = @[] g.importStack = @[] @@ -495,11 +499,12 @@ proc resetAllModules*(g: ModuleGraph) = g.usageSym = nil g.owners = @[] g.methods = @[] - initStrTable(g.compilerprocs) - initStrTable(g.exposed) + g.compilerprocs = initStrTable() + g.exposed = initStrTable() initModuleGraphFields(g) proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = + result = nil if fileIdx.int32 >= 0: if isCachedModule(g, fileIdx.int32): result = g.packed[fileIdx.int32].module @@ -579,6 +584,7 @@ proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) = if m != nil: g.suggestSymbols.del(fileIdx) g.suggestErrors.del(fileIdx) + g.resetForBackend incl m.flags, sfDirty proc unmarkAllDirty*(g: ModuleGraph) = @@ -605,6 +611,7 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) = proc needsCompilation*(g: ModuleGraph): bool = # every module that *depends* on this file is also dirty: + result = false for i in 0i32..= 0: result.add "\n" & indent & spaces(info.col) & '^' + else: + result = "" proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string = let title = case msg @@ -511,7 +515,8 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg) proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, - eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.gcsafe, noinline.} = + eh: TErrorHandling, info2: InstantiationInfo, isRaw = false, + ignoreError = false) {.gcsafe, noinline.} = var title: string color: ForegroundColor @@ -576,7 +581,8 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, " compiler msg initiated here", KindColor, KindFormat % $hintMsgOrigin, resetStyle, conf.unitSep) - handleError(conf, msg, eh, s, ignoreMsg) + if not ignoreError: + handleError(conf, msg, eh, s, ignoreMsg) if msg in fatalMsgs: # most likely would have died here but just in case, we restore state conf.m.errorOutputs = errorOutputsOld diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim index 5cc66f3ea43d6..96e0967702cb2 100644 --- a/compiler/nilcheck.nim +++ b/compiler/nilcheck.nim @@ -309,6 +309,7 @@ proc symbol(n: PNode): Symbol = # echo "symbol ", n, " ", n.kind, " ", result.int func `$`(map: NilMap): string = + result = "" var now = map var stack: seq[NilMap] = @[] while not now.isNil: @@ -416,7 +417,7 @@ proc moveOut(ctx: NilCheckerContext, map: NilMap, target: PNode) = if targetSetIndex != noSetIndex: var targetSet = map.sets[targetSetIndex] if targetSet.len > 1: - var other: ExprIndex + var other: ExprIndex = default(ExprIndex) for element in targetSet: if element.ExprIndex != targetIndex: @@ -561,7 +562,7 @@ proc derefWarning(n, ctx, map; kind: Nilability) = if n.info in ctx.warningLocations: return ctx.warningLocations.incl(n.info) - var a: seq[History] + var a: seq[History] = @[] if n.kind == nkSym: a = history(map, ctx.index(n)) var res = "" @@ -765,7 +766,7 @@ proc checkIf(n, ctx, map): Check = # the state of the conditions: negating conditions before the current one var layerHistory = newNilMap(mapIf) # the state after branch effects - var afterLayer: NilMap + var afterLayer: NilMap = nil # the result nilability for expressions var nilability = Safe @@ -862,9 +863,10 @@ proc checkInfix(n, ctx, map): Check = ## a or b : map is an union of a and b's ## a == b : use checkCondition ## else: no change, just check args + result = default(Check) if n[0].kind == nkSym: - var mapL: NilMap - var mapR: NilMap + var mapL: NilMap = nil + var mapR: NilMap = nil if n[0].sym.magic notin {mAnd, mEqRef}: mapL = checkCondition(n[1], ctx, map, false, false) mapR = checkCondition(n[2], ctx, map, false, false) @@ -947,7 +949,7 @@ proc checkCase(n, ctx, map): Check = let base = n[0] result.map = map.copyMap() result.nilability = Safe - var a: PNode + var a: PNode = nil for child in n: case child.kind: of nkOfBranch: @@ -1222,7 +1224,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = # TODO deeper nested elements? # A(field: B()) # # field: Safe -> - var elements: seq[(PNode, Nilability)] + var elements: seq[(PNode, Nilability)] = @[] for i, child in n: result = check(child, ctx, result.map) if i > 0: @@ -1333,7 +1335,7 @@ proc preVisit(ctx: NilCheckerContext, s: PSym, body: PNode, conf: ConfigRef) = ctx.symbolIndices = {resultId: resultExprIndex}.toTable() var cache = newIdentCache() ctx.expressions = SeqOfDistinct[ExprIndex, PNode](@[newIdentNode(cache.getIdent("result"), s.ast.info)]) - var emptySet: IntSet # set[ExprIndex] + var emptySet: IntSet = initIntSet() # set[ExprIndex] ctx.dependants = SeqOfDistinct[ExprIndex, IntSet](@[emptySet]) for i, arg in s.typ.n.sons: if i > 0: diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 5b418cfd3b2d8..04005368512c1 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -31,9 +31,6 @@ define:useStdoutAsStdmsg warning[ObservableStores]:off @end -@if nimHasWarnCastSizes: - warning[CastSizes]:on -@end @if nimHasWarningAsError: warningAsError[GcUnsafe2]:on @@ -46,3 +43,11 @@ define:useStdoutAsStdmsg @if nimHasWarnBareExcept: warningAserror[BareExcept]:on @end + + +@if nimUseStrictDefs: + experimental:strictDefs + warningAsError[Uninit]:on + warningAsError[ProveInit]:on +@end + diff --git a/compiler/nim.nim b/compiler/nim.nim index b28e8b20c6b2f..023a76ff9b12e 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -91,6 +91,9 @@ proc getNimRunExe(conf: ConfigRef): string = if conf.isDefined("mingw"): if conf.isDefined("i386"): result = "wine" elif conf.isDefined("amd64"): result = "wine64" + else: result = "" + else: + result = "" proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = let self = NimProg( @@ -113,7 +116,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = conf.backend = backendC if conf.selectedGC == gcUnselected: - if conf.backend in {backendC, backendCpp, backendObjc}: + if conf.backend in {backendC, backendCpp, backendObjc} or + (conf.cmd == cmdInteractive and isDefined(conf, "nir")): initOrcDefines(conf) mainCommand(graph) @@ -137,7 +141,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = # tasyncjs_fail` would fail, refs https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode if cmdPrefix.len == 0: cmdPrefix = findNodeJs().quoteShell cmdPrefix.add " --unhandled-rejections=strict" - else: doAssert false, $conf.backend + else: raiseAssert $conf.backend if cmdPrefix.len > 0: cmdPrefix.add " " # without the `cmdPrefix.len > 0` check, on windows you'd get a cryptic: # `The parameter is incorrect` diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 440d35fe52296..97a66f1cd99f8 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -37,11 +37,16 @@ proc isSpecial(ver: Version): bool = proc isValidVersion(v: string): bool = if v.len > 0: - if v[0] in {'#'} + Digits: return true + if v[0] in {'#'} + Digits: + result = true + else: + result = false + else: + result = false proc `<`*(ver: Version, ver2: Version): bool = ## This is synced from Nimble's version module. - + result = false # Handling for special versions such as "#head" or "#branch". if ver.isSpecial or ver2.isSpecial: if ver2.isSpecial and ($ver2).normalize == "#head": @@ -145,7 +150,7 @@ proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) = conf.lazyPaths.insert(AbsoluteDir path, 0) proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) = - var packages: PackageInfo + var packages: PackageInfo = initTable[string, tuple[version, checksum: string]]() var pos = dir.len-1 if dir[pos] in {DirSep, AltSep}: inc(pos) for k,p in os.walkDir(dir): diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index fceedb2c48161..78215d281a664 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -162,7 +162,7 @@ proc checkSymbol(L: Lexer, tok: Token) = lexMessage(L, errGenerated, "expected identifier, but got: " & $tok) proc parseAssignment(L: var Lexer, tok: var Token; - config: ConfigRef; condStack: var seq[bool]) = + config: ConfigRef; filename: AbsoluteFile; condStack: var seq[bool]) = if tok.ident != nil: if tok.ident.s == "-" or tok.ident.s == "--": confTok(L, tok, config, condStack) # skip unnecessary prefix @@ -205,6 +205,7 @@ proc parseAssignment(L: var Lexer, tok: var Token; checkSymbol(L, tok) val.add($tok) confTok(L, tok, config, condStack) + config.currentConfigDir = parentDir(filename.string) if percent: processSwitch(s, strtabs.`%`(val, config.configVars, {useEnvironment, useEmpty}), passPP, info, config) @@ -214,7 +215,7 @@ proc parseAssignment(L: var Lexer, tok: var Token; proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache; config: ConfigRef): bool = var - L: Lexer + L: Lexer = default(Lexer) tok: Token stream: PLLStream stream = llStreamOpen(filename, fmRead) @@ -224,10 +225,12 @@ proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache; tok.tokType = tkEof # to avoid a pointless warning var condStack: seq[bool] = @[] confTok(L, tok, config, condStack) # read in the first token - while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack) + while tok.tokType != tkEof: parseAssignment(L, tok, config, filename, condStack) if condStack.len > 0: lexMessage(L, errGenerated, "expected @end") closeLexer(L) return true + else: + result = false proc getUserConfigPath*(filename: RelativeFile): AbsoluteFile = result = getConfigDir().AbsoluteDir / RelativeDir"nim" / filename @@ -250,7 +253,7 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: template runNimScriptIfExists(path: AbsoluteFile, isMain = false) = let p = path # eval once - var s: PLLStream + var s: PLLStream = nil if isMain and optWasNimscript in conf.globalOptions: if conf.projectIsStdin: s = stdin.llStreamOpen elif conf.projectIsCmd: s = llStreamOpen(conf.cmdInput) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 8e8f4ac7bf1cd..e98de7e62b698 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -12,7 +12,7 @@ import ast, modules, condsyms, options, llstream, lineinfos, vm, vmdef, modulegraphs, idents, os, pathutils, - scriptconfig, std/compilesettings + scriptconfig, std/[compilesettings, tables] import pipelines @@ -78,6 +78,9 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) = assert i != nil assert i.mainModule != nil, "no main module selected" initStrTables(i.graph, i.mainModule) + i.graph.cacheSeqs.clear() + i.graph.cacheCounters.clear() + i.graph.cacheTables.clear() i.mainModule.ast = nil let s = if scriptStream != nil: scriptStream diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index 49c80065ae21a..59a542a8582a9 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -62,7 +62,9 @@ proc someInSet*(s: PNode, a, b: PNode): bool = result = false proc toBitSet*(conf: ConfigRef; s: PNode): TBitSet = - var first, j: Int128 + result = @[] + var first: Int128 = Zero + var j: Int128 = Zero first = firstOrd(conf, s.typ[0]) bitSetInit(result, int(getSize(conf, s.typ))) for i in 0.. 0, $n + +proc clearDest(c: var ProcCon; n: PNode; d: var Value) {.inline.} = + when false: + if n.typ.isNil or n.typ.kind == tyVoid: + let s = extractTemp(d) + if s != SymId(-1): + freeLoc(c.sm, s) + +proc isNotOpr(n: PNode): bool = + n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mNot + +proc jmpBack(c: var ProcCon; n: PNode; lab: LabelId) = + c.code.gotoLabel toLineInfo(c, n.info), GotoLoop, lab + +type + JmpKind = enum opcFJmp, opcTJmp + +proc xjmp(c: var ProcCon; n: PNode; jk: JmpKind; v: Value): LabelId = + result = newLabel(c.labelGen) + let info = toLineInfo(c, n.info) + buildTyped c.code, info, Select, Bool8Id: + c.code.copyTree Tree(v) + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, jk == opcTJmp) + c.code.gotoLabel info, Goto, result + +proc patch(c: var ProcCon; n: PNode; L: LabelId) = + addLabel c.code, toLineInfo(c, n.info), Label, L + +proc genWhile(c: var ProcCon; n: PNode) = + # lab1: + # cond, tmp + # fjmp tmp, lab2 + # body + # jmp lab1 + # lab2: + let info = toLineInfo(c, n.info) + let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) + withBlock(nil, info, lab1): + if isTrue(n[0]): + c.gen(n[1]) + c.jmpBack(n, lab1) + elif isNotOpr(n[0]): + var tmp = c.genx(n[0][1]) + let lab2 = c.xjmp(n, opcTJmp, tmp) + c.freeTemp(tmp) + c.gen(n[1]) + c.jmpBack(n, lab1) + c.patch(n, lab2) + else: + var tmp = c.genx(n[0]) + let lab2 = c.xjmp(n, opcFJmp, tmp) + c.freeTemp(tmp) + c.gen(n[1]) + c.jmpBack(n, lab1) + c.patch(n, lab2) + +proc genBlock(c: var ProcCon; n: PNode; d: var Value) = + openScope c.sm + let info = toLineInfo(c, n.info) + let lab1 = newLabel(c.labelGen) + + withBlock(n[0].sym, info, lab1): + c.gen(n[1], d) + + c.code.addLabel(info, Label, lab1) + closeScope c.sm + c.clearDest(n, d) + +proc jumpTo(c: var ProcCon; n: PNode; L: LabelId) = + c.code.addLabel(toLineInfo(c, n.info), Goto, L) + +proc genBreak(c: var ProcCon; n: PNode) = + if n[0].kind == nkSym: + for i in countdown(c.blocks.len-1, 0): + if c.blocks[i][0] == n[0].sym: + c.jumpTo n, c.blocks[i][1] + return + localError(c.config, n.info, "NIR problem: cannot find 'break' target") + else: + c.jumpTo n, c.blocks[c.blocks.high][1] + +proc genIf(c: var ProcCon; n: PNode; d: var Value) = + # if (!expr1) goto lab1; + # thenPart + # goto LEnd + # lab1: + # if (!expr2) goto lab2; + # thenPart2 + # goto LEnd + # lab2: + # elsePart + # Lend: + if isEmpty(d) and not isEmptyType(n.typ): d = getTemp(c, n) + var ending = newLabel(c.labelGen) + for i in 0..>3] &(1U<<((NU)($2)&7U)))!=0)" + + template body(target) = + buildTyped target, info, BoolNot, Bool8Id: + buildTyped target, info, Eq, t: + buildTyped target, info, BitAnd, t: + if c.m.types.g[setType].kind != ArrayTy: + copyTree target, a + else: + buildTyped target, info, ArrayAt, t: + copyTree target, a + buildTyped target, info, BitShr, t: + buildTyped target, info, Cast, expansion: + copyTree target, b + addIntVal target, c.m.integers, info, expansion, 3 + + buildTyped target, info, BitShl, t: + addIntVal target, c.m.integers, info, t, 1 + buildTyped target, info, BitAnd, t: + buildTyped target, info, Cast, expansion: + copyTree target, b + addIntVal target, c.m.integers, info, expansion, mask + addIntVal target, c.m.integers, info, t, 0 + intoDest d, info, t, body + + c.freeTemp(b) + c.freeTemp(a) + +proc genInSet(c: var ProcCon; n: PNode; d: var Value) = + let g {.cursor.} = c.m.graph + if n[1].kind == nkCurly and fewCmps(g.config, n[1]): + # a set constructor but not a constant set: + # do not emit the set, but generate a bunch of comparisons; and if we do + # so, we skip the unnecessary range check: This is a semantical extension + # that code now relies on. :-/ XXX + let elem = if n[2].kind in {nkChckRange, nkChckRange64}: n[2][0] + else: n[2] + let curly = n[1] + var ex: PNode = nil + for it in curly: + var test: PNode + if it.kind == nkRange: + test = newTree(nkCall, g.operators.opAnd.newSymNode, + newTree(nkCall, g.operators.opLe.newSymNode, it[0], elem), # a <= elem + newTree(nkCall, g.operators.opLe.newSymNode, elem, it[1]) + ) + else: + test = newTree(nkCall, g.operators.opEq.newSymNode, elem, it) + test.typ = getSysType(g, it.info, tyBool) + + if ex == nil: ex = test + else: ex = newTree(nkCall, g.operators.opOr.newSymNode, ex, test) + + if ex == nil: + let info = toLineInfo(c, n.info) + template body(target) = + boolVal target, info, false + intoDest d, info, Bool8Id, body + else: + gen c, ex, d + else: + genInBitset c, n, d + +proc genCard(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + if isEmpty(d): d = getTemp(c, n) + + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + buildTyped c.code, info, Call, t: + if c.m.types.g[setType].kind == ArrayTy: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "cardSet") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType): + copyTree c.code, a + c.code.addImmediateVal info, int(getSize(c.config, n[1].typ)) + elif t == UInt64Id: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits64") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + copyTree c.code, a + else: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "countBits32") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, Cast, UInt32Id: + copyTree c.code, a + freeTemp c, a + +proc genEqSet(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + + if c.m.types.g[setType].kind == ArrayTy: + if isEmpty(d): d = getTemp(c, n) + + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + buildTyped c.code, info, Eq, t: + buildTyped c.code, info, Call, t: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "nimCmpMem") + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType): + copyTree c.code, a + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, setType): + copyTree c.code, b + c.code.addImmediateVal info, int(getSize(c.config, n[1].typ)) + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + + else: + template body(target) = + buildTyped target, info, Eq, setType: + copyTree target, a + copyTree target, b + intoDest d, info, Bool8Id, body + + freeTemp c, b + freeTemp c, a + +proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: int): (SymId, LabelId, LabelId) = + let tmp = allocTemp(c.sm, c.m.nativeIntId) + c.code.addSummon info, tmp, c.m.nativeIntId + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmp + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, first + let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) + result = (tmp, lab1, newLabel(c.labelGen)) + + buildTyped c.code, info, Select, Bool8Id: + buildTyped c.code, info, Lt, c.m.nativeIntId: + c.code.addSymUse info, tmp + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, last + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, false) + c.code.gotoLabel info, Goto, result[2] + +proc beginCountLoop(c: var ProcCon; info: PackedLineInfo; first, last: Value): (SymId, LabelId, LabelId) = + let tmp = allocTemp(c.sm, c.m.nativeIntId) + c.code.addSummon info, tmp, c.m.nativeIntId + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmp + copyTree c.code, first + let lab1 = c.code.addNewLabel(c.labelGen, info, LoopLabel) + result = (tmp, lab1, newLabel(c.labelGen)) + + buildTyped c.code, info, Select, Bool8Id: + buildTyped c.code, info, Le, c.m.nativeIntId: + c.code.addSymUse info, tmp + copyTree c.code, last + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, false) + c.code.gotoLabel info, Goto, result[2] + +proc endLoop(c: var ProcCon; info: PackedLineInfo; s: SymId; back, exit: LabelId) = + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, s + buildTyped c.code, info, Add, c.m.nativeIntId: + c.code.addSymUse info, s + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, 1 + c.code.addLabel info, GotoLoop, back + c.code.addLabel info, Label, exit + freeTemp(c.sm, s) + +proc genLeSet(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + + if c.m.types.g[setType].kind == ArrayTy: + let elemType = bitsetBasetype(c.m.types, n[1].typ) + if isEmpty(d): d = getTemp(c, n) + # "for ($1 = 0; $1 < $2; $1++):" + # " $3 = (($4[$1] & ~ $5[$1]) == 0)" + # " if (!$3) break;" + let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ))) + buildTyped c.code, info, Asgn, Bool8Id: + copyTree c.code, d + buildTyped c.code, info, Eq, elemType: + buildTyped c.code, info, BitAnd, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, a + c.code.addSymUse info, idx + buildTyped c.code, info, BitNot, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, b + c.code.addSymUse info, idx + c.code.addIntVal c.m.integers, info, elemType, 0 + + # if !$3: break + buildTyped c.code, info, Select, Bool8Id: + c.code.copyTree d + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, false) + c.code.gotoLabel info, Goto, endLabel + + endLoop(c, info, idx, backLabel, endLabel) + else: + # "(($1 & ~ $2)==0)" + template body(target) = + buildTyped target, info, Eq, setType: + buildTyped target, info, BitAnd, setType: + copyTree target, a + buildTyped target, info, BitNot, setType: + copyTree target, b + target.addIntVal c.m.integers, info, setType, 0 + + intoDest d, info, Bool8Id, body + + freeTemp c, b + freeTemp c, a + +proc genLtSet(c: var ProcCon; n: PNode; d: var Value) = + localError(c.m.graph.config, n.info, "`<` for sets not implemented") + +proc genBinarySet(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + let t = typeToIr(c.m.types, n.typ) + + let setType = typeToIr(c.m.types, n[1].typ) + + if c.m.types.g[setType].kind == ArrayTy: + let elemType = bitsetBasetype(c.m.types, n[1].typ) + if isEmpty(d): d = getTemp(c, n) + # "for ($1 = 0; $1 < $2; $1++):" + # " $3 = (($4[$1] & ~ $5[$1]) == 0)" + # " if (!$3) break;" + let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, int(getSize(c.config, n[1].typ))) + buildTyped c.code, info, Asgn, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, d + c.code.addSymUse info, idx + buildTyped c.code, info, (if m == mPlusSet: BitOr else: BitAnd), elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, a + c.code.addSymUse info, idx + if m == mMinusSet: + buildTyped c.code, info, BitNot, elemType: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, b + c.code.addSymUse info, idx + else: + buildTyped c.code, info, ArrayAt, elemType: + copyTree c.code, b + c.code.addSymUse info, idx + + endLoop(c, info, idx, backLabel, endLabel) + else: + # "(($1 & ~ $2)==0)" + template body(target) = + buildTyped target, info, (if m == mPlusSet: BitOr else: BitAnd), setType: + copyTree target, a + if m == mMinusSet: + buildTyped target, info, BitNot, setType: + copyTree target, b + else: + copyTree target, b + + intoDest d, info, setType, body + + freeTemp c, b + freeTemp c, a + +proc genInclExcl(c: var ProcCon; n: PNode; m: TMagic) = + let info = toLineInfo(c, n.info) + let a = c.genx(n[1]) + let b = c.genx(n[2]) + + let setType = typeToIr(c.m.types, n[1].typ) + + let t = bitsetBasetype(c.m.types, n[1].typ) + let mask = + case t + of UInt8Id: 7 + of UInt16Id: 15 + of UInt32Id: 31 + else: 63 + + buildTyped c.code, info, Asgn, setType: + if c.m.types.g[setType].kind == ArrayTy: + if m == mIncl: + # $1[(NU)($2)>>3] |=(1U<<($2&7U)) + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitOr, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, 7 + else: + # $1[(NU)($2)>>3] &= ~(1U<<($2&7U)) + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitAnd, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, a + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, b + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, 7 + + else: + copyTree c.code, a + if m == mIncl: + # $1 |= ((NU8)1)<<(($2) & 7) + buildTyped c.code, info, BitOr, setType: + copyTree c.code, a + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, mask + else: + # $1 &= ~(((NU8)1) << (($2) & 7)) + buildTyped c.code, info, BitAnd, setType: + copyTree c.code, a + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, b + c.code.addIntVal c.m.integers, info, t, mask + freeTemp c, b + freeTemp c, a + +proc genSetConstrDyn(c: var ProcCon; n: PNode; d: var Value) = + # example: { a..b, c, d, e, f..g } + # we have to emit an expression of the form: + # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c); + # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g); + let info = toLineInfo(c, n.info) + let setType = typeToIr(c.m.types, n.typ) + let size = int(getSize(c.config, n.typ)) + let t = bitsetBasetype(c.m.types, n.typ) + let mask = + case t + of UInt8Id: 7 + of UInt16Id: 15 + of UInt32Id: 31 + else: 63 + + if isEmpty(d): d = getTemp(c, n) + if c.m.types.g[setType].kind != ArrayTy: + buildTyped c.code, info, Asgn, setType: + copyTree c.code, d + c.code.addIntVal c.m.integers, info, t, 0 + + for it in n: + if it.kind == nkRange: + let a = genx(c, it[0]) + let b = genx(c, it[1]) + let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b) + buildTyped c.code, info, Asgn, setType: + copyTree c.code, d + buildTyped c.code, info, BitAnd, setType: + copyTree c.code, d + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + c.code.addSymUse info, idx + c.code.addIntVal c.m.integers, info, t, mask + + endLoop(c, info, idx, backLabel, endLabel) + freeTemp c, b + freeTemp c, a + + else: + let a = genx(c, it) + buildTyped c.code, info, Asgn, setType: + copyTree c.code, d + buildTyped c.code, info, BitAnd, setType: + copyTree c.code, d + buildTyped c.code, info, BitNot, t: + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, a + c.code.addIntVal c.m.integers, info, t, mask + freeTemp c, a + + else: + # init loop: + let (idx, backLabel, endLabel) = beginCountLoop(c, info, 0, size) + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + c.code.addIntVal c.m.integers, info, t, 0 + endLoop(c, info, idx, backLabel, endLabel) + + # incl elements: + for it in n: + if it.kind == nkRange: + let a = genx(c, it[0]) + let b = genx(c, it[1]) + let (idx, backLabel, endLabel) = beginCountLoop(c, info, a, b) + + buildTyped c.code, info, Asgn, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + c.code.addSymUse info, idx + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitOr, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + c.code.addSymUse info, idx + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + c.code.addSymUse info, idx + c.code.addIntVal c.m.integers, info, t, 7 + + endLoop(c, info, idx, backLabel, endLabel) + freeTemp c, b + freeTemp c, a + + else: + let a = genx(c, it) + # $1[(NU)($2)>>3] |=(1U<<($2&7U)) + buildTyped c.code, info, Asgn, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, a + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitOr, t: + buildTyped c.code, info, ArrayAt, t: + copyTree c.code, d + buildTyped c.code, info, BitShr, t: + buildTyped c.code, info, Cast, c.m.nativeUIntId: + copyTree c.code, a + addIntVal c.code, c.m.integers, info, c.m.nativeUIntId, 3 + buildTyped c.code, info, BitShl, t: + c.code.addIntVal c.m.integers, info, t, 1 + buildTyped c.code, info, BitAnd, t: + copyTree c.code, a + c.code.addIntVal c.m.integers, info, t, 7 + freeTemp c, a + +proc genSetConstr(c: var ProcCon; n: PNode; d: var Value) = + if isDeepConstExpr(n): + let info = toLineInfo(c, n.info) + let setType = typeToIr(c.m.types, n.typ) + let size = int(getSize(c.config, n.typ)) + let cs = toBitSet(c.config, n) + + if c.m.types.g[setType].kind != ArrayTy: + template body(target) = + target.addIntVal c.m.integers, info, setType, cast[BiggestInt](bitSetToWord(cs, size)) + intoDest d, info, setType, body + else: + let t = bitsetBasetype(c.m.types, n.typ) + template body(target) = + buildTyped target, info, ArrayConstr, setType: + for i in 0..high(cs): + target.addIntVal c.m.integers, info, t, int64 cs[i] + intoDest d, info, setType, body + else: + genSetConstrDyn c, n, d + +proc genStrConcat(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + # + # s = "Hello " & name & ", how do you feel?" & 'z' + # + # + # { + # string tmp0; + # ... + # tmp0 = rawNewString(6 + 17 + 1 + s2->len); + # // we cannot generate s = rawNewString(...) here, because + # // ``s`` may be used on the right side of the expression + # appendString(tmp0, strlit_1); + # appendString(tmp0, name); + # appendString(tmp0, strlit_2); + # appendChar(tmp0, 'z'); + # asgn(s, tmp0); + # } + var args: seq[Value] = @[] + var precomputedLen = 0 + for i in 1 ..< n.len: + let it = n[i] + if skipTypes(it.typ, abstractVarRange).kind == tyChar: + inc precomputedLen + elif it.kind in {nkStrLit..nkTripleStrLit}: + inc precomputedLen, it.strVal.len + args.add genx(c, it) + + # generate length computation: + var tmpLen = allocTemp(c.sm, c.m.nativeIntId) + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmpLen + c.code.addIntVal c.m.integers, info, c.m.nativeIntId, precomputedLen + for a in mitems(args): + buildTyped c.code, info, Asgn, c.m.nativeIntId: + c.code.addSymUse info, tmpLen + buildTyped c.code, info, CheckedAdd, c.m.nativeIntId: + c.code.addSymUse info, tmpLen + buildTyped c.code, info, FieldAt, c.m.nativeIntId: + copyTree c.code, a + c.code.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 + + var tmpStr = getTemp(c, n) + # ^ because of aliasing, we always go through a temporary + let t = typeToIr(c.m.types, n.typ) + buildTyped c.code, info, Asgn, t: + copyTree c.code, tmpStr + buildTyped c.code, info, Call, t: + let codegenProc = magicsys.getCompilerProc(c.m.graph, "rawNewString") + #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + c.code.addSymUse info, tmpLen + freeTemp c.sm, tmpLen + + for i in 1 ..< n.len: + let it = n[i] + let isChar = skipTypes(it.typ, abstractVarRange).kind == tyChar + buildTyped c.code, info, Call, VoidId: + let codegenProc = magicsys.getCompilerProc(c.m.graph, + (if isChar: "appendChar" else: "appendString")) + #assert codegenProc != nil, $n & " " & (c.m.graph.config $ n.info) + let theProc = c.genx newSymNode(codegenProc, n.info) + copyTree c.code, theProc + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, t): + copyTree c.code, tmpStr + copyTree c.code, args[i-1] + freeTemp c, args[i-1] + + if isEmpty(d): + d = tmpStr + else: + # XXX Test that this does not cause memory leaks! + buildTyped c.code, info, Asgn, t: + copyTree c.code, d + copyTree c.code, tmpStr + +proc genDefault(c: var ProcCon; n: PNode; d: var Value) = + let m = expandDefault(n.typ, n.info) + gen c, m, d + +proc genWasMoved(c: var ProcCon; n: PNode) = + let n1 = n[1].skipAddr + # XXX We need a way to replicate this logic or better yet a better + # solution for injectdestructors.nim: + #if c.withinBlockLeaveActions > 0 and notYetAlive(n1): + var d = c.genx(n1) + assert not isEmpty(d) + let m = expandDefault(n1.typ, n1.info) + gen c, m, d + +proc genMove(c: var ProcCon; n: PNode; d: var Value) = + let info = toLineInfo(c, n.info) + let n1 = n[1].skipAddr + var a = c.genx(n1) + if n.len == 4: + # generated by liftdestructors: + let src = c.genx(n[2]) + # if ($1.p == $2.p) goto lab1 + let lab1 = newLabel(c.labelGen) + + let payloadType = seqPayloadPtrType(c.m.types, n1.typ) + buildTyped c.code, info, Select, Bool8Id: + buildTyped c.code, info, Eq, payloadType: + buildTyped c.code, info, FieldAt, payloadType: + copyTree c.code, a + c.code.addImmediateVal info, 1 # (len, p)-pair + buildTyped c.code, info, FieldAt, payloadType: + copyTree c.code, src + c.code.addImmediateVal info, 1 # (len, p)-pair + + build c.code, info, SelectPair: + build c.code, info, SelectValue: + c.code.boolVal(info, true) + c.code.gotoLabel info, Goto, lab1 + + gen(c, n[3]) + c.patch n, lab1 + + buildTyped c.code, info, Asgn, typeToIr(c.m.types, n1.typ): + copyTree c.code, a + copyTree c.code, src + + else: + if isEmpty(d): d = getTemp(c, n) + buildTyped c.code, info, Asgn, typeToIr(c.m.types, n1.typ): + copyTree c.code, d + copyTree c.code, a + var op = getAttachedOp(c.m.graph, n.typ, attachedWasMoved) + if op == nil or skipTypes(n1.typ, abstractVar+{tyStatic}).kind in {tyOpenArray, tyVarargs}: + let m = expandDefault(n1.typ, n1.info) + gen c, m, a + else: + var opB = c.genx(newSymNode(op)) + buildTyped c.code, info, Call, typeToIr(c.m.types, n.typ): + copyTree c.code, opB + buildTyped c.code, info, AddrOf, ptrTypeOf(c.m.types.g, typeToIr(c.m.types, n1.typ)): + copyTree c.code, a + +proc genDestroy(c: var ProcCon; n: PNode) = + let t = n[1].typ.skipTypes(abstractInst) + case t.kind + of tyString: + var unused = default(Value) + genUnaryCp(c, n, unused, "nimDestroyStrV1") + of tySequence: + #[ + var a = initLocExpr(c, arg) + linefmt(c, cpsStmts, "if ($1.p && ($1.p->cap & NIM_STRLIT_FLAG) == 0) {$n" & + " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" & + "}$n", + [rdLoc(a), getTypeDesc(c.module, t.lastSon)]) + ]# + globalError(c.config, n.info, "not implemented: =destroy for seqs") + else: discard "nothing to do" + +proc genMagic(c: var ProcCon; n: PNode; d: var Value; m: TMagic) = + case m + of mAnd: c.genAndOr(n, opcFJmp, d) + of mOr: c.genAndOr(n, opcTJmp, d) + of mPred, mSubI: c.genBinaryOp(n, d, CheckedSub) + of mSucc, mAddI: c.genBinaryOp(n, d, CheckedAdd) + of mInc: + unused(c, n, d) + c.genIncDec(n, CheckedAdd) + of mDec: + unused(c, n, d) + c.genIncDec(n, CheckedSub) + of mOrd, mChr, mArrToSeq, mUnown: + c.gen(n[1], d) + of generatedMagics: + genCall(c, n, d) + of mNew, mNewFinalize: + unused(c, n, d) + c.genNew(n, needsInit = true) + of mNewSeq: + unused(c, n, d) + c.genNewSeq(n) + of mNewSeqOfCap: c.genNewSeqOfCap(n, d) + of mNewString, mNewStringOfCap, mExit: c.genCall(n, d) + of mLengthOpenArray, mLengthArray, mLengthSeq, mLengthStr: + genArrayLen(c, n, d) + of mMulI: genBinaryOp(c, n, d, CheckedMul) + of mDivI: genBinaryOp(c, n, d, CheckedDiv) + of mModI: genBinaryOp(c, n, d, CheckedMod) + of mAddF64: genBinaryOp(c, n, d, Add) + of mSubF64: genBinaryOp(c, n, d, Sub) + of mMulF64: genBinaryOp(c, n, d, Mul) + of mDivF64: genBinaryOp(c, n, d, Div) + of mShrI: genBinaryOp(c, n, d, BitShr) + of mShlI: genBinaryOp(c, n, d, BitShl) + of mAshrI: genBinaryOp(c, n, d, BitShr) + of mBitandI: genBinaryOp(c, n, d, BitAnd) + of mBitorI: genBinaryOp(c, n, d, BitOr) + of mBitxorI: genBinaryOp(c, n, d, BitXor) + of mAddU: genBinaryOp(c, n, d, Add) + of mSubU: genBinaryOp(c, n, d, Sub) + of mMulU: genBinaryOp(c, n, d, Mul) + of mDivU: genBinaryOp(c, n, d, Div) + of mModU: genBinaryOp(c, n, d, Mod) + of mEqI, mEqB, mEqEnum, mEqCh: + genCmpOp(c, n, d, Eq) + of mLeI, mLeEnum, mLeCh, mLeB: + genCmpOp(c, n, d, Le) + of mLtI, mLtEnum, mLtCh, mLtB: + genCmpOp(c, n, d, Lt) + of mEqF64: genCmpOp(c, n, d, Eq) + of mLeF64: genCmpOp(c, n, d, Le) + of mLtF64: genCmpOp(c, n, d, Lt) + of mLePtr, mLeU: genCmpOp(c, n, d, Le) + of mLtPtr, mLtU: genCmpOp(c, n, d, Lt) + of mEqProc, mEqRef: + genCmpOp(c, n, d, Eq) + of mXor: genBinaryOp(c, n, d, BitXor) + of mNot: genUnaryOp(c, n, d, BoolNot) + of mUnaryMinusI, mUnaryMinusI64: + genUnaryMinus(c, n, d) + #genNarrow(c, n, d) + of mUnaryMinusF64: genUnaryMinus(c, n, d) + of mUnaryPlusI, mUnaryPlusF64: gen(c, n[1], d) + of mBitnotI: + genUnaryOp(c, n, d, BitNot) + when false: + # XXX genNarrowU modified, do not narrow signed types + let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) + let size = getSize(c.config, t) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): + c.gABC(n, opcNarrowU, d, TRegister(size*8)) + of mStrToStr, mEnsureMove: c.gen n[1], d + of mIntToStr: genUnaryCp(c, n, d, "nimIntToStr") + of mInt64ToStr: genUnaryCp(c, n, d, "nimInt64ToStr") + of mBoolToStr: genUnaryCp(c, n, d, "nimBoolToStr") + of mCharToStr: genUnaryCp(c, n, d, "nimCharToStr") + of mFloatToStr: + if n[1].typ.skipTypes(abstractInst).kind == tyFloat32: + genUnaryCp(c, n, d, "nimFloat32ToStr") + else: + genUnaryCp(c, n, d, "nimFloatToStr") + of mCStrToStr: genUnaryCp(c, n, d, "cstrToNimstr") + of mEnumToStr: genEnumToStr(c, n, d) + + of mEqStr: genBinaryCp(c, n, d, "eqStrings") + of mEqCString: genCall(c, n, d) + of mLeStr: genBinaryCp(c, n, d, "leStrings") + of mLtStr: genBinaryCp(c, n, d, "ltStrings") + + of mSetLengthStr: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genBinaryCp(c, nb, d, "setLengthStrV2") + + of mSetLengthSeq: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genCall(c, nb, d) + + of mSwap: + unused(c, n, d) + c.gen(lowerSwap(c.m.graph, n, c.m.idgen, + if c.prc == nil: c.m.module else: c.prc), d) + of mParseBiggestFloat: + genCall c, n, d + of mHigh: + c.genHigh n, d + + of mEcho: + unused(c, n, d) + genUnaryCp c, n, d, "echoBinSafe" + + of mAppendStrCh: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genBinaryCp(c, nb, d, "nimAddCharV1") + of mMinI, mMaxI, mAbsI, mDotDot: + c.genCall(n, d) + of mSizeOf: + localError(c.config, n.info, sizeOfLikeMsg("sizeof")) + of mAlignOf: + localError(c.config, n.info, sizeOfLikeMsg("alignof")) + of mOffsetOf: + localError(c.config, n.info, sizeOfLikeMsg("offsetof")) + of mRunnableExamples: + discard "just ignore any call to runnableExamples" + of mOf: genOf(c, n, d) + of mAppendStrStr: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genBinaryCp(c, nb, d, "nimAddStrV1") + of mAppendSeqElem: + unused(c, n, d) + let nb = copyTree(n) + nb[1] = makeAddr(nb[1], c.m.idgen) + genCall(c, nb, d) + of mIsNil: genIsNil(c, n, d) + of mInSet: genInSet(c, n, d) + of mCard: genCard(c, n, d) + of mEqSet: genEqSet(c, n, d) + of mLeSet: genLeSet(c, n, d) + of mLtSet: genLtSet(c, n, d) + of mMulSet: genBinarySet(c, n, d, m) + of mPlusSet: genBinarySet(c, n, d, m) + of mMinusSet: genBinarySet(c, n, d, m) + of mIncl, mExcl: + unused(c, n, d) + genInclExcl(c, n, m) + of mConStrStr: genStrConcat(c, n, d) + of mDefault, mZeroDefault: + genDefault c, n, d + of mMove: genMove(c, n, d) + of mWasMoved, mReset: + unused(c, n, d) + genWasMoved(c, n) + of mDestroy: genDestroy(c, n) + #of mAccessEnv: unaryExpr(d, n, d, "$1.ClE_0") + #of mAccessTypeField: genAccessTypeField(c, n, d) + #of mSlice: genSlice(c, n, d) + of mTrace: discard "no code to generate" + else: + # mGCref, mGCunref: unused by ORC + globalError(c.config, n.info, "cannot generate code for: " & $m) + +#[ + + of mRepr: genUnaryABC(c, n, d, opcRepr) + + of mNodeId: + c.genUnaryABC(n, d, opcNodeId) + + of mExpandToAst: + if n.len != 2: + globalError(c.config, n.info, "expandToAst requires 1 argument") + let arg = n[1] + if arg.kind in nkCallKinds: + #if arg[0].kind != nkSym or arg[0].sym.kind notin {skTemplate, skMacro}: + # "ExpandToAst: expanded symbol is no macro or template" + if isEmpty(d): d = c.getTemp(n) + c.genCall(arg, d) + # do not call clearDest(n, d) here as getAst has a meta-type as such + # produces a value + else: + globalError(c.config, n.info, "expandToAst requires a call expression") + of mParseExprToAst: + genBinaryABC(c, n, d, opcParseExprToAst) + of mParseStmtToAst: + genBinaryABC(c, n, d, opcParseStmtToAst) + of mTypeTrait: + let tmp = c.genx(n[1]) + if isEmpty(d): d = c.getTemp(n) + c.gABx(n, opcSetType, tmp, c.genType(n[1].typ)) + c.gABC(n, opcTypeTrait, d, tmp) + c.freeTemp(tmp) + of mSlurp: genUnaryABC(c, n, d, opcSlurp) + of mNLen: genUnaryABI(c, n, d, opcLenSeq, nimNodeFlag) + of mGetImpl: genUnaryABC(c, n, d, opcGetImpl) + of mGetImplTransf: genUnaryABC(c, n, d, opcGetImplTransf) + of mSymOwner: genUnaryABC(c, n, d, opcSymOwner) + of mSymIsInstantiationOf: genBinaryABC(c, n, d, opcSymIsInstantiationOf) + of mNChild: genBinaryABC(c, n, d, opcNChild) + of mNAdd: genBinaryABC(c, n, d, opcNAdd) + of mNAddMultiple: genBinaryABC(c, n, d, opcNAddMultiple) + of mNKind: genUnaryABC(c, n, d, opcNKind) + of mNSymKind: genUnaryABC(c, n, d, opcNSymKind) + + of mNccValue: genUnaryABC(c, n, d, opcNccValue) + of mNccInc: genBinaryABC(c, n, d, opcNccInc) + of mNcsAdd: genBinaryABC(c, n, d, opcNcsAdd) + of mNcsIncl: genBinaryABC(c, n, d, opcNcsIncl) + of mNcsLen: genUnaryABC(c, n, d, opcNcsLen) + of mNcsAt: genBinaryABC(c, n, d, opcNcsAt) + of mNctLen: genUnaryABC(c, n, d, opcNctLen) + of mNctGet: genBinaryABC(c, n, d, opcNctGet) + of mNctHasNext: genBinaryABC(c, n, d, opcNctHasNext) + of mNctNext: genBinaryABC(c, n, d, opcNctNext) + + of mNIntVal: genUnaryABC(c, n, d, opcNIntVal) + of mNFloatVal: genUnaryABC(c, n, d, opcNFloatVal) + of mNSymbol: genUnaryABC(c, n, d, opcNSymbol) + of mNIdent: genUnaryABC(c, n, d, opcNIdent) + of mNGetType: + let tmp = c.genx(n[1]) + if isEmpty(d): d = c.getTemp(n) + let rc = case n[0].sym.name.s: + of "getType": 0 + of "typeKind": 1 + of "getTypeInst": 2 + else: 3 # "getTypeImpl" + c.gABC(n, opcNGetType, d, tmp, rc) + c.freeTemp(tmp) + #genUnaryABC(c, n, d, opcNGetType) + of mNSizeOf: + let imm = case n[0].sym.name.s: + of "getSize": 0 + of "getAlign": 1 + else: 2 # "getOffset" + c.genUnaryABI(n, d, opcNGetSize, imm) + of mNStrVal: genUnaryABC(c, n, d, opcNStrVal) + of mNSigHash: genUnaryABC(c, n , d, opcNSigHash) + of mNSetIntVal: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetIntVal) + of mNSetFloatVal: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetFloatVal) + of mNSetSymbol: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetSymbol) + of mNSetIdent: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetIdent) + of mNSetStrVal: + unused(c, n, d) + genBinaryStmt(c, n, opcNSetStrVal) + of mNNewNimNode: genBinaryABC(c, n, d, opcNNewNimNode) + of mNCopyNimNode: genUnaryABC(c, n, d, opcNCopyNimNode) + of mNCopyNimTree: genUnaryABC(c, n, d, opcNCopyNimTree) + of mNBindSym: genBindSym(c, n, d) + of mStrToIdent: genUnaryABC(c, n, d, opcStrToIdent) + of mEqIdent: genBinaryABC(c, n, d, opcEqIdent) + of mEqNimrodNode: genBinaryABC(c, n, d, opcEqNimNode) + of mSameNodeType: genBinaryABC(c, n, d, opcSameNodeType) + of mNLineInfo: + case n[0].sym.name.s + of "getFile": genUnaryABI(c, n, d, opcNGetLineInfo, 0) + of "getLine": genUnaryABI(c, n, d, opcNGetLineInfo, 1) + of "getColumn": genUnaryABI(c, n, d, opcNGetLineInfo, 2) + of "copyLineInfo": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNCopyLineInfo) + of "setLine": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNSetLineInfoLine) + of "setColumn": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNSetLineInfoColumn) + of "setFile": + internalAssert c.config, n.len == 3 + unused(c, n, d) + genBinaryStmt(c, n, opcNSetLineInfoFile) + else: internalAssert c.config, false + of mNHint: + unused(c, n, d) + genBinaryStmt(c, n, opcNHint) + of mNWarning: + unused(c, n, d) + genBinaryStmt(c, n, opcNWarning) + of mNError: + if n.len <= 1: + # query error condition: + c.gABC(n, opcQueryErrorFlag, d) + else: + # setter + unused(c, n, d) + genBinaryStmt(c, n, opcNError) + of mNCallSite: + if isEmpty(d): d = c.getTemp(n) + c.gABC(n, opcCallSite, d) + of mNGenSym: genBinaryABC(c, n, d, opcGenSym) + +]# + +proc canElimAddr(n: PNode; idgen: IdGenerator): PNode = + result = nil + case n[0].kind + of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: + var m = n[0][0] + if m.kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) + result = copyNode(n[0]) + result.add m[0] + if n.typ.skipTypes(abstractVar).kind != tyOpenArray: + result.typ = n.typ + elif n.typ.skipTypes(abstractInst).kind in {tyVar}: + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) + of nkHiddenStdConv, nkHiddenSubConv, nkConv: + var m = n[0][1] + if m.kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x) + result = copyNode(n[0]) + result.add n[0][0] + result.add m[0] + if n.typ.skipTypes(abstractVar).kind != tyOpenArray: + result.typ = n.typ + elif n.typ.skipTypes(abstractInst).kind in {tyVar}: + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen) + else: + if n[0].kind in {nkDerefExpr, nkHiddenDeref}: + # addr ( deref ( x )) --> x + result = n[0][0] + +template valueIntoDest(c: var ProcCon; info: PackedLineInfo; d: var Value; typ: PType; body: untyped) = + if isEmpty(d): + body(Tree d) + else: + buildTyped c.code, info, Asgn, typeToIr(c.m.types, typ): + copyTree c.code, d + body(c.code) + +proc genAddr(c: var ProcCon; n: PNode; d: var Value, flags: GenFlags) = + if (let m = canElimAddr(n, c.m.idgen); m != nil): + gen(c, m, d, flags) + return + + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[0], flags) + template body(target) = + buildTyped target, info, AddrOf, typeToIr(c.m.types, n.typ): + copyTree target, tmp + + valueIntoDest c, info, d, n.typ, body + freeTemp c, tmp + +proc genDeref(c: var ProcCon; n: PNode; d: var Value; flags: GenFlags) = + let info = toLineInfo(c, n.info) + let tmp = c.genx(n[0], flags) + template body(target) = + buildTyped target, info, Load, typeToIr(c.m.types, n.typ): + copyTree target, tmp + + valueIntoDest c, info, d, n.typ, body + freeTemp c, tmp + +proc addAddrOfFirstElem(c: var ProcCon; target: var Tree; info: PackedLineInfo; tmp: Value; typ: PType) = + let arrType = typ.skipTypes(abstractVar) + let elemType = arrayPtrTypeOf(c.m.types.g, typeToIr(c.m.types, arrType.lastSon)) + case arrType.kind + of tyString: + let t = typeToIr(c.m.types, typ.lastSon) + target.addImmediateVal info, 0 + buildTyped target, info, AddrOf, elemType: + buildTyped target, info, ArrayAt, t: + buildTyped target, info, FieldAt, strPayloadPtrType(c.m.types): + copyTree target, tmp + target.addImmediateVal info, 1 # (len, p)-pair + target.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + # len: + target.addImmediateVal info, 1 + buildTyped target, info, FieldAt, c.m.nativeIntId: + copyTree target, tmp + target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 + + of tySequence: + let t = typeToIr(c.m.types, typ.lastSon) + target.addImmediateVal info, 0 + buildTyped target, info, AddrOf, elemType: + buildTyped target, info, ArrayAt, t: + buildTyped target, info, FieldAt, seqPayloadPtrType(c.m.types, typ): + copyTree target, tmp + target.addImmediateVal info, 1 # (len, p)-pair + target.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + # len: + target.addImmediateVal info, 1 + buildTyped target, info, FieldAt, c.m.nativeIntId: + copyTree target, tmp + target.addImmediateVal info, 0 # (len, p)-pair so len is at index 0 + + of tyArray: + let t = typeToIr(c.m.types, typ.lastSon) + target.addImmediateVal info, 0 + buildTyped target, info, AddrOf, elemType: + buildTyped target, info, ArrayAt, t: + copyTree target, tmp + target.addIntVal c.m.integers, info, c.m.nativeIntId, 0 + target.addImmediateVal info, 1 + target.addIntVal(c.m.integers, info, c.m.nativeIntId, toInt lengthOrd(c.config, typ)) + else: + raiseAssert "addAddrOfFirstElem: " & typeToString(typ) + +proc genToOpenArrayConv(c: var ProcCon; arg: PNode; d: var Value; flags: GenFlags; destType: PType) = + let info = toLineInfo(c, arg.info) + let tmp = c.genx(arg, flags) + let arrType = destType.skipTypes(abstractVar) + template body(target) = + buildTyped target, info, ObjConstr, typeToIr(c.m.types, arrType): + c.addAddrOfFirstElem target, info, tmp, arg.typ + + valueIntoDest c, info, d, arrType, body + freeTemp c, tmp + +proc genConv(c: var ProcCon; n, arg: PNode; d: var Value; flags: GenFlags; opc: Opcode) = + let targetType = n.typ.skipTypes({tyDistinct}) + let argType = arg.typ.skipTypes({tyDistinct}) + + if sameBackendType(targetType, argType) or ( + argType.kind == tyProc and targetType.kind == argType.kind): + # don't do anything for lambda lifting conversions: + gen c, arg, d + return + + if opc != Cast and targetType.skipTypes({tyVar, tyLent}).kind in {tyOpenArray, tyVarargs} and + argType.skipTypes({tyVar, tyLent}).kind notin {tyOpenArray, tyVarargs}: + genToOpenArrayConv c, arg, d, flags, n.typ + return + + let info = toLineInfo(c, n.info) + let tmp = c.genx(arg, flags) + template body(target) = + buildTyped target, info, opc, typeToIr(c.m.types, n.typ): + if opc == CheckedObjConv: + target.addLabel info, CheckedGoto, c.exitLabel + copyTree target, tmp + + valueIntoDest c, info, d, n.typ, body + freeTemp c, tmp + +proc genObjOrTupleConstr(c: var ProcCon; n: PNode, d: var Value) = + # XXX x = (x.old, 22) produces wrong code ... stupid self assignments + let info = toLineInfo(c, n.info) + template body(target) = + buildTyped target, info, ObjConstr, typeToIr(c.m.types, n.typ): + for i in ord(n.kind == nkObjConstr)..; x begins to live + SummonParam, + SummonConst, + Kill, # `Kill x`: scope end for `x` + + AddrOf, + ArrayAt, # addr(a[i]) + FieldAt, # addr(obj.field) + + Load, # a[] + Store, # a[] = b + Asgn, # a = b + SetExc, + TestExc, + + CheckedRange, + CheckedIndex, + + Call, + IndirectCall, + CheckedCall, # call that can raise + CheckedIndirectCall, # call that can raise + CheckedAdd, # with overflow checking etc. + CheckedSub, + CheckedMul, + CheckedDiv, + CheckedMod, + Add, + Sub, + Mul, + Div, + Mod, + BitShl, + BitShr, + BitAnd, + BitOr, + BitXor, + BitNot, + BoolNot, + Eq, + Le, + Lt, + Cast, + NumberConv, + CheckedObjConv, + ObjConv, + TestOf, + Emit, + ProcDecl, + PragmaPair + +type + PragmaKey* = enum + FastCall, StdCall, CDeclCall, SafeCall, SysCall, InlineCall, NoinlineCall, ThisCall, NoCall, + ExternName, + HeaderImport, + DllImport, + DllExport, + ObjExport + +const + LastAtomicValue = GotoLoop + + OpcodeBits = 8'u32 + OpcodeMask = (1'u32 shl OpcodeBits) - 1'u32 + + ValueProducingAtoms = {ImmediateVal, IntVal, StrVal, SymUse, NilVal} + + ValueProducing* = { + ImmediateVal, + IntVal, + StrVal, + SymUse, + NilVal, + ModuleSymUse, + ArrayConstr, + ObjConstr, + CheckedAdd, + CheckedSub, + CheckedMul, + CheckedDiv, + CheckedMod, + Add, + Sub, + Mul, + Div, + Mod, + BitShl, + BitShr, + BitAnd, + BitOr, + BitXor, + BitNot, + BoolNot, + Eq, + Le, + Lt, + Cast, + NumberConv, + CheckedObjConv, + ObjConv, + AddrOf, + Load, + ArrayAt, + FieldAt, + TestOf + } + +type + Instr* = object # 8 bytes + x: uint32 + info: PackedLineInfo + +template kind*(n: Instr): Opcode = Opcode(n.x and OpcodeMask) +template operand(n: Instr): uint32 = (n.x shr OpcodeBits) + +template toX(k: Opcode; operand: uint32): uint32 = + uint32(k) or (operand shl OpcodeBits) + +template toX(k: Opcode; operand: LitId): uint32 = + uint32(k) or (operand.uint32 shl OpcodeBits) + +type + Tree* = object + nodes: seq[Instr] + + Values* = object + numbers: BiTable[int64] + strings: BiTable[string] + +type + PatchPos* = distinct int + NodePos* = distinct int + +const + InvalidPatchPos* = PatchPos(-1) + +proc isValid(p: PatchPos): bool {.inline.} = p.int != -1 + +proc prepare*(tree: var Tree; info: PackedLineInfo; kind: Opcode): PatchPos = + result = PatchPos tree.nodes.len + tree.nodes.add Instr(x: toX(kind, 1'u32), info: info) + +proc isAtom(tree: Tree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue +proc isAtom(tree: Tree; pos: NodePos): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue + +proc patch*(tree: var Tree; pos: PatchPos) = + let pos = pos.int + let k = tree.nodes[pos].kind + assert k > LastAtomicValue + let distance = int32(tree.nodes.len - pos) + assert distance > 0 + tree.nodes[pos].x = toX(k, cast[uint32](distance)) + +template build*(tree: var Tree; info: PackedLineInfo; kind: Opcode; body: untyped) = + let pos = prepare(tree, info, kind) + body + patch(tree, pos) + +template buildTyped*(tree: var Tree; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) = + let pos = prepare(tree, info, kind) + tree.addTyped info, typ + body + patch(tree, pos) + +proc len*(tree: Tree): int {.inline.} = tree.nodes.len + +template rawSpan(n: Instr): int = int(operand(n)) + +proc nextChild(tree: Tree; pos: var int) {.inline.} = + if tree.nodes[pos].kind > LastAtomicValue: + assert tree.nodes[pos].operand > 0'u32 + inc pos, tree.nodes[pos].rawSpan + else: + inc pos + +iterator sons*(tree: Tree; n: NodePos): NodePos = + var pos = n.int + assert tree.nodes[pos].kind > LastAtomicValue + let last = pos + tree.nodes[pos].rawSpan + inc pos + while pos < last: + yield NodePos pos + nextChild tree, pos + +template `[]`*(t: Tree; n: NodePos): Instr = t.nodes[n.int] + +proc span(tree: Tree; pos: int): int {.inline.} = + if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand) + +proc copyTree*(dest: var Tree; src: Tree) = + let pos = 0 + let L = span(src, pos) + let d = dest.nodes.len + dest.nodes.setLen(d + L) + assert L > 0 + for i in 0..= 0 and x < ((1 shl 32) - OpcodeBits.int) + t.nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info) + +proc addPragmaId*(t: var Tree; info: PackedLineInfo; x: PragmaKey) = + t.nodes.add Instr(x: toX(PragmaId, uint32(x)), info: info) + +proc addIntVal*(t: var Tree; integers: var BiTable[int64]; info: PackedLineInfo; typ: TypeId; x: int64) = + buildTyped t, info, NumberConv, typ: + t.nodes.add Instr(x: toX(IntVal, uint32(integers.getOrIncl(x))), info: info) + +proc addStrVal*(t: var Tree; strings: var BiTable[string]; info: PackedLineInfo; s: string) = + t.nodes.add Instr(x: toX(StrVal, uint32(strings.getOrIncl(s))), info: info) + +proc addNilVal*(t: var Tree; info: PackedLineInfo; typ: TypeId) = + buildTyped t, info, NumberConv, typ: + t.nodes.add Instr(x: toX(NilVal, uint32(0)), info: info) + +proc escapeToNimLit(s: string; result: var string) = + result.add '"' + for c in items s: + if c < ' ' or int(c) >= 128: + result.add '\\' + result.addInt int(c) + elif c == '\\': + result.add r"\\" + elif c == '\n': + result.add r"\n" + elif c == '\r': + result.add r"\r" + elif c == '\t': + result.add r"\t" + else: + result.add c + result.add '"' + +proc toString*(t: Tree; pos: NodePos; strings: BiTable[string]; integers: BiTable[int64]; + r: var string; nesting = 0) = + if r.len > 0 and r[r.len-1] notin {' ', '\n', '(', '[', '{'}: + r.add ' ' + + case t[pos].kind + of Nop: r.add "Nop" + of ImmediateVal: + r.add $t[pos].operand + of IntVal: + r.add "IntVal " + r.add $integers[LitId t[pos].operand] + of StrVal: + escapeToNimLit(strings[LitId t[pos].operand], r) + of SymDef: + r.add "SymDef " + r.add $t[pos].operand + of SymUse: + r.add "SymUse " + r.add $t[pos].operand + of PragmaId: + r.add $cast[PragmaKey](t[pos].operand) + of Typed: + r.add "Typed " + r.add $t[pos].operand + of NilVal: + r.add "NilVal" + of Label: + r.add "L" + r.add $t[pos].operand + of Goto, CheckedGoto, LoopLabel, GotoLoop: + r.add $t[pos].kind + r.add ' ' + r.add $t[pos].operand + else: + r.add $t[pos].kind + r.add "{\n" + for i in 0..<(nesting+1)*2: r.add ' ' + for p in sons(t, pos): + toString t, p, strings, integers, r, nesting+1 + r.add "\n" + for i in 0.. 0 + +proc isEmpty*(v: Value): bool {.inline.} = Tree(v).len == 0 + +proc extractTemp*(v: Value): SymId = + if hasValue(v) and Tree(v)[NodePos 0].kind == SymUse: + result = SymId(Tree(v)[NodePos 0].operand) + else: + result = SymId(-1) + +proc copyTree*(dest: var Tree; src: Value) = copyTree dest, Tree(src) + +proc addImmediateVal*(t: var Value; info: PackedLineInfo; x: int) = + assert x >= 0 and x < ((1 shl 32) - OpcodeBits.int) + Tree(t).nodes.add Instr(x: toX(ImmediateVal, uint32(x)), info: info) + +template build*(tree: var Value; info: PackedLineInfo; kind: Opcode; body: untyped) = + let pos = prepare(Tree(tree), info, kind) + body + patch(tree, pos) + +proc addTyped*(t: var Value; info: PackedLineInfo; typ: TypeId) {.inline.} = + addTyped(Tree(t), info, typ) + +template buildTyped*(tree: var Value; info: PackedLineInfo; kind: Opcode; typ: TypeId; body: untyped) = + let pos = prepare(tree, info, kind) + tree.addTyped info, typ + body + patch(tree, pos) + +proc addStrVal*(t: var Value; strings: var BiTable[string]; info: PackedLineInfo; s: string) = + addStrVal(Tree(t), strings, info, s) + +proc addNilVal*(t: var Value; info: PackedLineInfo; typ: TypeId) = + addNilVal Tree(t), info, typ diff --git a/compiler/nir/nirlineinfos.nim b/compiler/nir/nirlineinfos.nim new file mode 100644 index 0000000000000..4e86f619ec3ad --- /dev/null +++ b/compiler/nir/nirlineinfos.nim @@ -0,0 +1,78 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# For the line information we use 32 bits. They are used as follows: +# Bit 0 (AsideBit): If we have inline line information or not. If not, the +# remaining 31 bits are used as an index into a seq[(LitId, int, int)]. +# +# We use 10 bits for the "file ID", this means a program can consist of as much +# as 1024 different files. (If it uses more files than that, the overflow bit +# would be set.) +# This means we have 21 bits left to encode the (line, col) pair. We use 7 bits for the column +# so 128 is the limit and 14 bits for the line number. +# The packed representation supports files with up to 16384 lines. +# Keep in mind that whenever any limit is reached the AsideBit is set and the real line +# information is kept in a side channel. + +import std / assertions + +const + AsideBit = 1 + FileBits = 10 + LineBits = 14 + ColBits = 7 + FileMax = (1 shl FileBits) - 1 + LineMax = (1 shl LineBits) - 1 + ColMax = (1 shl ColBits) - 1 + +static: + assert AsideBit + FileBits + LineBits + ColBits == 32 + +import .. / ic / bitabs # for LitId + +type + PackedLineInfo* = distinct uint32 + + LineInfoManager* = object + aside*: seq[(LitId, int32, int32)] + +proc pack*(m: var LineInfoManager; file: LitId; line, col: int32): PackedLineInfo = + if file.uint32 <= FileMax.uint32 and line <= LineMax and col <= ColMax: + let col = if col < 0'i32: 0'u32 else: col.uint32 + let line = if line < 0'i32: 0'u32 else: line.uint32 + # use inline representation: + result = PackedLineInfo((file.uint32 shl 1'u32) or (line shl uint32(AsideBit + FileBits)) or + (col shl uint32(AsideBit + FileBits + LineBits))) + else: + result = PackedLineInfo((m.aside.len shl 1) or AsideBit) + m.aside.add (file, line, col) + +proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) = + let i = i.uint32 + if (i and 1'u32) == 0'u32: + # inline representation: + result = (LitId((i shr 1'u32) and FileMax.uint32), + int32((i shr uint32(AsideBit + FileBits)) and LineMax.uint32), + int32((i shr uint32(AsideBit + FileBits + LineBits)) and ColMax.uint32)) + else: + result = m.aside[int(i shr 1'u32)] + +proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId = + result = unpack(m, i)[0] + +when isMainModule: + var m = LineInfoManager(aside: @[]) + for i in 0'i32..<16388'i32: + for col in 0'i32..<100'i32: + let packed = pack(m, LitId(1023), i, col) + let u = unpack(m, packed) + assert u[0] == LitId(1023) + assert u[1] == i + assert u[2] == col + echo m.aside.len diff --git a/compiler/nir/nirslots.nim b/compiler/nir/nirslots.nim new file mode 100644 index 0000000000000..256c25a190a2a --- /dev/null +++ b/compiler/nir/nirslots.nim @@ -0,0 +1,105 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Management of slots. Similar to "register allocation" +## in lower level languages. + +import std / [assertions, tables] +import nirtypes, nirinsts + +type + SlotManagerFlag* = enum + ReuseTemps, + ReuseVars + SlotKind* = enum + Temp, Perm + SlotManager* = object # "register allocator" + live: Table[SymId, (SlotKind, TypeId)] + dead: Table[TypeId, seq[SymId]] + flags: set[SlotManagerFlag] + inScope: seq[SymId] + locGen: ref int + +proc initSlotManager*(flags: set[SlotManagerFlag]; generator: ref int): SlotManager {.inline.} = + SlotManager(flags: flags, locGen: generator) + +proc allocRaw(m: var SlotManager; t: TypeId; f: SlotManagerFlag; k: SlotKind): SymId {.inline.} = + if f in m.flags and m.dead.hasKey(t) and m.dead[t].len > 0: + result = m.dead[t].pop() + else: + result = SymId(m.locGen[]) + inc m.locGen[] + m.inScope.add result + m.live[result] = (k, t) + +proc allocTemp*(m: var SlotManager; t: TypeId): SymId {.inline.} = + result = allocRaw(m, t, ReuseTemps, Temp) + +proc allocVar*(m: var SlotManager; t: TypeId): SymId {.inline.} = + result = allocRaw(m, t, ReuseVars, Perm) + +proc freeLoc*(m: var SlotManager; s: SymId) = + let t = m.live.getOrDefault(s) + assert t[1].int != 0 + m.live.del s + m.dead.mgetOrPut(t[1], @[]).add s + +proc freeTemp*(m: var SlotManager; s: SymId) = + let t = m.live.getOrDefault(s) + if t[1].int != 0 and t[0] == Temp: + m.live.del s + m.dead.mgetOrPut(t[1], @[]).add s + +iterator stillAlive*(m: SlotManager): (SymId, TypeId) = + for k, v in pairs(m.live): + yield (k, v[1]) + +proc getType*(m: SlotManager; s: SymId): TypeId {.inline.} = m.live[s][1] + +proc openScope*(m: var SlotManager) = + m.inScope.add SymId(-1) # add marker + +proc closeScope*(m: var SlotManager) = + var i = m.inScope.len - 1 + while i >= 0: + if m.inScope[i] == SymId(-1): + m.inScope.setLen i + break + dec i + +when isMainModule: + var m = initSlotManager({ReuseTemps}, new(int)) + + var g = initTypeGraph() + + let a = g.openType ArrayTy + g.addBuiltinType Int8Id + g.addArrayLen 5'u64 + let finalArrayType = sealType(g, a) + + let obj = g.openType ObjectDecl + g.addName "MyType" + + g.addField "p", finalArrayType + let objB = sealType(g, obj) + + let x = m.allocTemp(objB) + assert x.int == 0 + + let y = m.allocTemp(objB) + assert y.int == 1 + + let z = m.allocTemp(Int8Id) + assert z.int == 2 + + m.freeLoc y + let y2 = m.allocTemp(objB) + assert y2.int == 1 + + diff --git a/compiler/nir/nirtypes.nim b/compiler/nir/nirtypes.nim new file mode 100644 index 0000000000000..a42feab005c82 --- /dev/null +++ b/compiler/nir/nirtypes.nim @@ -0,0 +1,352 @@ +# +# +# The Nim Compiler +# (c) Copyright 2023 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Type system for NIR. Close to C's type system but without its quirks. + +import std / [assertions, hashes] +import .. / ic / bitabs + +type + NirTypeKind* = enum + VoidTy, IntTy, UIntTy, FloatTy, BoolTy, CharTy, NameVal, IntVal, + AnnotationVal, + VarargsTy, # the `...` in a C prototype; also the last "atom" + APtrTy, # pointer to aliasable memory + UPtrTy, # pointer to unique/unaliasable memory + AArrayPtrTy, # pointer to array of aliasable memory + UArrayPtrTy, # pointer to array of unique/unaliasable memory + ArrayTy, + LastArrayTy, # array of unspecified size as a last field inside an object + ObjectTy, + UnionTy, + ProcTy, + ObjectDecl, + UnionDecl, + FieldDecl + +const + TypeKindBits = 8'u32 + TypeKindMask = (1'u32 shl TypeKindBits) - 1'u32 + +type + TypeNode* = object # 4 bytes + x: uint32 + +template kind*(n: TypeNode): NirTypeKind = NirTypeKind(n.x and TypeKindMask) +template operand(n: TypeNode): uint32 = (n.x shr TypeKindBits) + +template toX(k: NirTypeKind; operand: uint32): uint32 = + uint32(k) or (operand shl TypeKindBits) + +template toX(k: NirTypeKind; operand: LitId): uint32 = + uint32(k) or (operand.uint32 shl TypeKindBits) + +type + TypeId* = distinct int + +proc `==`*(a, b: TypeId): bool {.borrow.} +proc hash*(a: TypeId): Hash {.borrow.} + +type + TypeGraph* = object + nodes: seq[TypeNode] + names: BiTable[string] + numbers: BiTable[uint64] + +const + VoidId* = TypeId 0 + Bool8Id* = TypeId 1 + Char8Id* = TypeId 2 + Int8Id* = TypeId 3 + Int16Id* = TypeId 4 + Int32Id* = TypeId 5 + Int64Id* = TypeId 6 + UInt8Id* = TypeId 7 + UInt16Id* = TypeId 8 + UInt32Id* = TypeId 9 + UInt64Id* = TypeId 10 + Float32Id* = TypeId 11 + Float64Id* = TypeId 12 + VoidPtrId* = TypeId 13 + LastBuiltinId* = 13 + +proc initTypeGraph*(): TypeGraph = + result = TypeGraph(nodes: @[ + TypeNode(x: toX(VoidTy, 0'u32)), + TypeNode(x: toX(BoolTy, 8'u32)), + TypeNode(x: toX(CharTy, 8'u32)), + TypeNode(x: toX(IntTy, 8'u32)), + TypeNode(x: toX(IntTy, 16'u32)), + TypeNode(x: toX(IntTy, 32'u32)), + TypeNode(x: toX(IntTy, 64'u32)), + TypeNode(x: toX(UIntTy, 8'u32)), + TypeNode(x: toX(UIntTy, 16'u32)), + TypeNode(x: toX(UIntTy, 32'u32)), + TypeNode(x: toX(UIntTy, 64'u32)), + TypeNode(x: toX(FloatTy, 32'u32)), + TypeNode(x: toX(FloatTy, 64'u32)), + TypeNode(x: toX(APtrTy, 2'u32)), + TypeNode(x: toX(VoidTy, 0'u32)) + ]) + assert result.nodes.len == LastBuiltinId+2 + +type + TypePatchPos* = distinct int + +const + InvalidTypePatchPos* = TypePatchPos(-1) + LastAtomicValue = VarargsTy + +proc isValid(p: TypePatchPos): bool {.inline.} = p.int != -1 + +proc prepare(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = + result = TypePatchPos tree.nodes.len + tree.nodes.add TypeNode(x: toX(kind, 1'u32)) + +proc isAtom(tree: TypeGraph; pos: int): bool {.inline.} = tree.nodes[pos].kind <= LastAtomicValue +proc isAtom(tree: TypeGraph; pos: TypeId): bool {.inline.} = tree.nodes[pos.int].kind <= LastAtomicValue + +proc patch(tree: var TypeGraph; pos: TypePatchPos) = + let pos = pos.int + let k = tree.nodes[pos].kind + assert k > LastAtomicValue + let distance = int32(tree.nodes.len - pos) + assert distance > 0 + tree.nodes[pos].x = toX(k, cast[uint32](distance)) + +proc len*(tree: TypeGraph): int {.inline.} = tree.nodes.len + +template rawSpan(n: TypeNode): int = int(operand(n)) + +proc nextChild(tree: TypeGraph; pos: var int) {.inline.} = + if tree.nodes[pos].kind > LastAtomicValue: + assert tree.nodes[pos].operand > 0'u32 + inc pos, tree.nodes[pos].rawSpan + else: + inc pos + +iterator sons*(tree: TypeGraph; n: TypeId): TypeId = + var pos = n.int + assert tree.nodes[pos].kind > LastAtomicValue + let last = pos + tree.nodes[pos].rawSpan + inc pos + while pos < last: + yield TypeId pos + nextChild tree, pos + +template `[]`*(t: TypeGraph; n: TypeId): TypeNode = t.nodes[n.int] + +proc elementType*(tree: TypeGraph; n: TypeId): TypeId {.inline.} = + assert tree[n].kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, ArrayTy, LastArrayTy} + result = TypeId(n.int+1) + +proc kind*(tree: TypeGraph; n: TypeId): NirTypeKind {.inline.} = tree[n].kind + +proc span(tree: TypeGraph; pos: int): int {.inline.} = + if tree.nodes[pos].kind <= LastAtomicValue: 1 else: int(tree.nodes[pos].operand) + +proc sons2(tree: TypeGraph; n: TypeId): (TypeId, TypeId) = + assert(not isAtom(tree, n.int)) + let a = n.int+1 + let b = a + span(tree, a) + result = (TypeId a, TypeId b) + +proc sons3(tree: TypeGraph; n: TypeId): (TypeId, TypeId, TypeId) = + assert(not isAtom(tree, n.int)) + let a = n.int+1 + let b = a + span(tree, a) + let c = b + span(tree, b) + result = (TypeId a, TypeId b, TypeId c) + +proc arrayLen*(tree: TypeGraph; n: TypeId): BiggestUInt = + assert tree[n].kind == ArrayTy + result = tree.numbers[LitId tree[n].operand] + +proc openType*(tree: var TypeGraph; kind: NirTypeKind): TypePatchPos = + assert kind in {APtrTy, UPtrTy, AArrayPtrTy, UArrayPtrTy, + ArrayTy, LastArrayTy, ProcTy, ObjectDecl, UnionDecl, + FieldDecl} + result = prepare(tree, kind) + +proc sealType*(tree: var TypeGraph; p: TypePatchPos): TypeId = + # TODO: Search for an existing instance of this type in + # order to reduce memory consumption. + result = TypeId(p) + patch tree, p + +proc nominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string): TypeId = + assert kind in {ObjectTy, UnionTy} + result = TypeId tree.nodes.len + tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name))) + +proc addNominalType*(tree: var TypeGraph; kind: NirTypeKind; name: string) = + assert kind in {ObjectTy, UnionTy} + tree.nodes.add TypeNode(x: toX(kind, tree.names.getOrIncl(name))) + +proc addVarargs*(tree: var TypeGraph) = + tree.nodes.add TypeNode(x: toX(VarargsTy, 0'u32)) + +proc getFloat128Type*(tree: var TypeGraph): TypeId = + result = TypeId tree.nodes.len + tree.nodes.add TypeNode(x: toX(FloatTy, 128'u32)) + +proc addBuiltinType*(g: var TypeGraph; id: TypeId) = + g.nodes.add g[id] + +template firstSon(n: TypeId): TypeId = TypeId(n.int+1) + +proc addType*(g: var TypeGraph; t: TypeId) = + # We cannot simply copy `*Decl` nodes. We have to introduce `*Ty` nodes instead: + if g[t].kind in {ObjectDecl, UnionDecl}: + assert g[t.firstSon].kind == NameVal + let name = LitId g[t.firstSon].operand + if g[t].kind == ObjectDecl: + g.nodes.add TypeNode(x: toX(ObjectTy, name)) + else: + g.nodes.add TypeNode(x: toX(UnionTy, name)) + else: + let pos = t.int + let L = span(g, pos) + let d = g.nodes.len + g.nodes.setLen(d + L) + assert L > 0 + for i in 0..= 0, typeToString(t) + + let p = openType(c.g, ObjectDecl) + c.g.addName typeName + + let f = c.g.openType FieldDecl + let arr = c.g.openType AArrayPtrTy + c.g.addType elementType + discard sealType(c.g, arr) # LastArrayTy + c.g.addName "data" + discard sealType(c.g, f) # FieldDecl + + c.g.addField "len", c.nativeInt + + result = sealType(c.g, p) # ObjectDecl + +proc strPayloadType(c: var TypesCon): string = + result = "NimStrPayload" + let p = openType(c.g, ObjectDecl) + c.g.addName result + c.g.addField "cap", c.nativeInt + + let f = c.g.openType FieldDecl + let arr = c.g.openType LastArrayTy + c.g.addBuiltinType Char8Id + discard sealType(c.g, arr) # LastArrayTy + c.g.addName "data" + discard sealType(c.g, f) # FieldDecl + + discard sealType(c.g, p) + +proc strPayloadPtrType*(c: var TypesCon): TypeId = + let mangled = strPayloadType(c) + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, mangled + result = sealType(c.g, ffp) # APtrTy + +proc stringToIr(c: var TypesCon): TypeId = + #[ + + NimStrPayload = object + cap: int + data: UncheckedArray[char] + + NimStringV2 = object + len: int + p: ptr NimStrPayload + + ]# + let payload = strPayloadType(c) + + let str = openType(c.g, ObjectDecl) + c.g.addName "NimStringV2" + c.g.addField "len", c.nativeInt + + let fp = c.g.openType FieldDecl + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, "NimStrPayload" + discard sealType(c.g, ffp) # APtrTy + c.g.addName "p" + discard sealType(c.g, fp) # FieldDecl + + result = sealType(c.g, str) # ObjectDecl + +proc seqPayloadType(c: var TypesCon; t: PType): string = + #[ + NimSeqPayload[T] = object + cap: int + data: UncheckedArray[T] + ]# + let e = lastSon(t) + result = mangle(c, e) + let payloadName = "NimSeqPayload" & result + + let elementType = typeToIr(c, e) + + let p = openType(c.g, ObjectDecl) + c.g.addName payloadName + c.g.addField "cap", c.nativeInt + + let f = c.g.openType FieldDecl + let arr = c.g.openType LastArrayTy + c.g.addType elementType + discard sealType(c.g, arr) # LastArrayTy + c.g.addName "data" + discard sealType(c.g, f) # FieldDecl + discard sealType(c.g, p) + +proc seqPayloadPtrType*(c: var TypesCon; t: PType): TypeId = + let mangledBase = seqPayloadType(c, t) + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase + result = sealType(c.g, ffp) # APtrTy + +proc seqToIr(c: var TypesCon; t: PType): TypeId = + #[ + NimSeqV2*[T] = object + len: int + p: ptr NimSeqPayload[T] + ]# + let mangledBase = seqPayloadType(c, t) + + let sq = openType(c.g, ObjectDecl) + c.g.addName "NimSeqV2" & mangledBase + c.g.addField "len", c.nativeInt + + let fp = c.g.openType FieldDecl + let ffp = c.g.openType APtrTy + c.g.addNominalType ObjectTy, "NimSeqPayload" & mangledBase + discard sealType(c.g, ffp) # APtrTy + c.g.addName "p" + discard sealType(c.g, fp) # FieldDecl + + result = sealType(c.g, sq) # ObjectDecl + + +proc closureToIr(c: var TypesCon; t: PType): TypeId = + # struct {fn(args, void* env), env} + # typedef struct {$n" & + # "N_NIMCALL_PTR($2, ClP_0) $3;$n" & + # "void* ClE_0;$n} $1;$n" + let mangledBase = mangle(c, t) + let typeName = "NimClosure" & mangledBase + + let procType = procToIr(c, t, addEnv=true) + + let p = openType(c.g, ObjectDecl) + c.g.addName typeName + + let f = c.g.openType FieldDecl + c.g.addType procType + c.g.addName "ClP_0" + discard sealType(c.g, f) # FieldDecl + + let f2 = c.g.openType FieldDecl + let voidPtr = openType(c.g, APtrTy) + c.g.addBuiltinType(VoidId) + discard sealType(c.g, voidPtr) + + c.g.addName "ClE_0" + discard sealType(c.g, f2) # FieldDecl + + result = sealType(c.g, p) # ObjectDecl + +proc bitsetBasetype*(c: var TypesCon; t: PType): TypeId = + let s = int(getSize(c.conf, t)) + case s + of 1: result = UInt8Id + of 2: result = UInt16Id + of 4: result = UInt32Id + of 8: result = UInt64Id + else: result = UInt8Id + +proc typeToIr*(c: var TypesCon; t: PType): TypeId = + if t == nil: return VoidId + case t.kind + of tyInt: + case int(getSize(c.conf, t)) + of 2: result = Int16Id + of 4: result = Int32Id + else: result = Int64Id + of tyInt8: result = Int8Id + of tyInt16: result = Int16Id + of tyInt32: result = Int32Id + of tyInt64: result = Int64Id + of tyFloat: + case int(getSize(c.conf, t)) + of 4: result = Float32Id + else: result = Float64Id + of tyFloat32: result = Float32Id + of tyFloat64: result = Float64Id + of tyFloat128: result = getFloat128Type(c.g) + of tyUInt: + case int(getSize(c.conf, t)) + of 2: result = UInt16Id + of 4: result = UInt32Id + else: result = UInt64Id + of tyUInt8: result = UInt8Id + of tyUInt16: result = UInt16Id + of tyUInt32: result = UInt32Id + of tyUInt64: result = UInt64Id + of tyBool: result = Bool8Id + of tyChar: result = Char8Id + of tyVoid: result = VoidId + of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned, tyRange: + result = typeToIr(c, t.lastSon) + of tyEnum: + if firstOrd(c.conf, t) < 0: + result = Int32Id + else: + case int(getSize(c.conf, t)) + of 1: result = UInt8Id + of 2: result = UInt16Id + of 4: result = Int32Id + of 8: result = Int64Id + else: result = Int32Id + of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic: + if t.len > 0: + result = typeToIr(c, t.lastSon) + else: + result = TypeId(-1) + of tyFromExpr: + if t.n != nil and t.n.typ != nil: + result = typeToIr(c, t.n.typ) + else: + result = TypeId(-1) + of tyArray: + cached(c, t): + var n = toInt64(lengthOrd(c.conf, t)) + if n <= 0: n = 1 # make an array of at least one element + let elemType = typeToIr(c, t[1]) + let a = openType(c.g, ArrayTy) + c.g.addType(elemType) + c.g.addArrayLen uint64(n) + result = sealType(c.g, a) + of tyPtr, tyRef: + cached(c, t): + let e = t.lastSon + if e.kind == tyUncheckedArray: + let elemType = typeToIr(c, e.lastSon) + let a = openType(c.g, AArrayPtrTy) + c.g.addType(elemType) + result = sealType(c.g, a) + else: + let elemType = typeToIr(c, t.lastSon) + let a = openType(c.g, APtrTy) + c.g.addType(elemType) + result = sealType(c.g, a) + of tyVar, tyLent: + cached(c, t): + let e = t.lastSon + if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: + # skip the modifier, `var openArray` is a (ptr, len) pair too: + result = typeToIr(c, e) + else: + let elemType = typeToIr(c, e) + let a = openType(c.g, APtrTy) + c.g.addType(elemType) + result = sealType(c.g, a) + of tySet: + let s = int(getSize(c.conf, t)) + case s + of 1: result = UInt8Id + of 2: result = UInt16Id + of 4: result = UInt32Id + of 8: result = UInt64Id + else: + # array[U8, s] + cached(c, t): + let a = openType(c.g, ArrayTy) + c.g.addType(UInt8Id) + c.g.addArrayLen uint64(s) + result = sealType(c.g, a) + of tyPointer: + let a = openType(c.g, APtrTy) + c.g.addBuiltinType(VoidId) + result = sealType(c.g, a) + of tyObject: + # Objects are special as they can be recursive in Nim. This is easily solvable. + # We check if we are already "processing" t. If so, we produce `ObjectTy` + # instead of `ObjectDecl`. + cached(c, t): + if not c.recursionCheck.containsOrIncl(t.itemId): + result = objectToIr(c, t) + else: + result = objectHeaderToIr(c, t) + of tyTuple: + cached(c, t): + result = tupleToIr(c, t) + of tyProc: + cached(c, t): + if t.callConv == ccClosure: + result = closureToIr(c, t) + else: + result = procToIr(c, t) + of tyVarargs, tyOpenArray: + cached(c, t): + result = openArrayToIr(c, t) + of tyString: + cached(c, t): + result = stringToIr(c) + of tySequence: + cached(c, t): + result = seqToIr(c, t) + of tyCstring: + cached(c, t): + let a = openType(c.g, AArrayPtrTy) + c.g.addBuiltinType Char8Id + result = sealType(c.g, a) + of tyUncheckedArray: + # We already handled the `ptr UncheckedArray` in a special way. + cached(c, t): + let elemType = typeToIr(c, t.lastSon) + let a = openType(c.g, LastArrayTy) + c.g.addType(elemType) + result = sealType(c.g, a) + of tyUntyped, tyTyped: + # this avoids a special case for system.echo which is not a generic but + # uses `varargs[typed]`: + result = VoidId + of tyNone, tyEmpty, tyTypeDesc, + tyNil, tyGenericInvocation, tyProxy, tyBuiltInTypeClass, + tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, + tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward: + result = TypeId(-1) diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim index a5cfa54209e3b..7e46f3d0b5f41 100644 --- a/compiler/optimizer.nim +++ b/compiler/optimizer.nim @@ -66,7 +66,7 @@ proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} = proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool = result = false for i in 0.. 0: let b = a.split(".") assert b.len == 3, a @@ -578,6 +587,7 @@ proc newConfigRef*(): ConfigRef = maxLoopIterationsVM: 10_000_000, vmProfileData: newProfileData(), spellSuggestMax: spellSuggestSecretSauce, + currentConfigDir: "" ) initConfigRefCommon(result) setTargetFromSystem(result.target) @@ -654,12 +664,12 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = of "nimrawsetjmp": result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osMacosx} - else: discard + else: result = false template quitOrRaise*(conf: ConfigRef, msg = "") = # xxx in future work, consider whether to also intercept `msgQuit` calls if conf.isDefined("nimDebug"): - doAssert false, msg + raiseAssert msg else: quit(msg) # quits with QuitFailure @@ -880,6 +890,7 @@ const stdPrefix = "std/" proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile, isTitle = false): RelativeFile = + result = RelativeFile("") let f = $f if isTitle: for dir in stdlibDirs: @@ -915,6 +926,7 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFi result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true) else: if m.startsWith(stdPrefix): + result = AbsoluteFile("") let stripped = m.substr(stdPrefix.len) for candidate in stdlibDirs: let path = (conf.libpath.string / candidate / stripped) diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index 8cf209779eb15..30f407792ae94 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -17,6 +17,7 @@ iterator myParentDirs(p: string): string = proc getNimbleFile*(conf: ConfigRef; path: string): string = ## returns absolute path to nimble file, e.g.: /pathto/cligen.nimble + result = "" var parents = 0 block packageSearch: for d in myParentDirs(path): diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 534c3b5d1849d..98f5099d688ee 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -184,6 +184,7 @@ type arStrange # it is a strange beast like 'typedesc[var T]' proc exprRoot*(n: PNode; allowCalls = true): PSym = + result = nil var it = n while true: case it.kind diff --git a/compiler/parser.nim b/compiler/parser.nim index 1b8fd70a60ac8..7caeca95e15cf 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -310,7 +310,7 @@ proc checkBinary(p: Parser) {.inline.} = if p.tok.spacing == {tsTrailing}: parMessage(p, warnInconsistentSpacing, prettyTok(p.tok)) -#| module = stmt ^* (';' / IND{=}) +#| module = complexOrSimpleStmt ^* (';' / IND{=}) #| #| comma = ',' COMMENT? #| semicolon = ';' COMMENT? @@ -1177,6 +1177,7 @@ proc optPragmas(p: var Parser): PNode = proc parseDoBlock(p: var Parser; info: TLineInfo): PNode = #| doBlock = 'do' paramListArrow pragma? colcom stmt + result = nil var params = parseParamList(p, retColon=false) let pragmas = optPragmas(p) colcom(p, result) @@ -1430,6 +1431,7 @@ proc parseTypeDesc(p: var Parser, fullExpr = false): PNode = result = newNodeP(nkObjectTy, p) getTok(p) of tkConcept: + result = nil parMessage(p, "the 'concept' keyword is only valid in 'type' sections") of tkVar: result = parseTypeDescKAux(p, nkVarTy, pmTypeDesc) of tkOut: result = parseTypeDescKAux(p, nkOutTy, pmTypeDesc) @@ -2285,7 +2287,7 @@ proc parseTypeDef(p: var Parser): PNode = setEndInfo() proc parseVarTuple(p: var Parser): PNode = - #| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' + #| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)? #| varTuple = varTupleLhs '=' optInd expr result = newNodeP(nkVarTuple, p) getTok(p) # skip '(' @@ -2302,9 +2304,14 @@ proc parseVarTuple(p: var Parser): PNode = if p.tok.tokType != tkComma: break getTok(p) skipComment(p, a) - result.add(p.emptyNode) # no type desc optPar(p) eat(p, tkParRi) + if p.tok.tokType == tkColon: + getTok(p) + optInd(p, result) + result.add(parseTypeDesc(p, fullExpr = true)) + else: + result.add(p.emptyNode) # no type desc setEndInfo() proc parseVariable(p: var Parser): PNode = diff --git a/compiler/patterns.nim b/compiler/patterns.nim index ff9a9efa347e2..7b0d7e4fb43d2 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -29,6 +29,8 @@ type proc getLazy(c: PPatternContext, sym: PSym): PNode = if c.mappingIsFull: result = c.mapping[sym.position] + else: + result = nil proc putLazy(c: PPatternContext, sym: PSym, n: PNode) = if not c.mappingIsFull: @@ -65,14 +67,21 @@ proc sameTrees*(a, b: PNode): bool = for i in 0..= 2: for i in 1.. 1 var key = if keyDeep: it[0] else: it @@ -965,9 +971,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) incl(sym.flags, sfGlobal) incl(sym.flags, sfPure) - of wMerge: - # only supported for backwards compat, doesn't do anything anymore - noVal(c, it) of wConstructor: incl(sym.flags, sfConstructor) if sfImportc notin sym.flags: @@ -1123,6 +1126,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wLocalPassc: assert sym != nil and sym.kind == skModule let s = expectStrLit(c, it) + appendToModule(sym, n) extccomp.addLocalCompileOption(c.config, s, toFullPathConsiderDirty(c.config, sym.info.fileIndex)) recordPragma(c, it, "localpassl", s) of wPush: @@ -1273,12 +1277,14 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, pragmaProposition(c, it) of wEnsures: pragmaEnsures(c, it) - of wEnforceNoRaises: + of wEnforceNoRaises, wQuirky: sym.flags.incl sfNeverRaises of wSystemRaisesDefect: sym.flags.incl sfSystemRaisesDefect of wVirtual: - processVirtual(c, it, sym) + processVirtual(c, it, sym, sfVirtual) + of wMember: + processVirtual(c, it, sym, sfMember) else: invalidPragma(c, it) elif comesFromPush and whichKeyword(ident) != wInvalid: diff --git a/compiler/readme.md b/compiler/readme.md index ac47508dc4e96..4a197991c42e5 100644 --- a/compiler/readme.md +++ b/compiler/readme.md @@ -4,4 +4,4 @@ - Note that this code has been translated from a bootstrapping version written in Pascal. - So the code is **not** a poster child of good Nim code. -See [Internals of the Nim Compiler](https://nim-lang.org/docs/intern.html) for more information. +See [Internals of the Nim Compiler](https://nim-lang.github.io/Nim/intern.html) for more information. diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 3f237c9322551..3a6d0ae6502eb 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -14,7 +14,7 @@ {.used.} import - lexer, options, idents, strutils, ast, msgs, lineinfos + lexer, options, idents, strutils, ast, msgs, lineinfos, wordrecg when defined(nimPreviewSlimSystem): import std/[syncio, assertions, formatfloat] @@ -31,6 +31,10 @@ type length*: int16 sym*: PSym + Section = enum + GenericParams + ObjectDef + TRenderTokSeq* = seq[TRenderTok] TSrcGen* = object indent*: int @@ -45,7 +49,7 @@ type pendingWhitespace: int comStack*: seq[PNode] # comment stack flags*: TRenderFlags - inGenericParams: bool + inside: set[Section] # Keeps track of contexts we are in checkAnon: bool # we're in a context that can contain sfAnon inPragma: int when defined(nimpretty): @@ -72,6 +76,18 @@ proc isKeyword*(i: PIdent): bool = if (i.id >= ord(tokKeywordLow) - ord(tkSymbol)) and (i.id <= ord(tokKeywordHigh) - ord(tkSymbol)): result = true + else: + result = false + +proc isExported(n: PNode): bool = + ## Checks if an ident is exported. + ## This is meant to be used with idents in nkIdentDefs. + case n.kind + of nkPostfix: + n[0].ident.s == "*" and n[1].kind == nkIdent + of nkPragmaExpr: + n[0].isExported() + else: false proc renderDefinitionName*(s: PSym, noQuotes = false): string = ## Returns the definition name of the symbol. @@ -85,6 +101,25 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string = else: result = '`' & x & '`' +template inside(g: var TSrcGen, section: Section, body: untyped) = + ## Runs `body` with `section` included in `g.inside`. + ## Removes it at the end of the body if `g` wasn't inside it + ## before the template. + let wasntInSection = section notin g.inside + g.inside.incl section + body + if wasntInSection: + g.inside.excl section + +template outside(g: var TSrcGen, section: Section, body: untyped) = + ## Temporarily removes `section` from `g.inside`. Adds it back + ## at the end of the body if `g` was inside it before the template + let wasInSection = section in g.inside + g.inside.excl section + body + if wasInSection: + g.inside.incl section + const IndentWidth = 2 longIndentWid = IndentWidth * 2 @@ -110,19 +145,13 @@ const MaxLineLen = 80 LineCommentColumn = 30 -proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) = - g.comStack = @[] - g.tokens = @[] - g.indent = 0 - g.lineLen = 0 - g.pos = 0 - g.idx = 0 - g.buf = "" - g.flags = renderFlags - g.pendingNL = -1 - g.pendingWhitespace = -1 - g.inGenericParams = false - g.config = config +proc initSrcGen(renderFlags: TRenderFlags; config: ConfigRef): TSrcGen = + result = TSrcGen(comStack: @[], tokens: @[], indent: 0, + lineLen: 0, pos: 0, idx: 0, buf: "", + flags: renderFlags, pendingNL: -1, + pendingWhitespace: -1, inside: {}, + config: config + ) proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) = g.tokens.add TRenderTok(kind: kind, length: int16(s.len), sym: sym) @@ -241,6 +270,7 @@ proc putComment(g: var TSrcGen, s: string) = optNL(g) proc maxLineLength(s: string): int = + result = 0 if s.len == 0: return 0 var i = 0 let hi = s.len - 1 @@ -338,6 +368,7 @@ proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = tyLent, tyDistinct, tyOrdinal, tyAlias, tySink}: result = lastSon(result) + result = "" let typ = n.typ.skip if typ != nil and typ.kind in {tyBool, tyEnum}: if sfPure in typ.sym.flags: @@ -455,6 +486,7 @@ proc referencesUsing(n: PNode): bool = proc lsub(g: TSrcGen; n: PNode): int = # computes the length of a tree + result = 0 if isNil(n): return 0 if shouldRenderComment(g, n): return MaxLineLen + 1 case n.kind @@ -554,7 +586,7 @@ proc lsub(g: TSrcGen; n: PNode): int = if n.len > 1: result = MaxLineLen + 1 else: result = lsons(g, n) + len("using_") of nkReturnStmt: - if n.len > 0 and n[0].kind == nkAsgn: + if n.len > 0 and n[0].kind == nkAsgn and renderIr notin g.flags: result = len("return_") + lsub(g, n[0][1]) else: result = len("return_") + lsub(g, n[0]) @@ -592,14 +624,12 @@ type const emptyContext: TContext = (spacing: 0, flags: {}) -proc initContext(c: var TContext) = - c.spacing = 0 - c.flags = {} +proc initContext(): TContext = + result = (spacing: 0, flags: {}) proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) proc gsub(g: var TSrcGen, n: PNode, fromStmtList = false) = - var c: TContext - initContext(c) + var c: TContext = initContext() gsub(g, n, c, fromStmtList = fromStmtList) proc hasCom(n: PNode): bool = @@ -729,9 +759,8 @@ proc gcond(g: var TSrcGen, n: PNode) = put(g, tkParRi, ")") proc gif(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() gcond(g, n[0][0]) - initContext(c) putWithSpace(g, tkColon, ":") if longMode(g, n) or (lsub(g, n[0][1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) @@ -742,20 +771,18 @@ proc gif(g: var TSrcGen, n: PNode) = gsub(g, n[i], c) proc gwhile(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() putWithSpace(g, tkWhile, "while") gcond(g, n[0]) putWithSpace(g, tkColon, ":") - initContext(c) if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments gstmts(g, n[1], c) proc gpattern(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() put(g, tkCurlyLe, "{") - initContext(c) if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments @@ -763,20 +790,18 @@ proc gpattern(g: var TSrcGen, n: PNode) = put(g, tkCurlyRi, "}") proc gpragmaBlock(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() gsub(g, n[0]) putWithSpace(g, tkColon, ":") - initContext(c) if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments gstmts(g, n[1], c) proc gtry(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() put(g, tkTry, "try") putWithSpace(g, tkColon, ":") - initContext(c) if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments @@ -784,9 +809,8 @@ proc gtry(g: var TSrcGen, n: PNode) = gsons(g, n, c, 1) proc gfor(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() putWithSpace(g, tkFor, "for") - initContext(c) if longMode(g, n) or (lsub(g, n[^1]) + lsub(g, n[^2]) + 6 + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) @@ -799,8 +823,7 @@ proc gfor(g: var TSrcGen, n: PNode) = gstmts(g, n[^1], c) proc gcase(g: var TSrcGen, n: PNode) = - var c: TContext - initContext(c) + var c: TContext = initContext() if n.len == 0: return var last = if n[^1].kind == nkElse: -2 else: -1 if longMode(g, n, 0, last): incl(c.flags, rfLongMode) @@ -810,17 +833,17 @@ proc gcase(g: var TSrcGen, n: PNode) = optNL(g) gsons(g, n, c, 1, last) if last == - 2: - initContext(c) + c = initContext() if longMode(g, n[^1]): incl(c.flags, rfLongMode) gsub(g, n[^1], c) proc genSymSuffix(result: var string, s: PSym) {.inline.} = - if sfGenSym in s.flags: + if sfGenSym in s.flags and s.name.id != ord(wUnderscore): result.add '_' result.addInt s.id proc gproc(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() if n[namePos].kind == nkSym: let s = n[namePos].sym var ret = renderDefinitionName(s) @@ -831,25 +854,23 @@ proc gproc(g: var TSrcGen, n: PNode) = if n[patternPos].kind != nkEmpty: gpattern(g, n[patternPos]) - let oldInGenericParams = g.inGenericParams - g.inGenericParams = true - if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and - n[miscPos][1].kind != nkEmpty: - gsub(g, n[miscPos][1]) - else: - gsub(g, n[genericParamsPos]) - g.inGenericParams = oldInGenericParams + g.inside(GenericParams): + if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and + n[miscPos][1].kind != nkEmpty: + gsub(g, n[miscPos][1]) + else: + gsub(g, n[genericParamsPos]) gsub(g, n[paramsPos]) if renderNoPragmas notin g.flags: gsub(g, n[pragmasPos]) if renderNoBody notin g.flags: - if n[bodyPos].kind != nkEmpty: + if n.len > bodyPos and n[bodyPos].kind != nkEmpty: put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") indentNL(g) gcoms(g) dedent(g) - initContext(c) + c = initContext() gstmts(g, n[bodyPos], c) putNL(g) else: @@ -858,8 +879,7 @@ proc gproc(g: var TSrcGen, n: PNode) = dedent(g) proc gTypeClassTy(g: var TSrcGen, n: PNode) = - var c: TContext - initContext(c) + var c: TContext = initContext() putWithSpace(g, tkConcept, "concept") gsons(g, n[0], c) # arglist gsub(g, n[1]) # pragmas @@ -878,8 +898,7 @@ proc gblock(g: var TSrcGen, n: PNode) = if n.len == 0: return - var c: TContext - initContext(c) + var c: TContext = initContext() if n[0].kind != nkEmpty: putWithSpace(g, tkBlock, "block") @@ -899,10 +918,9 @@ proc gblock(g: var TSrcGen, n: PNode) = gstmts(g, n[1], c) proc gstaticStmt(g: var TSrcGen, n: PNode) = - var c: TContext + var c: TContext = initContext() putWithSpace(g, tkStatic, "static") putWithSpace(g, tkColon, ":") - initContext(c) if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen): incl(c.flags, rfLongMode) gcoms(g) # a good place for comments @@ -916,7 +934,7 @@ proc gasm(g: var TSrcGen, n: PNode) = gsub(g, n[1]) proc gident(g: var TSrcGen, n: PNode) = - if g.inGenericParams and n.kind == nkSym: + if GenericParams in g.inside and n.kind == nkSym: if sfAnon in n.sym.flags or (n.typ != nil and tfImplicitTypeParam in n.typ.flags): return @@ -940,7 +958,9 @@ proc gident(g: var TSrcGen, n: PNode) = s.addInt localId if sfCursor in n.sym.flags: s.add "_cursor" - elif n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags or n.sym.kind == skTemp): + elif n.kind == nkSym and (renderIds in g.flags or + (sfGenSym in n.sym.flags and n.sym.name.id != ord(wUnderscore)) or + n.sym.kind == skTemp): s.add '_' s.addInt n.sym.id when defined(debugMagics): @@ -974,6 +994,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind = case n.kind of nkClosedSymChoice, nkOpenSymChoice: if n.len > 0: result = bracketKind(g, n[0]) + else: result = bkNone of nkSym: result = case n.sym.name.s of "[]": bkBracket @@ -982,6 +1003,8 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind = of "{}=": bkCurlyAsgn else: bkNone else: result = bkNone + else: + result = bkNone proc skipHiddenNodes(n: PNode): PNode = result = n @@ -1007,7 +1030,7 @@ proc accentedName(g: var TSrcGen, n: PNode) = gsub(g, n) proc infixArgument(g: var TSrcGen, n: PNode, i: int) = - if i < 1 and i > 2: return + if i < 1 or i > 2: return var needsParenthesis = false let nNext = n[i].skipHiddenNodes if nNext.kind == nkInfix: @@ -1054,11 +1077,13 @@ proc isCustomLit(n: PNode): bool = if n.len == 2 and n[0].kind == nkRStrLit: let ident = n[1].getPIdent result = ident != nil and ident.s.startsWith('\'') + else: + result = false proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = if isNil(n): return var - a: TContext + a: TContext = default(TContext) if shouldRenderComment(g, n): pushCom(g, n) case n.kind # atoms: of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n)) @@ -1304,14 +1329,31 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = gsub(g, n, pragmasPos) put(g, tkColon, ":") gsub(g, n, bodyPos) - of nkConstDef, nkIdentDefs: + of nkIdentDefs: + # Skip if this is a property in a type and its not exported + # (While also not allowing rendering of non exported fields) + if ObjectDef in g.inside and (not n[0].isExported() and renderNonExportedFields notin g.flags): + return + # We render the identDef without being inside the section incase we render something like + # y: proc (x: string) # (We wouldn't want to check if x is exported) + g.outside(ObjectDef): + gcomma(g, n, 0, -3) + if n.len >= 2 and n[^2].kind != nkEmpty: + putWithSpace(g, tkColon, ":") + gsub(g, n[^2], c) + elif n.referencesUsing and renderExpandUsing in g.flags: + putWithSpace(g, tkColon, ":") + gsub(g, newSymNode(n.origUsingType), c) + + if n.len >= 1 and n[^1].kind != nkEmpty: + put(g, tkSpaces, Space) + putWithSpace(g, tkEquals, "=") + gsub(g, n[^1], c) + of nkConstDef: gcomma(g, n, 0, -3) if n.len >= 2 and n[^2].kind != nkEmpty: putWithSpace(g, tkColon, ":") gsub(g, n[^2], c) - elif n.referencesUsing and renderExpandUsing in g.flags: - putWithSpace(g, tkColon, ":") - gsub(g, newSymNode(n.origUsingType), c) if n.len >= 1 and n[^1].kind != nkEmpty: put(g, tkSpaces, Space) @@ -1334,6 +1376,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = putWithSpace(g, tkColon, ":") gsub(g, n, 1) of nkInfix: + if n.len < 3: + var i = 0 + put(g, tkOpr, "Too few children for nkInfix") + return let oldLineLen = g.lineLen # we cache this because lineLen gets updated below infixArgument(g, n, 1) put(g, tkSpaces, Space) @@ -1385,6 +1431,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = if n.kind in {nkIdent, nkSym}: let tmp = n.getPIdent.s result = tmp.len > 0 and tmp[0] in {'a'..'z', 'A'..'Z'} + else: + result = false var useSpace = false if i == 1 and n[0].kind == nkIdent and n[0].ident.s in ["=", "'"]: if not n[1].isAlpha: # handle `=destroy`, `'big' @@ -1468,20 +1516,19 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = of nkObjectTy: if n.len > 0: putWithSpace(g, tkObject, "object") - gsub(g, n[0]) - gsub(g, n[1]) - gcoms(g) - gsub(g, n[2]) + g.inside(ObjectDef): + gsub(g, n[0]) + gsub(g, n[1]) + gcoms(g) + gsub(g, n[2]) else: put(g, tkObject, "object") of nkRecList: indentNL(g) for i in 0.. 0 and n[0].kind == nkAsgn: + if n.len > 0 and n[0].kind == nkAsgn and renderIr notin g.flags: gsub(g, n[0], 1) else: gsub(g, n, 0) @@ -1736,13 +1783,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = gsub(g, n, 0) put(g, tkParRi, ")") of nkGotoState: - var c: TContext - initContext c + var c: TContext = initContext() putWithSpace g, tkSymbol, "goto" gsons(g, n, c) of nkState: - var c: TContext - initContext c + var c: TContext = initContext() putWithSpace g, tkSymbol, "state" gsub(g, n[0], c) putWithSpace(g, tkColon, ":") @@ -1766,8 +1811,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string = if n == nil: return "" - var g: TSrcGen - initSrcGen(g, renderFlags, newPartialConfigRef()) + var g: TSrcGen = initSrcGen(renderFlags, newPartialConfigRef()) # do not indent the initial statement list so that # writeFile("file.nim", repr n) # produces working Nim code: @@ -1784,9 +1828,8 @@ proc renderModule*(n: PNode, outfile: string, fid = FileIndex(-1); conf: ConfigRef = nil) = var - f: File - g: TSrcGen - initSrcGen(g, renderFlags, conf) + f: File = default(File) + g: TSrcGen = initSrcGen(renderFlags, conf) g.fid = fid for i in 0..= frmt.len or frmt[i] notin {'0'..'9'}: break num = j if j > high(args) + 1: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt else: result.add(args[j-1]) of '{': @@ -88,10 +88,10 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope = num = j if frmt[i] == '}': inc(i) else: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt if j > high(args) + 1: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt else: result.add(args[j-1]) of 'n': @@ -101,7 +101,7 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope = result.add("\n") inc(i) else: - doAssert false, "invalid format string: " & frmt + raiseAssert "invalid format string: " & frmt else: result.add(frmt[i]) inc(i) @@ -119,7 +119,7 @@ const proc equalsFile*(s: Rope, f: File): bool = ## returns true if the contents of the file `f` equal `r`. var - buf: array[bufSize, char] + buf: array[bufSize, char] = default(array[bufSize, char]) bpos = buf.len blen = buf.len btotal = 0 @@ -151,7 +151,7 @@ proc equalsFile*(s: Rope, f: File): bool = proc equalsFile*(r: Rope, filename: AbsoluteFile): bool = ## returns true if the contents of the file `f` equal `r`. If `f` does not ## exist, false is returned. - var f: File + var f: File = default(File) result = open(f, filename.string) if result: result = equalsFile(r, f) diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 21b0eb19561cf..c89767296b268 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -17,7 +17,7 @@ import pathutils, pipelines when defined(nimPreviewSlimSystem): - import std/syncio + import std/[syncio, assertions] # we support 'cmpIgnoreStyle' natively for efficiency: from strutils import cmpIgnoreStyle, contains @@ -160,6 +160,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; cbconf getCommand: setResult(a, conf.command) cbconf switch: + conf.currentConfigDir = vthisDir processSwitch(a.getString 0, a.getString 1, passPP, module.info, conf) cbconf hintImpl: processSpecificNote(a.getString 0, wHint, passPP, module.info, @@ -207,8 +208,8 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; let oldGlobalOptions = conf.globalOptions let oldSelectedGC = conf.selectedGC - undefSymbol(conf.symbols, "nimv2") - conf.globalOptions.excl {optTinyRtti, optOwnedRefs, optSeqDestructors} + unregisterArcOrc(conf) + conf.globalOptions.excl optOwnedRefs conf.selectedGC = gcUnselected var m = graph.makeModule(scriptName) @@ -230,6 +231,17 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}: conf.globalOptions.incl {optTinyRtti, optSeqDestructors} defineSymbol(conf.symbols, "nimv2") + defineSymbol(conf.symbols, "gcdestructors") + defineSymbol(conf.symbols, "nimSeqsV2") + case conf.selectedGC + of gcArc: + defineSymbol(conf.symbols, "gcarc") + of gcOrc: + defineSymbol(conf.symbols, "gcorc") + of gcAtomicArc: + defineSymbol(conf.symbols, "gcatomicarc") + else: + raiseAssert "unreachable" # ensure we load 'system.nim' again for the real non-config stuff! resetSystemArtifacts(graph) diff --git a/compiler/sem.nim b/compiler/sem.nim index f92853d9e3442..55c6a427f5194 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -19,14 +19,18 @@ import intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, lowerings, plugins/active, lineinfos, strtabs, int128, - isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs + isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs, + extccomp when not defined(leanCompiler): import spawn when defined(nimPreviewSlimSystem): - import std/formatfloat + import std/[ + formatfloat, + assertions, + ] # implementation @@ -96,6 +100,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = result.typ = formal elif arg.kind in nkSymChoices and formal.skipTypes(abstractInst).kind == tyEnum: # Pick the right 'sym' from the sym choice by looking at 'formal' type: + result = nil for ch in arg: if sameType(ch.typ, formal): return getConstExpr(c.module, ch, c.idgen, c.graph) @@ -149,7 +154,7 @@ proc commonType*(c: PContext; x, y: PType): PType = let idx = ord(b.kind == tyArray) if a[idx].kind == tyEmpty: return y elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len: - var nt: PType + var nt: PType = nil for i in 0.. 0: it = it.lastSon - result = it.kind in nkLastBlockStmts or - it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags + + case it.kind + of nkIfStmt: + var hasElse = false + for branch in it: + checkBranch: + if branch.len == 2: + branch[1] + elif branch.len == 1: + hasElse = true + branch[0] + else: + raiseAssert "Malformed `if` statement during endsInNoReturn" + # none of the branches returned + result = hasElse # Only truly a no-return when it's exhaustive + of nkCaseStmt: + for i in 1 ..< it.len: + let branch = it[i] + checkBranch: + case branch.kind + of nkOfBranch: + branch[^1] + of nkElifBranch: + branch[1] + of nkElse: + branch[0] + else: + raiseAssert "Malformed `case` statement in endsInNoReturn" + # none of the branches returned + result = true + of nkTryStmt: + checkBranch(it[0]) + for i in 1 ..< it.len: + let branch = it[i] + checkBranch(branch[^1]) + # none of the branches returned + result = true + else: + result = it.kind in nkLastBlockStmts or + it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags proc commonType*(c: PContext; x: PType, y: PNode): PType = # ignore exception raising branches in case/if expressions @@ -279,16 +330,6 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}; expectedType: PType = nil): PNode -proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym = - if t.sym != nil: return t.sym - result = newSym(skType, getIdent(c.cache, "AnonType"), c.idgen, t.owner, info) - result.flags.incl sfAnon - result.typ = t - -proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode = - result = newSymNode(symFromType(c, t, info), info) - result.typ = makeTypeDesc(c, t) - when false: proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext = result = newEvalContext(c.module, mode) @@ -305,6 +346,7 @@ when false: result = isOpImpl(c, n) proc hasCycle(n: PNode): bool = + result = false incl n.flags, nfNone for i in 0.. 0: var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp)) asgnExpr.typ = aTyp asgnExpr.sons.add child result = semExpr(c, asgnExpr) + else: + result = nil elif aTypSkip.kind == tyArray: - let child = defaultNodeField(c, a, aTypSkip[1]) + let child = defaultNodeField(c, a, aTypSkip[1], checkDefault) if child != nil: let node = newNode(nkIntLit) @@ -644,18 +709,26 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode = node )) result.typ = aTyp + else: + result = nil elif aTypSkip.kind == tyTuple: var hasDefault = false if aTypSkip.n != nil: - let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault) + let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault) if hasDefault and children.len > 0: result = newNodeI(nkTupleConstr, a.info) result.typ = aTyp result.sons.add children result = semExpr(c, result) + else: + result = nil + else: + result = nil + else: + result = nil -proc defaultNodeField(c: PContext, a: PNode): PNode = - result = defaultNodeField(c, a, a.typ) +proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode = + result = defaultNodeField(c, a, a.typ, checkDefault) include semtempl, semgnrc, semstmts, semexprs @@ -712,17 +785,19 @@ proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool = break case n.kind of nkImportStmt: + result = false for x in n: if x.kind == nkIdent: let f = checkModuleName(g.config, x, false) if f == g.systemModule.info.fileIndex: return true of nkImportExceptStmt, nkFromStmt: + result = false if n[0].kind == nkIdent: let f = checkModuleName(g.config, n[0], false) if f == g.systemModule.info.fileIndex: return true - else: discard + else: result = false proc isEmptyTree(n: PNode): bool = case n.kind diff --git a/compiler/semcall.nim b/compiler/semcall.nim index d2460ab06df7d..a4114497fb98a 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -51,9 +51,9 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode, result.add((symx, o.lastOverloadScope)) symx = nextOverloadIter(o, c, headSymbol) if result.len > 0: - initCandidate(c, best, result[0].s, initialBinding, + best = initCandidate(c, result[0].s, initialBinding, result[0].scope, diagnostics) - initCandidate(c, alt, result[0].s, initialBinding, + alt = initCandidate(c, result[0].s, initialBinding, result[0].scope, diagnostics) best.state = csNoMatch @@ -85,7 +85,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, var z: TCandidate # current candidate while true: determineType(c, sym) - initCandidate(c, z, sym, initialBinding, scope, diagnosticsFlag) + z = initCandidate(c, sym, initialBinding, scope, diagnosticsFlag) # this is kinda backwards as without a check here the described # problems in recalc would not happen, but instead it 100% @@ -215,7 +215,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): var maybeWrongSpace = false - var candidatesAll: seq[string] + var candidatesAll: seq[string] = @[] var candidates = "" var skipped = 0 for err in errors: @@ -370,6 +370,7 @@ proc bracketNotFoundError(c: PContext; n: PNode) = notFoundError(c, n, errors) proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = + result = "" if c.compilesContextId > 0: # we avoid running more diagnostic when inside a `compiles(expr)`, to # errors while running diagnostic (see test D20180828T234921), and @@ -405,8 +406,9 @@ proc resolveOverloads(c: PContext, n, orig: PNode, filter: TSymKinds, flags: TExprFlags, errors: var CandidateErrors, errorsEnabled: bool): TCandidate = + result = default(TCandidate) var initialBinding: PNode - var alt: TCandidate + var alt: TCandidate = default(TCandidate) var f = n[0] if f.kind == nkBracketExpr: # fill in the bindings: @@ -562,8 +564,64 @@ proc getCallLineInfo(n: PNode): TLineInfo = discard result = n.info -proc semResolvedCall(c: PContext, x: TCandidate, - n: PNode, flags: TExprFlags): PNode = +proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) = + ## Helper proc to inherit bound generic parameters from expectedType into x. + ## Does nothing if 'inferGenericTypes' isn't in c.features. + if inferGenericTypes notin c.features: return + if expectedType == nil or x.callee[0] == nil: return # required for inference + + var + flatUnbound: seq[PType] = @[] + flatBound: seq[PType] = @[] + # seq[(result type, expected type)] + var typeStack = newSeq[(PType, PType)]() + + template stackPut(a, b) = + ## skips types and puts the skipped version on stack + # It might make sense to skip here one by one. It's not part of the main + # type reduction because the right side normally won't be skipped + const toSkip = { tyVar, tyLent, tyStatic, tyCompositeTypeClass, tySink } + let + x = a.skipTypes(toSkip) + y = if a.kind notin toSkip: b + else: b.skipTypes(toSkip) + typeStack.add((x, y)) + + stackPut(x.callee[0], expectedType) + + while typeStack.len() > 0: + let (t, u) = typeStack.pop() + if t == u or t == nil or u == nil or t.kind == tyAnything or u.kind == tyAnything: + continue + case t.kind + of ConcreteTypes, tyGenericInvocation, tyUncheckedArray: + # nested, add all the types to stack + let + startIdx = if u.kind in ConcreteTypes: 0 else: 1 + endIdx = min(u.len() - startIdx, t.len()) + + for i in startIdx ..< endIdx: + # early exit with current impl + if t[i] == nil or u[i] == nil: return + stackPut(t[i], u[i]) + of tyGenericParam: + let prebound = x.bindings.idTableGet(t).PType + if prebound != nil: + continue # Skip param, already bound + + # fully reduced generic param, bind it + if t notin flatUnbound: + flatUnbound.add(t) + flatBound.add(u) + else: + discard + # update bindings + for i in 0 ..< flatUnbound.len(): + x.bindings.idTablePut(flatUnbound[i], flatBound[i]) + +proc semResolvedCall(c: PContext, x: var TCandidate, + n: PNode, flags: TExprFlags; + expectedType: PType = nil): PNode = assert x.state == csMatch var finalCallee = x.calleeSym let info = getCallLineInfo(n) @@ -583,10 +641,12 @@ proc semResolvedCall(c: PContext, x: TCandidate, if x.calleeSym.magic in {mArrGet, mArrPut}: finalCallee = x.calleeSym else: + c.inheritBindings(x, expectedType) finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info) else: # For macros and templates, the resolved generic params # are added as normal params. + c.inheritBindings(x, expectedType) for s in instantiateGenericParamList(c, gp, x.bindings): case s.kind of skConst: @@ -595,7 +655,12 @@ proc semResolvedCall(c: PContext, x: TCandidate, else: x.call.add c.graph.emptyNode of skType: - x.call.add newSymNode(s, n.info) + var tn = newSymNode(s, n.info) + # this node will be used in template substitution, + # pretend this is an untyped node and let regular sem handle the type + # to prevent problems where a generic parameter is treated as a value + tn.typ = nil + x.call.add tn else: internalAssert c.config, false @@ -615,7 +680,8 @@ proc tryDeref(n: PNode): PNode = result.add n proc semOverloadedCall(c: PContext, n, nOrig: PNode, - filter: TSymKinds, flags: TExprFlags): PNode = + filter: TSymKinds, flags: TExprFlags; + expectedType: PType = nil): PNode = var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) if r.state == csMatch: @@ -625,7 +691,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, message(c.config, n.info, hintUserRaw, "Non-matching candidates for " & renderTree(n) & "\n" & candidates) - result = semResolvedCall(c, r, n, flags) + result = semResolvedCall(c, r, n, flags, expectedType) else: if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil: result = semGenericStmt(c, n) @@ -635,7 +701,10 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, # this time enabling all the diagnostic output (this should fail again) result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain}) elif efNoUndeclared notin flags: + result = nil notFoundError(c, n, errors) + else: + result = nil proc explicitGenericInstError(c: PContext; n: PNode): PNode = localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n)) @@ -653,8 +722,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = if formal.kind == tyStatic and arg.kind != tyStatic: let evaluated = c.semTryConstExpr(c, n[i]) if evaluated != nil: - arg = newTypeS(tyStatic, c) - arg.sons = @[evaluated.typ] + arg = newTypeS(tyStatic, c, sons = @[evaluated.typ]) arg.n = evaluated let tm = typeRel(m, formal, arg) if tm in {isNone, isConvertible}: return nil @@ -665,14 +733,18 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = onUse(info, s) result = newSymNode(newInst, info) -proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = - assert n.kind == nkBracketExpr +proc setGenericParams(c: PContext, n: PNode) = + ## sems generic params in subscript expression for i in 1.. 0: - result = ambiguousSymChoice(c, n, result) elif result.typ == nil or result.typ == c.enforceVoidContext: localError(c.config, n.info, errExprXHasNoType % renderTree(result, {renderNoComments})) @@ -155,6 +126,39 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = result = symChoice(c, n, s, scClosed) +proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode + +proc isSymChoice(n: PNode): bool {.inline.} = + result = n.kind in nkSymChoices + +proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode = + result = n + if expectedType != nil: + result = fitNode(c, expectedType, result, n.info) + if isSymChoice(result) and efAllowSymChoice notin flags: + # some contexts might want sym choices preserved for later disambiguation + # in general though they are ambiguous + let first = n[0].sym + var foundSym: PSym = nil + if first.kind == skEnumField and + not isAmbiguous(c, first.name, {skEnumField}, foundSym) and + foundSym == first: + # choose the first resolved enum field, i.e. the latest in scope + # to mirror behavior before overloadable enums + result = n[0] + else: + var err = "ambiguous identifier '" & first.name.s & + "' -- use one of the following:\n" + for child in n: + let candidate = child.sym + err.add " " & candidate.owner.name.s & "." & candidate.name.s + err.add ": " & typeToString(candidate.typ) & "\n" + localError(c.config, n.info, err) + n.typ = errorType(c) + result = n + if result.kind == nkSym: + result = semSym(c, result, result.sym, flags) + proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} = result = copyTree(s.astdef) if result.isNil: @@ -238,6 +242,9 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = result = convNotInRange else: # we use d, s here to speed up that operation a bit: + if d.kind == tyFromExpr: + result = convNotLegal + return case cmpTypes(c, d, s) of isNone, isGeneric: if not compareTypes(targetTyp.skipTypes(abstractVar), srcTyp.skipTypes({tyOwned}), dcEqIgnoreDistinct): @@ -288,17 +295,9 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool = result = (dstSize >= srcSize) or (skipTypes(dst, abstractInst).kind in IntegralTypes) or (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes) - if result and (dstSize > srcSize): - var warnMsg = "target type is larger than source type" - warnMsg.add("\n target type: '$1' ($2)" % [$dst, if dstSize == 1: "1 byte" else: $dstSize & " bytes"]) - warnMsg.add("\n source type: '$1' ($2)" % [$src, if srcSize == 1: "1 byte" else: $srcSize & " bytes"]) - message(conf, info, warnCastSizes, warnMsg) if result and src.kind == tyNil: return dst.size <= conf.target.ptrSize -proc isSymChoice(n: PNode): bool {.inline.} = - result = n.kind in nkSymChoices - proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) = # XXX: liftParamType started to perform addDecl # we could do that instead in semTypeNode by snooping for added @@ -313,7 +312,7 @@ proc isOwnedSym(c: PContext; n: PNode): bool = let s = qualifiedLookUp(c, n, {}) result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned" -proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = +proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType = nil): PNode = if n.len != 2: localError(c.config, n.info, "a type conversion takes exactly one argument") return n @@ -360,10 +359,10 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = if n[1].kind == nkExprEqExpr and targetType.skipTypes(abstractPtrs).kind == tyObject: localError(c.config, n.info, "object construction uses ':', not '='") - var op = semExprWithType(c, n[1]) - if op.kind == nkClosedSymChoice and op.len > 0 and - op[0].sym.kind == skEnumField: # resolves overloadedable enums - op = ambiguousSymChoice(c, n, op) + var op = semExprWithType(c, n[1], flags * {efDetermineType} + {efAllowSymChoice}) + if isSymChoice(op) and op[0].sym.kind notin routineKinds: + # T(foo) disambiguation syntax only allowed for routines + op = semSymChoice(c, op) if targetType.kind != tyGenericParam and targetType.isMetaType: let final = inferWithMetatype(c, targetType, op, true) result.add final @@ -375,7 +374,9 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = # here or needs to be overwritten too then. result.add op - if targetType.kind == tyGenericParam: + if targetType.kind == tyGenericParam or + (op.typ != nil and op.typ.kind == tyFromExpr and c.inGenericContext > 0): + # expression is compiled early in a generic body result.typ = makeTypeFromExpr(c, copyTree(result)) return result @@ -389,7 +390,8 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple: op = fitNode(c, targetType, op, result.info) of convNotNeedeed: - message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) + if efNoSem2Check notin flags: + message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) of convNotLegal: result = fitNode(c, result.typ, result[1], result.info) if result == nil: @@ -657,7 +659,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp else: discard rawAddSon(result.typ, nil) # index type var - firstIndex, lastIndex: Int128 + firstIndex, lastIndex: Int128 = Zero indexType = getSysType(c.graph, n.info, tyInt) lastValidIndex = lastOrd(c.config, indexType) if n.len == 0: @@ -957,17 +959,17 @@ proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode = result = fixupTypeAfterEval(c, result, a) proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, - flags: TExprFlags): PNode = + flags: TExprFlags; expectedType: PType = nil): PNode = if flags*{efInTypeof, efWantIterator, efWantIterable} != {}: # consider: 'for x in pReturningArray()' --> we don't want the restriction # to 'skIterator' anymore; skIterator is preferred in sigmatch already # for typeof support. # for ``typeof(countup(1,3))``, see ``tests/ttoseq``. result = semOverloadedCall(c, n, nOrig, - {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags) + {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags, expectedType) else: result = semOverloadedCall(c, n, nOrig, - {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags) + {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags, expectedType) if result != nil: if result[0].kind != nkSym: @@ -992,7 +994,7 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, proc resolveIndirectCall(c: PContext; n, nOrig: PNode; t: PType): TCandidate = - initCandidate(c, result, t) + result = initCandidate(c, t) matches(c, n, nOrig, result) proc bracketedMacro(n: PNode): PSym = @@ -1000,10 +1002,8 @@ proc bracketedMacro(n: PNode): PSym = result = n[0].sym if result.kind notin {skMacro, skTemplate}: result = nil - -proc setGenericParams(c: PContext, n: PNode) = - for i in 1.. 0: # don't interpret () as type isTupleType = tupexp[0].typ.kind == tyTypeDesc # check if either everything or nothing is tyTypeDesc @@ -2839,6 +2875,7 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp result = tupexp proc shouldBeBracketExpr(n: PNode): bool = + result = false assert n.kind in nkCallKinds let a = n[0] if a.kind in nkCallKinds: @@ -2856,6 +2893,8 @@ proc asBracketExpr(c: PContext; n: PNode): PNode = if n.kind in {nkIdent, nkAccQuoted}: let s = qualifiedLookUp(c, n, {}) result = s != nil and isGenericRoutineStrict(s) + else: + result = false assert n.kind in nkCallKinds if n.len > 1 and isGeneric(c, n[1]): @@ -2888,7 +2927,7 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) if defExpr.kind == nkSym and defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym: let paramPos = defExpr.sym.position + 1 - if call[paramPos].kind != nkSym: + if call[paramPos].skipAddr.kind != nkSym: let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.idgen, c.p.owner, letSection.info, c.p.owner.options) hoistedVarSym.typ = call[paramPos].typ @@ -2900,7 +2939,7 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) call[paramPos] = newSymNode(hoistedVarSym) # Refer the original arg to its hoisted sym - # arg we refer to is a sym, wether introduced by hoisting or not doesn't matter, we simply reuse it + # arg we refer to is a sym, whether introduced by hoisting or not doesn't matter, we simply reuse it defExpr = call[paramPos] else: for i in 0.. 2: b = getConstExpr(m, n[2], idgen, g) @@ -396,7 +413,8 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P of tyBool, tyEnum: # xxx shouldn't we disallow `tyEnum`? result = a result.typ = n.typ - else: doAssert false, $srcTyp.kind + else: + raiseAssert $srcTyp.kind of tyInt..tyInt64, tyUInt..tyUInt64: case srcTyp.kind of tyFloat..tyFloat64: @@ -420,7 +438,7 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P result = a result.typ = n.typ of tyOpenArray, tyVarargs, tyProc, tyPointer: - discard + result = nil else: result = a result.typ = n.typ @@ -447,21 +465,25 @@ proc foldArrayAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNo result = x.sons[idx] if result.kind == nkExprColonExpr: result = result[1] else: + result = nil localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n) of nkBracket: idx -= toInt64(firstOrd(g.config, x.typ)) if idx >= 0 and idx < x.len: result = x[int(idx)] - else: localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n) + else: + result = nil + localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n) of nkStrLit..nkTripleStrLit: result = newNodeIT(nkCharLit, x.info, n.typ) if idx >= 0 and idx < x.strVal.len: result.intVal = ord(x.strVal[int(idx)]) else: localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n) - else: discard + else: result = nil proc foldFieldAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = # a real field access; proc calls have already been transformed + result = nil if n[1].kind != nkSym: return nil var x = getConstExpr(m, n[0], idgen, g) if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return @@ -496,6 +518,7 @@ proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode = result.typ = s.typ proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = + result = nil var name = s.name.s let prag = extractPragma(s) if prag != nil: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 70cb64b5168cc..eebf11c0a0a7a 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -59,6 +59,7 @@ template isMixedIn(sym): bool = proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, ctx: var GenericCtx; flags: TSemGenericFlags, fromDotExpr=false): PNode = + result = nil semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody) incl(s.flags, sfUsed) template maybeDotChoice(c: PContext, n: PNode, s: PSym, fromDotExpr: bool) = @@ -167,6 +168,17 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, elif s.isMixedIn: result = newDot(result, symChoice(c, n, s, scForceOpen)) else: + if s.kind == skType and candidates.len > 1: + var ambig = false + let s2 = searchInScopes(c, ident, ambig) + if ambig: + # this is a type conversion like a.T where T is ambiguous with + # other types or routines + # in regular code, this never considers a type conversion and + # skips to routine overloading + # so symchoices are used which behave similarly with type symbols + result = newDot(result, symChoice(c, n, s, scForceOpen)) + return let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true) result = newDot(result, syms) @@ -176,6 +188,18 @@ proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = styleCheckDef(c, n.info, s, kind) onDef(n.info, s) +proc addTempDeclToIdents(c: PContext; n: PNode; kind: TSymKind; inCall: bool) = + case n.kind + of nkIdent: + if inCall: + addTempDecl(c, n, kind) + of nkCallKinds: + for s in n: + addTempDeclToIdents(c, s, kind, true) + else: + for s in n: + addTempDeclToIdents(c, s, kind, inCall) + proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = result = n @@ -348,7 +372,9 @@ proc semGenericStmt(c: PContext, n: PNode, var a = n[i] checkMinSonsLen(a, 1, c.config) for j in 0.. 0: + openScope(c) + for i in 0.. 0: + localError(c.config, discriminatorVal.info, "branch initialization " & + "with a runtime discriminator is not supported " & + "for a branch whose fields have default values.") + discard collectMissingCaseFields(c, n[i], constrCtx, @[]) + proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] = + result = (initUnknown, @[]) case n.kind of nkRecList: for field in n: @@ -321,13 +347,27 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, discriminator.sym, constrCtx.initExpr) if discriminatorVal == nil: - # None of the branches were explicitly selected by the user and no - # value was given to the discrimator. We can assume that it will be - # initialized to zero and this will select a particular branch as - # a result: - let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0) - let matchedBranch = n.pickCaseBranch defaultValue - discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[]) + if discriminator.sym.ast != nil: + # branch is selected by the default field value of discriminator + let discriminatorDefaultVal = discriminator.sym.ast + result.status = initUnknown + result.defaults.add newTree(nkExprColonExpr, n[0], discriminatorDefaultVal) + if discriminatorDefaultVal.kind == nkIntLit: + let matchedBranch = n.pickCaseBranch discriminatorDefaultVal + if matchedBranch != nil: + let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags) + result.defaults.add defaults + collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults) + else: + collectBranchFields(c, n, discriminatorDefaultVal, constrCtx, flags) + else: + # None of the branches were explicitly selected by the user and no + # value was given to the discrimator. We can assume that it will be + # initialized to zero and this will select a particular branch as + # a result: + let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0) + let matchedBranch = n.pickCaseBranch defaultValue + discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[]) else: result.status = initPartial if discriminatorVal.kind == nkIntLit: @@ -339,10 +379,8 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, result.defaults.add defaults collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults) else: - # All bets are off. If any of the branches has a mandatory - # fields we must produce an error: - for i in 1..= 0 and c.locals[s].stride != nil: result = c.locals[s].stride.intVal + else: + result = 0 else: + result = 0 for i in 0.. 0: it = it.lastSon - result = it.kind in {nkBreakStmt, nkReturnStmt, nkRaiseStmt} or - it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags + case it.kind + of nkBreakStmt, nkReturnStmt: + result = bsBreakOrReturn + of nkRaiseStmt: + result = bsNoReturn + of nkCallKinds: + if it[0].kind == nkSym and sfNoReturn in it[0].sym.flags: + result = bsNoReturn + else: + result = bsNone + else: + result = bsNone + +proc addIdToIntersection(tracked: PEffects, inter: var TIntersection, resCounter: var int, + hasBreaksBlock: BreakState, oldState: int, resSym: PSym, hasResult: bool) = + if hasResult: + var alreadySatisfy = false + + if hasBreaksBlock == bsNoReturn: + alreadySatisfy = true + inc resCounter + + for i in oldState..= toCover: tracked.init.add id # else we can't merge @@ -715,12 +768,17 @@ proc trackIf(tracked: PEffects, n: PNode) = addFact(tracked.guards, n[0][0]) let oldState = tracked.init.len + let hasResult = hasResultSym(tracked.owner) + let resSym = if hasResult: tracked.owner.ast[resultPos].sym else: nil + var resCounter = 0 + var inter: TIntersection = @[] var toCover = 0 track(tracked, n[0][1]) - if not breaksBlock(n[0][1]): inc toCover - for i in oldState..= toCover: tracked.init.add id # else we can't merge as it is not exhaustive @@ -844,7 +906,9 @@ proc trackCall(tracked: PEffects; n: PNode) = if n.typ != nil: if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray: createTypeBoundOps(tracked, n.typ, n.info) - if getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) == nil: + + let notConstExpr = getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) == nil + if notConstExpr: if a.kind == nkCast and a[1].typ.kind == tyProc: a = a[1] # XXX: in rare situations, templates and macros will reach here after @@ -903,9 +967,6 @@ proc trackCall(tracked: PEffects; n: PNode) = optStaticBoundsCheck in tracked.currOptions: checkBounds(tracked, n[1], n[2]) - if a.kind != nkSym or a.sym.magic notin {mRunnableExamples, mNBindSym, mExpandToAst, mQuoteAst}: - for i in 0.. 0 and a.sym.name.s[0] == '=' and tracked.owner.kind != skMacro: @@ -932,15 +993,22 @@ proc trackCall(tracked: PEffects; n: PNode) = # consider this case: p(out x, x); we want to remark that 'x' is not # initialized until after the call. Since we do this after we analysed the # call, this is fine. - initVar(tracked, n[i].skipAddr, false) + initVar(tracked, n[i].skipHiddenAddr, false) if strictFuncs in tracked.c.features and not tracked.inEnforcedNoSideEffects and - isDangerousLocation(n[i].skipAddr, tracked.owner): + isDangerousLocation(n[i].skipHiddenAddr, tracked.owner): if sfNoSideEffect in tracked.owner.flags: localError(tracked.config, n[i].info, "cannot pass $1 to `var T` parameter within a strict func" % renderTree(n[i])) tracked.hasSideEffect = true else: discard + if notConstExpr and (a.kind != nkSym or + a.sym.magic notin {mRunnableExamples, mNBindSym, mExpandToAst, mQuoteAst} + ): + # tracked after out analysis + for i in 0.. 0: - s.typ.n[0] = b.typ.n[0] - s.typ.flags = b.typ.flags - else: - localError(c.config, n.info, errNoSymbolToBorrowFromFound) + var (b, state) = searchForBorrowProc(c, c.currentScope.parent, s) + case state + of bsMatch: + # store the alias: + n[bodyPos] = newSymNode(b) + # Carry over the original symbol magic, this is necessary in order to ensure + # the semantic pass is correct + s.magic = b.magic + if b.typ != nil and b.typ.len > 0: + s.typ.n[0] = b.typ.n[0] + s.typ.flags = b.typ.flags + of bsNoDistinct: + localError(c.config, n.info, "borrow proc without distinct type parameter is meaningless") + of bsReturnNotMatch: + localError(c.config, n.info, "borrow from proc return type mismatch: '$1'" % typeToString(b.typ[0])) + of bsGeneric: + localError(c.config, n.info, "borrow with generic parameter is not supported") + of bsNotSupported: + localError(c.config, n.info, "borrow from '$1' is not supported" % $b.name.s) + else: + localError(c.config, n.info, errNoSymbolToBorrowFromFound) proc swapResult(n: PNode, sRes: PSym, dNode: PNode) = ## Swap nodes that are (skResult) symbols to d(estination)Node. @@ -1660,16 +1705,6 @@ proc swapResult(n: PNode, sRes: PSym, dNode: PNode) = n[i] = dNode swapResult(n[i], sRes, dNode) - -proc addThis(c: PContext, n: PNode, t: PType, owner: TSymKind) = - var s = newSym(skResult, getIdent(c.cache, "this"), c.idgen, - getCurrOwner(c), n.info) - s.typ = t - incl(s.flags, sfUsed) - c.p.resultSym = s - n.add newSymNode(c.p.resultSym) - addParamOrResult(c, c.p.resultSym, owner) - proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) = template genResSym(s) = var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen, @@ -1697,6 +1732,7 @@ proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) = proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode = # Mirrored with semVarMacroPragma + result = nil var n = prc[pragmasPos] if n == nil or n.kind == nkEmpty: return for i in 0.. 1 + for i in 1.. 0: # from templates m = m.lastSon - if m.kind in nkLastBlockStmts or - m.kind in nkCallKinds and m[0].kind == nkSym and - sfNoReturn in m[0].sym.flags: + if endsInNoReturn(m): for j in i + 1..= 2 and n[1].kind == nkPragma): let pragmaNode = n[1] @@ -218,15 +200,15 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = var found = false if ni.kind == nkIdent: for a in templatePragmas: - if ni.ident == getIdent(c.c.cache, $a): + if ni.ident.id == ord(a): found = true break if not found: openScope(c) pragmaNode[i] = semTemplBody(c, pragmaNode[i]) closeScope(c) - let ident = getIdentNode(c, n) - if not isTemplParam(c, ident): + let (ident, hasParam) = getIdentReplaceParams(c, n) + if not hasParam: if n.kind != nkSym and not (n.kind == nkIdent and n.ident.id == ord(wUnderscore)): let local = newGenSym(k, ident, c) addPrelimDecl(c.c, local) @@ -235,8 +217,6 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = replaceIdentBySym(c.c, n, newSymNode(local, n.info)) if k == skParam and c.inTemplateHeader > 0: local.flags.incl sfTemplateParam - else: - replaceIdentBySym(c.c, n, ident) proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = incl(s.flags, sfUsed) @@ -292,20 +272,21 @@ proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = result = n checkSonsLen(n, bodyPos + 1, c.c.config) - # routines default to 'inject': - if n.kind notin nkLambdaKinds and symBinding(n[pragmasPos]) == spGenSym: - let ident = getIdentNode(c, n[namePos]) - if not isTemplParam(c, ident): - var s = newGenSym(k, ident, c) - s.ast = n - addPrelimDecl(c.c, s) - styleCheckDef(c.c, n.info, s) - onDef(n.info, s) - n[namePos] = newSymNode(s, n[namePos].info) + if n.kind notin nkLambdaKinds: + # routines default to 'inject': + if symBinding(n[pragmasPos]) == spGenSym: + let (ident, hasParam) = getIdentReplaceParams(c, n[namePos]) + if not hasParam: + var s = newGenSym(k, ident, c) + s.ast = n + addPrelimDecl(c.c, s) + styleCheckDef(c.c, n.info, s) + onDef(n.info, s) + n[namePos] = newSymNode(s, n[namePos].info) + else: + n[namePos] = ident else: - n[namePos] = ident - else: - n[namePos] = semRoutineInTemplName(c, n[namePos]) + n[namePos] = semRoutineInTemplName(c, n[namePos]) # open scope for parameters openScope(c) for i in patternPos..paramsPos-1: @@ -566,6 +547,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = # so we use the generic code for nkDotExpr too let s = qualifiedLookUp(c.c, n, {}) if s != nil: + # mirror the nkIdent case # do not symchoice a quoted template parameter (bug #2390): if s.owner == c.owner and s.kind == skParam and n.kind == nkAccQuoted and n.len == 1: @@ -577,7 +559,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = elif contains(c.toMixin, s.name.id): return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0) else: - return symChoice(c.c, n, s, scOpen, c.noGenSym > 0) + if s.kind in {skType, skVar, skLet, skConst}: + discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule}) + return semTemplSymbol(c.c, n, s, c.noGenSym > 0) if n.kind == nkDotExpr: result = n result[0] = semTemplBody(c, n[0]) @@ -673,6 +657,9 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = # a template's parameters are not gensym'ed even if that was originally the # case as we determine whether it's a template parameter in the template # body by the absence of the sfGenSym flag: + let retType = s.typ[0] + if retType != nil and retType.kind != tyUntyped: + allUntyped = false for i in 1.. 1: n[1].info else: n.info - localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) - result = makeRangeWithStaticExpr(c, e) - if c.inGenericContext > 0: result.flags.incl tfUnresolved + elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc) : + if e.typ.kind == tyStatic: + if e.sym.ast != nil: + return semArrayIndex(c, e.sym.ast) + if e.typ.lastSon.kind != tyGenericParam and not isOrdinalType(e.typ.lastSon): + let info = if n.safeLen > 1: n[1].info else: n.info + localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) + result = makeRangeWithStaticExpr(c, e) + if c.inGenericContext > 0: result.flags.incl tfUnresolved + else: + result = e.typ.skipTypes({tyTypeDesc}) + result.flags.incl tfImplicitStatic elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e): if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})): localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc)) @@ -371,12 +389,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType = elif e.kind == nkIdent: result = e.typ.skipTypes({tyTypeDesc}) else: - let x = semConstExpr(c, e) - if x.kind in {nkIntLit..nkUInt64Lit}: - result = makeRangeType(c, 0, x.intVal-1, n.info, - x.typ.skipTypes({tyTypeDesc})) - else: - result = x.typ.skipTypes({tyTypeDesc}) + result = semArrayIndexConst(c, e, n.info) #localError(c.config, n[1].info, errConstExprExpected) proc semArray(c: PContext, n: PNode, prev: PType): PType = @@ -494,6 +507,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, else: localError(c.config, n[0].info, errInvalidVisibilityX % renderTree(n[0])) else: + result = nil illFormedAst(n, c.config) else: result = newSymG(kind, n, c) @@ -881,6 +895,7 @@ proc tryAddInheritedFields(c: PContext, check: var IntSet, pos: var int, result = true proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType = + result = nil if n.len == 0: return newConstraint(c, tyObject) var check = initIntSet() @@ -1000,10 +1015,11 @@ proc findEnforcedStaticType(t: PType): PType = # This handles types such as `static[T] and Foo`, # which are subset of `static[T]`, hence they could # be treated in the same way + result = nil if t == nil: return nil if t.kind == tyStatic: return t if t.kind == tyAnd: - for s in t.sons: + for s in t: let t = findEnforcedStaticType(s) if t != nil: return t @@ -1099,13 +1115,16 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, let t = c.newTypeWithSons(tyTypeDesc, @[paramType.base]) incl t.flags, tfCheckedForDestructor result = addImplicitGeneric(c, t, paramTypId, info, genericParams, paramName) - + else: + result = nil of tyDistinct: if paramType.len == 1: # disable the bindOnce behavior for the type class result = recurse(paramType.base, true) - + else: + result = nil of tyTuple: + result = nil for i in 0.. 1 and n.sym.ast[2].kind == nkObjectTy: + for p in n.sym.ast[2][^1]: + if p.kind == nkIdentDefs and p[1].typ != nil and p[1].typ.kind == tyGenericInvocation and + p[1][0].kind == nkSym and p[1][0].typ.kind == tyForward: + return true + return false + proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if s.typ == nil: localError(c.config, n.info, "cannot instantiate the '$1' $2" % @@ -1490,20 +1525,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = var t = s.typ.skipTypes({tyAlias}) if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody: t = t.base - result = newOrPrevType(tyGenericInvocation, prev, c) addSonSkipIntLit(result, t, c.idgen) - template addToResult(typ) = + template addToResult(typ, skip) = + if typ.isNil: internalAssert c.config, false rawAddSon(result, typ) - else: addSonSkipIntLit(result, typ, c.idgen) + else: + if skip: + addSonSkipIntLit(result, typ, c.idgen) + else: + rawAddSon(result, makeRangeWithStaticExpr(c, typ.n)) if t.kind == tyForward: for i in 1..= i - 1 and tfImplicitStatic in rType[i - 1].flags and isIntLit(typ): + skip = false + addToResult(typ, skip) if isConcrete: if s.ast == nil and s.typ.kind != tyCompositeTypeClass: # XXX: What kind of error is this? is it still relevant? localError(c.config, n.info, errCannotInstantiateX % s.name.s) result = newOrPrevType(tyError, prev, c) + elif containsGenericInvocationWithForward(n[0]): + c.skipTypes.add n #fixes 1500 else: result = instGenericContainer(c, n.info, result, allowMetaTypes = false) @@ -1566,6 +1611,8 @@ proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType = result.sym = prev.sym if prev.kind != tyGenericBody: assignType(prev, result) + else: + result = nil proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) = if prev != nil: @@ -1628,11 +1675,10 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = pragmas = n[1] inherited = n[2] - result = newOrPrevType(tyUserTypeClass, prev, c) - result.flags.incl tfCheckedForDestructor var owner = getCurrOwner(c) var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType], c.idgen) - result.sons = @[candidateTypeSlot] + result = newOrPrevType(tyUserTypeClass, prev, c, sons = @[candidateTypeSlot]) + result.flags.incl tfCheckedForDestructor result.n = n if inherited.kind != nkEmpty: @@ -1675,6 +1721,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = closeScope(c) proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode = + result = nil for p in pragmas: let key = if p.kind in nkPragmaCallKinds and p.len >= 1: p[0] else: p @@ -1958,6 +2005,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = semTypeExpr(c, n[1], prev) else: if c.inGenericContext > 0 and n.kind == nkCall: + let n = semGenericStmt(c, n) result = makeTypeFromExpr(c, n.copyTree) else: result = semTypeExpr(c, n, prev) @@ -2249,7 +2297,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = of nkIdentDefs: var def = a[^1] let constraint = a[^2] - var typ: PType + var typ: PType = nil if constraint.kind != nkEmpty: typ = semTypeNode(c, constraint, nil) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 7c9bf9039d1e5..4ab0e2de10da9 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -12,8 +12,6 @@ import ast, astalgo, msgs, types, magicsys, semdata, renderer, options, lineinfos, modulegraphs -from concepts import makeTypeDesc - when defined(nimPreviewSlimSystem): import std/assertions @@ -36,6 +34,7 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) = localError(info, errInheritanceOnlyWithNonFinalObjects) proc searchInstTypes*(g: ModuleGraph; key: PType): PType = + result = nil let genericTyp = key[0] if not (genericTyp.kind == tyGenericBody and genericTyp.sym != nil): return @@ -46,12 +45,13 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType = # XXX: This happens for prematurely cached # types such as Channel[empty]. Why? # See the notes for PActor in handleGenericInvocation - return + # if this is return the same type gets cached more than it needs to + continue if not sameFlags(inst, key): continue block matchType: - for j in 1..high(key.sons): + for j in 1.. 0: @@ -384,10 +383,9 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = else: header = instCopyType(cl, t) - result = newType(tyGenericInst, nextTypeId(cl.c.idgen), t[0].owner) + result = newType(tyGenericInst, nextTypeId(cl.c.idgen), t[0].owner, sons = @[header[0]]) result.flags = header.flags # be careful not to propagate unnecessary flags here (don't use rawAddSon) - result.sons = @[header[0]] # ugh need another pass for deeply recursive generic types (e.g. PActor) # we need to add the candidate here, before it's fully instantiated for # recursive instantions: @@ -434,6 +432,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # One step is enough, because the recursive nature of # handleGenericInvocation will handle the alias-to-alias-to-alias case if newbody.isGenericAlias: newbody = newbody.skipGenericAlias + rawAddSon(result, newbody) checkPartialConstructedType(cl.c.config, cl.info, newbody) if not cl.allowMetaTypes: @@ -486,7 +485,7 @@ proc eraseVoidParams*(t: PType) = t[pos] = t[j] t.n[pos] = t.n[j] inc pos - setLen t.sons, pos + newSons t, pos setLen t.n.sons, pos break @@ -590,8 +589,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = # return tyStatic values to let anyone make # use of this knowledge. The patching here # won't be necessary then. - result = newTypeS(tyStatic, cl.c) - result.sons = @[n.typ] + result = newTypeS(tyStatic, cl.c, sons = @[n.typ]) result.n = n else: result = n.typ @@ -683,20 +681,18 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo; owner: PSym): TReplTypeVars = - initIdTable(result.symMap) - initIdTable(result.localCache) - result.typeMap = typeMap - result.info = info - result.c = p - result.owner = owner + result = TReplTypeVars(symMap: initIdTable(), + localCache: initIdTable(), typeMap: typeMap, + info: info, c: p, owner: owner) proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; - owner: PSym, allowMetaTypes = false): PNode = + owner: PSym, allowMetaTypes = false, + fromStaticExpr = false, expectedType: PType = nil): PNode = var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, typeMap, n.info, owner) cl.allowMetaTypes = allowMetaTypes pushInfoContext(p.config, n.info) - result = replaceTypeVarsN(cl, n) + result = replaceTypeVarsN(cl, n, expectedType = expectedType) popInfoContext(p.config) when false: diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 2d91fb2a01fd8..dbf16df3d70d3 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -159,7 +159,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi else: c.hashSym(t.sym) - var symWithFlags: PSym + var symWithFlags: PSym = nil template hasFlag(sym): bool = let ret = {sfAnon, sfGenSym} * sym.flags != {} if ret: symWithFlags = sym @@ -187,7 +187,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi hashType c, t[0], flags, conf of tyRef, tyPtr, tyGenericBody, tyVar: c &= char(t.kind) - if t.sons.len > 0: + if t.len > 0: c.hashType t.lastSon, flags, conf if tfVarIsPtr in t.flags: c &= ".varisptr" of tyFromExpr: @@ -260,6 +260,7 @@ when defined(debugSigHashes): # (select hash from sighashes group by hash having count(*) > 1) order by hash; proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): SigHash = + result = default(SigHash) var c: MD5Context md5Init c hashType c, t, flags+{CoOwnerSig}, conf @@ -269,6 +270,7 @@ proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): typeToString(t), $result) proc hashProc(s: PSym; conf: ConfigRef): SigHash = + result = default(SigHash) var c: MD5Context md5Init c hashType c, s.typ, {CoProc}, conf @@ -289,6 +291,7 @@ proc hashProc(s: PSym; conf: ConfigRef): SigHash = md5Final c, result.MD5Digest proc hashNonProc*(s: PSym): SigHash = + result = default(SigHash) var c: MD5Context md5Init c hashSym(c, s) @@ -305,6 +308,7 @@ proc hashNonProc*(s: PSym): SigHash = md5Final c, result.MD5Digest proc hashOwner*(s: PSym): SigHash = + result = default(SigHash) var c: MD5Context md5Init c var m = s @@ -374,7 +378,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash = ## compute unique digest of the proc/func/method symbols ## recursing into invoked symbols as well assert(sym.kind in skProcKinds, $sym.kind) - + result = default(SigHash) graph.symBodyHashes.withValue(sym.id, value): return value[] diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 5786a320c6d94..3867a67b70474 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -100,25 +100,18 @@ proc markOwnerModuleAsUsed*(c: PContext; s: PSym) template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone proc initCandidateAux(ctx: PContext, - c: var TCandidate, callee: PType) {.inline.} = - c.c = ctx - c.exactMatches = 0 - c.subtypeMatches = 0 - c.convMatches = 0 - c.intConvMatches = 0 - c.genericMatches = 0 - c.state = csEmpty - c.firstMismatch = MismatchInfo() - c.callee = callee - c.call = nil - c.baseTypeMatch = false - c.genericConverter = false - c.inheritancePenalty = 0 - -proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) = - initCandidateAux(ctx, c, callee) - c.calleeSym = nil - initIdTable(c.bindings) + callee: PType): TCandidate {.inline.} = + result = TCandidate(c: ctx, exactMatches: 0, subtypeMatches: 0, + convMatches: 0, intConvMatches: 0, genericMatches: 0, + state: csEmpty, firstMismatch: MismatchInfo(), + callee: callee, call: nil, baseTypeMatch: false, + genericConverter: false, inheritancePenalty: 0 + ) + +proc initCandidate*(ctx: PContext, callee: PType): TCandidate = + result = initCandidateAux(ctx, callee) + result.calleeSym = nil + result.bindings = initIdTable() proc put(c: var TCandidate, key, val: PType) {.inline.} = ## Given: proc foo[T](x: T); foo(4) @@ -134,27 +127,27 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} = echo "binding ", key, " -> ", val idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen)) -proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, +proc initCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1, - diagnosticsEnabled = false) = - initCandidateAux(ctx, c, callee.typ) - c.calleeSym = callee + diagnosticsEnabled = false): TCandidate = + result = initCandidateAux(ctx, callee.typ) + result.calleeSym = callee if callee.kind in skProcKinds and calleeScope == -1: if callee.originatingModule == ctx.module: - c.calleeScope = 2 + result.calleeScope = 2 var owner = callee while true: owner = owner.skipGenericOwner if owner.kind == skModule: break - inc c.calleeScope + inc result.calleeScope else: - c.calleeScope = 1 + result.calleeScope = 1 else: - c.calleeScope = calleeScope - c.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil - c.diagnosticsEnabled = diagnosticsEnabled - c.magic = c.calleeSym.magic - initIdTable(c.bindings) + result.calleeScope = calleeScope + result.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil + result.diagnosticsEnabled = diagnosticsEnabled + result.magic = result.calleeSym.magic + result.bindings = initIdTable() if binding != nil and callee.kind in routineKinds: var typeParams = callee.ast[genericParamsPos] for i in 1..min(typeParams.len, binding.len-1): @@ -166,14 +159,14 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, bound = makeTypeDesc(ctx, bound) else: bound = bound.skipTypes({tyTypeDesc}) - put(c, formalTypeParam, bound) + put(result, formalTypeParam, bound) proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = - initCandidate(ctx, result, callee, binding, calleeScope) + result = initCandidate(ctx, callee, binding, calleeScope) proc newCandidate*(ctx: PContext, callee: PType): TCandidate = - initCandidate(ctx, result, callee) + result = initCandidate(ctx, callee) proc copyCandidate(a: var TCandidate, b: TCandidate) = a.c = b.c @@ -214,8 +207,9 @@ proc sumGeneric(t: PType): int = # count the "genericness" so that Foo[Foo[T]] has the value 3 # and Foo[T] has the value 2 so that we know Foo[Foo[T]] is more # specific than Foo[T]. + result = 0 var t = t - var isvar = 1 + var isvar = 0 while true: case t.kind of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyUncheckedArray, @@ -225,7 +219,7 @@ proc sumGeneric(t: PType): int = inc result of tyOr: var maxBranch = 0 - for branch in t.sons: + for branch in t: let branchSum = sumGeneric(branch) if branchSum > maxBranch: maxBranch = branchSum inc result, maxBranch @@ -251,6 +245,8 @@ proc sumGeneric(t: PType): int = of tyBool, tyChar, tyEnum, tyObject, tyPointer, tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64, tyCompositeTypeClass: + return isvar + 1 + of tyBuiltInTypeClass: return isvar else: return 0 @@ -342,6 +338,7 @@ template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = p result.add argTypeToString(arg, prefer) proc describeArg*(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName): string = + result = "" describeArgImpl(c, n, i, startIdx, prefer) proc describeArgs*(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string = @@ -438,6 +435,8 @@ proc isConvertibleToRange(c: PContext, f, a: PType): bool = # `isIntLit` is correct and should be used above as well, see PR: # https://github.com/nim-lang/Nim/pull/11197 result = isIntLit(a) or a.kind in {tyFloat..tyFloat128} + else: + result = false proc handleFloatRange(f, a: PType): TTypeRelation = if a.kind == f.kind: @@ -454,6 +453,38 @@ proc handleFloatRange(f, a: PType): TTypeRelation = else: result = isIntConv else: result = isNone +proc getObjectTypeOrNil(f: PType): PType = + #[ + Returns a type that is f's effective typeclass. This is usually just one level deeper + in the hierarchy of generality for a type. `object`, `ref object`, `enum` and user defined + tyObjects are common return values. + ]# + if f == nil: return nil + case f.kind: + of tyGenericInvocation, tyCompositeTypeClass, tyAlias: + if f.len <= 0 or f[0] == nil: + result = nil + else: + result = getObjectTypeOrNil(f[0]) + of tyGenericBody, tyGenericInst: + result = getObjectTypeOrNil(f.lastSon) + of tyUserTypeClass: + if f.isResolvedUserTypeClass: + result = f.base # ?? idk if this is right + else: + result = f.lastSon + of tyStatic, tyOwned, tyVar, tyLent, tySink: + result = getObjectTypeOrNil(f.base) + of tyInferred: + # This is not true "After a candidate type is selected" + result = getObjectTypeOrNil(f.base) + of tyTyped, tyUntyped, tyFromExpr: + result = nil + of tyRange: + result = f.lastSon + else: + result = f + proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) = if fGenericOrigin != nil and last.kind == tyGenericInst and last.len-1 == fGenericOrigin.len: @@ -468,7 +499,8 @@ proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int = var depth = 0 var last = a while t != nil and not sameObjectTypes(f, t): - assert t.kind == tyObject + if t.kind != tyObject: # avoid entering generic params etc + return -1 t = t[0] if t == nil: break last = t @@ -504,6 +536,7 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType = else: break if r.kind == tyObject and ptrs <= 1: result = r + else: result = nil proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin: PType): bool = assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody} @@ -526,6 +559,8 @@ proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin genericParamPut(c, last, fGenericOrigin) d = depth result = true + else: + result = false proc minRel(a, b: TTypeRelation): TTypeRelation = if a <= b: result = a @@ -565,7 +600,7 @@ proc inconsistentVarTypes(f, a: PType): bool {.inline.} = proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = ## For example we have: - ## ``` + ## ```nim ## proc myMap[T,S](sIn: seq[T], f: proc(x: T): S): seq[S] = ... ## proc innerProc[Q,W](q: Q): W = ... ## ``` @@ -607,6 +642,8 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = if reverseRel >= isGeneric: result = isInferred #inc c.genericMatches + else: + result = isNone else: # Note that this typeRel call will save f's resolved type into c.bindings # if f is metatype. @@ -654,7 +691,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = of tyNil: result = f.allowsNil - else: discard + else: result = isNone proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = template checkRange[T](a0, a1, f0, f1: T): TTypeRelation = @@ -699,14 +736,14 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = typeClass[0][0] = prevCandidateType closeScope(c) - var typeParams: seq[(PSym, PType)] + var typeParams: seq[(PSym, PType)] = @[] if ff.kind == tyUserTypeClassInst: for i in 1..<(ff.len - 1): var typeParamName = ff.base[i-1].sym.name typ = ff[i] - param: PSym + param: PSym = nil alreadyBound = PType(idTableGet(m.bindings, typ)) if alreadyBound != nil: typ = alreadyBound @@ -746,8 +783,8 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = addDecl(c, param) var - oldWriteHook: typeof(m.c.config.writelnHook) - diagnostics: seq[string] + oldWriteHook = default typeof(m.c.config.writelnHook) + diagnostics: seq[string] = @[] errorPrefix: string flags: TExprFlags = {} collectDiagnostics = m.diagnosticsEnabled or @@ -808,7 +845,8 @@ proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType = result = t proc tryResolvingStaticExpr(c: var TCandidate, n: PNode, - allowUnresolved = false): PNode = + allowUnresolved = false, + expectedType: PType = nil): PNode = # Consider this example: # type Value[N: static[int]] = object # proc foo[N](a: Value[N], r: range[0..(N-1)]) @@ -817,6 +855,8 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode, # This proc is used to evaluate such static expressions. let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil, allowMetaTypes = allowUnresolved) + if instantiated.kind in nkCallKinds: + return nil result = c.c.semExpr(c.c, instantiated) proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool = @@ -888,7 +928,7 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool = else: discard elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil: - var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ.sons) + var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ) inferred.n = newIntNode(nkIntLit, rhs) put(c, lhs.typ, inferred) if c.c.matchedConcept != nil: @@ -919,6 +959,7 @@ proc inferStaticsInRange(c: var TCandidate, else: failureToInferStaticParam(c.c.config, exp) + result = isNone if lowerBound.kind == nkIntLit: if upperBound.kind == nkIntLit: if lengthOrd(c.c.config, concrete) == upperBound.intVal - lowerBound.intVal + 1: @@ -989,8 +1030,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # of the designated type class. # # 3) When used with two type classes, it will check whether the types - # matching the first type class are a strict subset of the types matching - # the other. This allows us to compare the signatures of generic procs in + # matching the first type class (aOrig) are a strict subset of the types matching + # the other (f). This allows us to compare the signatures of generic procs in # order to give preferrence to the most specific one: # # seq[seq[any]] is a strict subset of seq[any] and hence more specific. @@ -1092,7 +1133,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # both int and string must match against number # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219): result = isGeneric - for branch in a.sons: + for branch in a: let x = typeRel(c, f, branch, flags + {trDontBind}) if x == isNone: return isNone if x < result: result = x @@ -1103,7 +1144,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, c.typedescMatched = true # seq[Sortable and Iterable] vs seq[Sortable] # only one match is enough - for branch in a.sons: + for branch in a: let x = typeRel(c, f, branch, flags + {trDontBind}) if x != isNone: return if x >= isGeneric: isGeneric else: x @@ -1138,8 +1179,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let x = typeRel(c, a, f, flags + {trDontBind}) if x >= isGeneric: return isGeneric - else: discard + of tyFromExpr: + if c.c.inGenericContext > 0: + # generic type bodies can sometimes compile call expressions + # prevent expressions with unresolved types from + # being passed as parameters + return isNone + else: discard case f.kind of tyEnum: if a.kind == f.kind and sameEnumTypes(f, a): result = isEqual @@ -1156,9 +1203,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if result notin {isNone, isGeneric}: # resolve any late-bound static expressions # that may appear in the range: + let expectedType = base(f) for i in 0..1: if f.n[i].kind == nkStaticExpr: - f.n[i] = tryResolvingStaticExpr(c, f.n[i]) + let r = tryResolvingStaticExpr(c, f.n[i], expectedType = expectedType) + if r != nil: + f.n[i] = r result = typeRangeRel(f, a) else: let f = skipTypes(f, {tyRange}) @@ -1198,7 +1248,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyArray: var fRange = f[0] var aRange = a[0] - if fRange.kind == tyGenericParam: + if fRange.kind in {tyGenericParam, tyAnything}: var prev = PType(idTableGet(c.bindings, fRange)) if prev == nil: put(c, fRange, a[0]) @@ -1227,6 +1277,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, return inferStaticsInRange(c, fRange, a) elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic: return inferStaticsInRange(c, aRange, f) + elif result == isGeneric and concreteType(c, aa, ff) == nil: + return isNone else: if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange): result = isNone @@ -1314,12 +1366,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyTuple: if a.kind == tyTuple: result = recordRel(c, f, a) of tyObject: - if a.kind == tyObject: - if sameObjectTypes(f, a): + let effectiveArgType = if useTypeLoweringRuleInTypeClass: + a + else: + getObjectTypeOrNil(a) + if effectiveArgType == nil: return isNone + if effectiveArgType.kind == tyObject: + if sameObjectTypes(f, effectiveArgType): result = isEqual # elif tfHasMeta in f.flags: result = recordRel(c, f, a) elif trIsOutParam notin flags: - var depth = isObjectSubtype(c, a, f, nil) + var depth = isObjectSubtype(c, effectiveArgType, f, nil) if depth > 0: inc(c.inheritancePenalty, depth) result = isSubtype @@ -1438,8 +1495,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let origF = f var f = if prev == nil: f else: prev - let roota = a.skipGenericAlias - let rootf = f.skipGenericAlias + let deptha = a.genericAliasDepth() + let depthf = f.genericAliasDepth() + let skipBoth = deptha == depthf and (a.len > 0 and f.len > 0 and a.base != f.base) + + let roota = if skipBoth or deptha > depthf: a.skipGenericAlias else: a + let rootf = if skipBoth or depthf > deptha: f.skipGenericAlias else: f if a.kind == tyGenericInst: if roota.base == rootf.base: @@ -1511,6 +1572,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyGenericInvocation: var x = a.skipGenericAlias + if x.kind == tyGenericParam and x.len > 0: + x = x.lastSon let concpt = f[0].skipTypes({tyGenericBody}) var preventHack = concpt.kind == tyConcept if x.kind == tyOwned and f[0].kind != tyOwned: @@ -1521,12 +1584,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, c.calleeSym != nil and c.calleeSym.kind in {skProc, skFunc} and c.call != nil and not preventHack: let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f) - #echo "inferred ", typeToString(inst), " for ", f return typeRel(c, inst, a, flags) if x.kind == tyGenericInvocation: if f[0] == x[0]: for i in 1..= x.len: return let tr = typeRel(c, f[i], x[i], flags) if tr <= isSubtype: return result = isGeneric @@ -1548,9 +1612,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, var fskip = skippedNone let aobj = x.skipToObject(askip) let fobj = genericBody.lastSon.skipToObject(fskip) - var depth = -1 - if fobj != nil and aobj != nil and askip == fskip: - depth = isObjectSubtype(c, aobj, fobj, f) result = typeRel(c, genericBody, x, flags) if result != isNone: # see tests/generics/tgeneric3.nim for an example that triggers this @@ -1576,7 +1637,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, put(c, key, x) elif typeRel(c, old, x, flags + {trDontBind}) == isNone: return isNone - + var depth = -1 + if fobj != nil and aobj != nil and askip == fskip: + depth = isObjectSubtype(c, aobj, fobj, f) + if result == isNone: # Here object inheriting from generic/specialized generic object # crossing path with metatypes/aliases, so we need to separate them @@ -1593,7 +1657,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyAnd: considerPreviousT: result = isEqual - for branch in f.sons: + for branch in f: let x = typeRel(c, branch, aOrig, flags) if x < isSubtype: return isNone # 'and' implies minimum matching result: @@ -1606,7 +1670,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isNone let oldInheritancePenalty = c.inheritancePenalty var maxInheritance = 0 - for branch in f.sons: + for branch in f: c.inheritancePenalty = 0 let x = typeRel(c, branch, aOrig, flags) maxInheritance = max(maxInheritance, c.inheritancePenalty) @@ -1621,7 +1685,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyNot: considerPreviousT: - for branch in f.sons: + for branch in f: if typeRel(c, branch, aOrig, flags) != isNone: return isNone @@ -1638,8 +1702,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, considerPreviousT: let target = f[0] let targetKind = target.kind - let effectiveArgType = a.skipTypes({tyRange, tyGenericInst, - tyBuiltInTypeClass, tyAlias, tySink, tyOwned}) + var effectiveArgType = a.getObjectTypeOrNil() + if effectiveArgType == nil: return isNone + effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass}) if targetKind == effectiveArgType.kind: if effectiveArgType.isEmptyContainer: return isNone @@ -1761,7 +1826,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if tfWildcard in a.flags: a.sym.transitionGenericParamToType() a.flags.excl tfWildcard - else: + elif doBind: + # The mechanics of `doBind` being a flag that also denotes sig cmp via + # negation is potentially problematic. `IsNone` is appropriate for + # preventing illegal bindings, but it is not necessarily appropriate + # before the bindings have been finalized. concrete = concreteType(c, a, f) if concrete == nil: return isNone @@ -1876,6 +1945,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # fix the expression, so it contains the already instantiated types if f.n == nil or f.n.kind == nkEmpty: return isGeneric let reevaluated = tryResolvingStaticExpr(c, f.n) + if reevaluated == nil: + result = isNone + return case reevaluated.typ.kind of tyTypeDesc: result = typeRel(c, a, reevaluated.typ.base, flags) @@ -1941,7 +2013,12 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv") result.add c.graph.emptyNode - result.add arg + if arg.typ != nil and arg.typ.kind == tyLent: + let a = newNodeIT(nkHiddenDeref, arg.info, arg.typ[0]) + a.add arg + result.add a + else: + result.add arg proc isLValue(c: PContext; n: PNode, isOutParam = false): bool {.inline.} = let aa = isAssignable(nil, n) @@ -2028,7 +2105,7 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, if result != nil: if result.typ == nil: return nil # bug #13378, ensure we produce a real generic instantiation: - result = c.semExpr(c, call) + result = c.semExpr(c, call, {efNoSem2Check}) # resulting type must be consistent with the other arguments: var r = typeRel(m, f[0], result.typ) if r < isGeneric: return nil @@ -2084,8 +2161,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, if evaluated != nil: # Don't build the type in-place because `evaluated` and `arg` may point # to the same object and we'd end up creating recursive types (#9255) - let typ = newTypeS(tyStatic, c) - typ.sons = @[evaluated.typ] + let typ = newTypeS(tyStatic, c, sons = @[evaluated.typ]) typ.n = evaluated arg = copyTree(arg) # fix #12864 arg.typ = typ @@ -2213,6 +2289,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, of isNone: # do not do this in ``typeRel`` as it then can't infer T in ``ref T``: if a.kind in {tyProxy, tyUnknown}: + if a.kind == tyUnknown and c.inGenericContext > 0: + # don't bother with fauxMatch mechanism in generic type, + # reject match, typechecking will be delayed to instantiation + return nil inc(m.genericMatches) m.fauxMatch = a.kind return arg @@ -2414,7 +2494,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int return template checkConstraint(n: untyped) {.dirty.} = - if not formal.constraint.isNil: + if not formal.constraint.isNil and sfCodegenDecl notin formal.flags: if matchNodeKinds(formal.constraint, n): # better match over other routines with no such restriction: inc(m.genericMatches, 100) @@ -2440,7 +2520,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int a = 1 # iterates over the actual given arguments f = if m.callee.kind != tyGenericBody: 1 else: 0 # iterates over formal parameters - arg: PNode # current prepared argument + arg: PNode = nil # current prepared argument formalLen = m.callee.n.len formal = if formalLen > 1: m.callee.n[1].sym else: nil # current routine parameter container: PNode = nil # constructed container @@ -2673,7 +2753,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = # forget all inferred types if the overload matching failed if m.state == csNoMatch: for t in m.inferredTypes: - if t.len > 1: t.sons.setLen 1 + if t.len > 1: t.newSons 1 proc argtypeMatches*(c: PContext, f, a: PType, fromHlo = false): bool = var m = newCandidate(c, f) @@ -2701,6 +2781,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; else: if f.kind in {tyVar}: f = f.lastSon if typeRel(m, f, t) == isNone: + result = nil localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") else: result = c.semGenerateInstance(c, dc, m.bindings, info) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index e07d55fbcd739..424d7450f8112 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -433,10 +433,9 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.paddingAtEnd = typ.base.paddingAtEnd of tyForward: - # is this really illegal recursion, or maybe just unknown? - typ.size = szIllegalRecursion - typ.align = szIllegalRecursion - typ.paddingAtEnd = szIllegalRecursion + typ.size = szUnknownSize + typ.align = szUnknownSize + typ.paddingAtEnd = szUnknownSize of tyStatic: if typ.n != nil: @@ -503,6 +502,7 @@ template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode = elif node[1].kind == nkCheckedFieldExpr: dotExpr = node[1][0] else: + dotExpr = nil localError(config, node.info, "can't compute offsetof on this ast") assert dotExpr != nil diff --git a/compiler/sourcemap.nim b/compiler/sourcemap.nim index 2fcc50bbe9846..b0b6fea2ef0ea 100644 --- a/compiler/sourcemap.nim +++ b/compiler/sourcemap.nim @@ -70,6 +70,7 @@ func encode*(values: seq[int]): string {.raises: [].} = shift = 5 continueBit = 1 shl 5 mask = continueBit - 1 + result = "" for val in values: # Sign is stored in first bit var newVal = abs(val) shl 1 @@ -101,8 +102,8 @@ iterator tokenize*(line: string): (int, string) = token = "" while col < line.len: var - token: string - name: string + token: string = "" + name: string = "" # First we find the next identifier col += line.skipWhitespace(col) col += line.skipUntil(IdentStartChars, col) @@ -110,7 +111,7 @@ iterator tokenize*(line: string): (int, string) = col += line.parseIdent(token, col) # Idents will either be originalName_randomInt or HEXhexCode_randomInt if token.startsWith("HEX"): - var hex: int + var hex: int = 0 # 3 = "HEX".len and we only want to parse the two integers after it discard token[3 ..< 5].parseHex(hex) name = $chr(hex) @@ -125,6 +126,7 @@ iterator tokenize*(line: string): (int, string) = func parse*(source: string): SourceInfo = ## Parses the JS output for embedded line info ## So it can convert those into a series of mappings + result = default(SourceInfo) var skipFirstLine = true currColumn = 0 @@ -133,9 +135,9 @@ func parse*(source: string): SourceInfo = # Add each line as a node into the output for line in source.splitLines(): var - lineNumber: int - linePath: string - column: int + lineNumber: int = 0 + linePath: string = "" + column: int = 0 if line.strip().scanf("/* line $i:$i \"$+\" */", lineNumber, column, linePath): # When we reach the first line mappinsegmentg then we can assume # we can map the rest of the JS lines to Nim lines diff --git a/compiler/spawn.nim b/compiler/spawn.nim index 7423fdfaa46d9..e6c089966f496 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -114,7 +114,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; idgen: IdGenerator; spawnKind: TSpawnResult, result: PSym) = var body = newNodeI(nkStmtList, f.info) - var threadLocalBarrier: PSym + var threadLocalBarrier: PSym = nil if barrier != nil: var varSection2 = newNodeI(nkVarSection, barrier.info) threadLocalBarrier = addLocalVar(g, varSection2, nil, idgen, result, @@ -122,7 +122,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym; body.add varSection2 body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info, threadLocalBarrier.newSymNode) - var threadLocalProm: PSym + var threadLocalProm: PSym = nil if spawnKind == srByVar: threadLocalProm = addLocalVar(g, varSection, nil, idgen, result, fv.typ, fv) elif fv != nil: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index f41f35519aa0f..5554991bd7369 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -55,6 +55,8 @@ proc findDocComment(n: PNode): PNode = result = findDocComment(n[1]) elif n.kind in {nkAsgn, nkFastAsgn, nkSinkAsgn} and n.len == 2: result = findDocComment(n[1]) + else: + result = nil proc extractDocComment(g: ModuleGraph; s: PSym): string = var n = findDocComment(s.ast) @@ -106,7 +108,7 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int if cmpIgnoreStyle(line[column..column + result - 1], ident) != 0: result = 0 else: - var sourceIdent: string + var sourceIdent: string = "" result = parseWhile(line, sourceIdent, OpChars + {'[', '(', '{', ']', ')', '}'}, column) if ident[^1] == '=' and ident[0] in linter.Letters: @@ -254,13 +256,17 @@ proc filterSym(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline.} = of nkOpenSymChoice, nkClosedSymChoice, nkAccQuoted: if n.len > 0: result = prefixMatch(s, n[0]) - else: discard + else: + result = default(PrefixMatch) + else: result = default(PrefixMatch) if s.kind != skModule: if prefix != nil: res = prefixMatch(s, prefix) result = res != PrefixMatch.None else: result = true + else: + result = false proc filterSymNoOpr(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline.} = result = filterSym(s, prefix, res) and s.name.s[0] in lexer.SymChars and @@ -274,10 +280,8 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = for module in c.friendModules: if fmoduleId == module.id: return true if f.kind == skField: - var symObj = f.owner - if symObj.typ.skipTypes({tyGenericBody, tyGenericInst, tyGenericInvocation, tyAlias}).kind in {tyRef, tyPtr}: - symObj = symObj.typ.toObjectFromRefPtrGeneric.sym - assert symObj != nil + var symObj = f.owner.typ.toObjectFromRefPtrGeneric.sym + assert symObj != nil for scope in allScopes(c.currentScope): for sym in scope.allowPrivateAccess: if symObj.id == sym.id: return true @@ -294,7 +298,7 @@ proc getQuality(s: PSym): range[0..100] = result = result - 5 proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) = - var pm: PrefixMatch + var pm: PrefixMatch = default(PrefixMatch) if filterSym(s, f, pm) and fieldVisible(c, s): outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, info, s.getQuality, pm, c.inTypeContext > 0, 0)) @@ -302,7 +306,7 @@ proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var template wholeSymTab(cond, section: untyped) {.dirty.} = for (item, scopeN, isLocal) in uniqueSyms(c): let it = item - var pm: PrefixMatch + var pm: PrefixMatch = default(PrefixMatch) if cond: outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, section, info, getQuality(it), pm, c.inTypeContext > 0, scopeN)) @@ -365,6 +369,8 @@ proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = if exp.kind == tyVarargs: exp = elemType(exp) if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: return result = sigmatch.argtypeMatches(c, s.typ[1], firstArg) + else: + result = false proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Suggestions) = assert typ != nil @@ -374,7 +380,7 @@ proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Sugges proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) = # do not produce too many symbols: for (it, scopeN, isLocal) in uniqueSyms(c): - var pm: PrefixMatch + var pm: PrefixMatch = default(PrefixMatch) if filterSym(it, f, pm): outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, n.info, it.getQuality, pm, c.inTypeContext > 0, scopeN)) @@ -383,7 +389,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but # ``myObj``. var typ = n.typ - var pm: PrefixMatch + var pm: PrefixMatch = default(PrefixMatch) when defined(nimsuggest): if n.kind == nkSym and n.sym.kind == skError and c.config.suggestVersion == 0: # consider 'foo.|' where 'foo' is some not imported module. @@ -440,7 +446,15 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) if t[0] == nil: break t = skipTypes(t[0], skipPtrs) elif typ.kind == tyTuple and typ.n != nil: - suggestSymList(c, typ.n, field, n.info, outputs) + # All tuple fields are in scope + # So go through each field and add it to the suggestions (If it passes the filter) + for node in typ.n: + if node.kind == nkSym: + let s = node.sym + var pm: PrefixMatch = default(PrefixMatch) + if filterSym(s, field, pm): + outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, n.info, + s.getQuality, pm, c.inTypeContext > 0, 0)) suggestOperations(c, n, field, orig, outputs) if typ != orig: @@ -452,17 +466,24 @@ type proc inCheckpoint*(current, trackPos: TLineInfo): TCheckPointResult = if current.fileIndex == trackPos.fileIndex: + result = cpNone if current.line == trackPos.line and abs(current.col-trackPos.col) < 4: return cpExact if current.line >= trackPos.line: return cpFuzzy + else: + result = cpNone proc isTracked*(current, trackPos: TLineInfo, tokenLen: int): bool = if current.fileIndex==trackPos.fileIndex and current.line==trackPos.line: let col = trackPos.col if col >= current.col and col <= current.col+tokenLen-1: - return true + result = true + else: + result = false + else: + result = false when defined(nimsuggest): # Since TLineInfo defined a == operator that doesn't include the column, @@ -692,7 +713,7 @@ proc suggestSentinel*(c: PContext) = var outputs: Suggestions = @[] # suggest everything: for (it, scopeN, isLocal) in uniqueSyms(c): - var pm: PrefixMatch + var pm: PrefixMatch = default(PrefixMatch) if filterSymNoOpr(it, nil, pm): outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), it.getQuality, diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index c00fe8b6771b8..1c8acf2a6420c 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -36,6 +36,8 @@ proc containsShebang(s: string, i: int): bool = var j = i + 2 while j < s.len and s[j] in Whitespace: inc(j) result = s[j] == '/' + else: + result = false proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache; config: ConfigRef): PNode = @@ -64,6 +66,7 @@ proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache llStreamClose(s) proc getFilter(ident: PIdent): FilterKind = + result = filtNone for i in FilterKind: if cmpIgnoreStyle(ident.s, $i) == 0: return i @@ -74,6 +77,7 @@ proc getCallee(conf: ConfigRef; n: PNode): PIdent = elif n.kind == nkIdent: result = n.ident else: + result = nil localError(conf, n.info, "invalid filter: " & renderTree(n)) proc applyFilter(p: var Parser, n: PNode, filename: AbsoluteFile, @@ -124,7 +128,7 @@ proc openParser*(p: var Parser, fileIdx: FileIndex, inputstream: PLLStream; proc setupParser*(p: var Parser; fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): bool = let filename = toFullPathConsiderDirty(config, fileIdx) - var f: File + var f: File = default(File) if not open(f, filename.string): rawMessage(config, errGenerated, "cannot open file: " & filename.string) return false @@ -132,7 +136,9 @@ proc setupParser*(p: var Parser; fileIdx: FileIndex; cache: IdentCache; result = true proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode = - var p: Parser + var p: Parser = default(Parser) if setupParser(p, fileIdx, cache, config): result = parseAll(p) closeParser(p) + else: + result = nil diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim index 83c891ca8b715..9ee8516c47398 100644 --- a/compiler/tccgen.nim +++ b/compiler/tccgen.nim @@ -14,7 +14,7 @@ const tinyPrefix = "dist/nim-tinyc-archive".unixToNativePath const nimRoot = currentSourcePath.parentDir.parentDir const tinycRoot = nimRoot / tinyPrefix when not dirExists(tinycRoot): - static: doAssert false, $(tinycRoot, "requires: ./koch installdeps tinyc") + static: raiseAssert $(tinycRoot, "requires: ./koch installdeps tinyc") {.compile: tinycRoot / "tinyc/libtcc.c".} var diff --git a/compiler/transf.nim b/compiler/transf.nim index 24363cad0be73..65b4c6c3bd00f 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -28,10 +28,11 @@ when defined(nimPreviewSlimSystem): import std/assertions type - TransformBodyFlag* = enum - dontUseCache, useCache + TransformFlag* = enum + useCache, keepOpenArrayConversions, force + TransformFlags* = set[TransformFlag] -proc transformBody*(g: ModuleGraph; idgen: IdGenerator, prc: PSym, flag: TransformBodyFlag, force = false): PNode +proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode import closureiters, lambdalifting @@ -50,9 +51,10 @@ type module: PSym transCon: PTransCon # top of a TransCon stack inlining: int # > 0 if we are in inlining context (copy vars) - isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields) contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' deferDetected, tooEarly: bool + isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields) + flags: TransformFlags graph: ModuleGraph idgen: IdGenerator @@ -74,9 +76,7 @@ proc newTransNode(kind: TNodeKind, n: PNode, proc newTransCon(owner: PSym): PTransCon = assert owner != nil - new(result) - initIdNodeTable(result.mapping) - result.owner = owner + result = PTransCon(mapping: initIdNodeTable(), owner: owner) proc pushTransCon(c: PTransf, t: PTransCon) = t.next = c.transCon @@ -118,7 +118,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = let s = n.sym if s.typ != nil and s.typ.callConv == ccClosure: if s.kind in routineKinds: - discard transformBody(c.graph, c.idgen, s, useCache) + discard transformBody(c.graph, c.idgen, s, {useCache}+c.flags) if s.kind == skIterator: if c.tooEarly: return n else: return liftIterSym(c.graph, n, c.idgen, getCurrOwner(c)) @@ -242,9 +242,10 @@ proc transformConstSection(c: PTransf, v: PNode): PNode = proc hasContinue(n: PNode): bool = case n.kind - of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard + of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: result = false of nkContinueStmt: result = true else: + result = false for i in 0.. 0: n[0] else: n case key.kind of nkIdent: result = whichKeyword(key.ident) of nkSym: result = whichKeyword(key.sym.name) - of nkCast: result = wCast + of nkCast: return wCast of nkClosedSymChoice, nkOpenSymChoice: - result = whichPragma(key[0]) - else: result = wInvalid + return whichPragma(key[0]) + else: return wInvalid + if result in nonPragmaWordsLow..nonPragmaWordsHigh: + result = wInvalid proc isNoSideEffectPragma*(n: PNode): bool = var k = whichPragma(n) @@ -145,12 +158,14 @@ proc isNoSideEffectPragma*(n: PNode): bool = result = k == wNoSideEffect proc findPragma*(n: PNode, which: TSpecialWord): PNode = + result = nil if n.kind == nkPragma: for son in n: if whichPragma(son) == which: return son proc effectSpec*(n: PNode, effectType: TSpecialWord): PNode = + result = nil for i in 0.. 0: result.add ": " var first = true - for son in t.sons: + for son in t: if not first: result.add " or " result.add son.typeToString first = false @@ -635,14 +637,14 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result.add(typeToString(t[i])) result.add "]" of tyAnd: - for i, son in t.sons: + for i, son in t: result.add(typeToString(son)) - if i < t.sons.high: + if i < t.len - 1: result.add(" and ") of tyOr: - for i, son in t.sons: + for i, son in t: result.add(typeToString(son)) - if i < t.sons.high: + if i < t.len - 1: result.add(" or ") of tyNot: result = "not " & typeToString(t[0]) @@ -786,7 +788,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 = of 4: result = toInt128(-2147483648) of 2: result = toInt128(-32768) of 1: result = toInt128(-128) - else: discard + else: result = Zero else: result = toInt128(0x8000000000000000'i64) of tyInt8: result = toInt128(-128) @@ -802,17 +804,21 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 = if t.n.len > 0: assert(t.n[0].kind == nkSym) result = toInt128(t.n[0].sym.position) + else: + result = Zero of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, tyStatic, tyInferred, tyUserTypeClasses, tyLent: result = firstOrd(conf, lastSon(t)) of tyOrdinal: if t.len > 0: result = firstOrd(conf, lastSon(t)) - else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') + else: + result = Zero + internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') of tyUncheckedArray, tyCstring: result = Zero else: - internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') result = Zero + internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') proc firstFloat*(t: PType): BiggestFloat = case t.kind @@ -834,14 +840,14 @@ proc targetSizeSignedToKind*(conf: ConfigRef): TTypeKind = of 8: result = tyInt64 of 4: result = tyInt32 of 2: result = tyInt16 - else: discard + else: result = tyNone proc targetSizeUnsignedToKind*(conf: ConfigRef): TTypeKind = case conf.target.intSize of 8: result = tyUInt64 of 4: result = tyUInt32 of 2: result = tyUInt16 - else: discard + else: result = tyNone proc normalizeKind*(conf: ConfigRef, k: TTypeKind): TTypeKind = case k @@ -869,7 +875,7 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 = of 4: result = toInt128(0x7FFFFFFF) of 2: result = toInt128(0x00007FFF) of 1: result = toInt128(0x0000007F) - else: discard + else: result = Zero else: result = toInt128(0x7FFFFFFFFFFFFFFF'u64) of tyInt8: result = toInt128(0x0000007F) of tyInt16: result = toInt128(0x00007FFF) @@ -889,18 +895,22 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 = if t.n.len > 0: assert(t.n[^1].kind == nkSym) result = toInt128(t.n[^1].sym.position) + else: + result = Zero of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, tyStatic, tyInferred, tyUserTypeClasses, tyLent: result = lastOrd(conf, lastSon(t)) of tyProxy: result = Zero of tyOrdinal: if t.len > 0: result = lastOrd(conf, lastSon(t)) - else: internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') + else: + result = Zero + internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') of tyUncheckedArray: result = Zero else: - internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') result = Zero + internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')') proc lastFloat*(t: PType): BiggestFloat = case t.kind @@ -972,7 +982,7 @@ type proc initSameTypeClosure: TSameTypeClosure = # we do the initialization lazily for performance (avoids memory allocations) - discard + result = TSameTypeClosure() proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool = result = c.s.len > 0 and c.s.contains((a.id, b.id)) @@ -1011,6 +1021,8 @@ proc equalParam(a, b: PSym): TParamsEquality = result = paramsEqual elif b.ast != nil: result = paramsIncompatible + else: + result = paramsNotEqual else: result = paramsNotEqual @@ -1078,6 +1090,8 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool = return false elif a.n != b.n and (a.n == nil or b.n == nil) and IgnoreTupleFields notin c.flags: result = false + else: + result = false template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) = if tfFromGeneric notin a.flags + b.flags: @@ -1097,6 +1111,8 @@ template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) = if tfFromGeneric in a.flags * b.flags and a.sym.id == b.sym.id: # ok, we need the expensive structural check body + else: + result = false proc sameObjectTypes*(a, b: PType): bool = # specialized for efficiency (sigmatch uses it) @@ -1134,6 +1150,12 @@ proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool = for i in 0.. 1: # we know the result is derived from the first argument: - var roots: seq[(PSym, int)] + var roots: seq[(PSym, int)] = @[] allRoots(n[1], roots, RootEscapes) for r in roots: connect(c, dest.sym, r[0], n[1].info) @@ -618,7 +620,8 @@ proc deps(c: var Partitions; dest, src: PNode) = if borrowChecking in c.goals: borrowingAsgn(c, dest, src) - var targets, sources: seq[(PSym, int)] + var targets: seq[(PSym, int)] = @[] + var sources: seq[(PSym, int)] = @[] allRoots(dest, targets, 0) allRoots(src, sources, 0) @@ -668,7 +671,7 @@ proc potentialMutationViaArg(c: var Partitions; n: PNode; callee: PType) = if constParameters in c.goals and tfNoSideEffect in callee.flags: discard "we know there are no hidden mutations through an immutable parameter" elif c.inNoSideEffectSection == 0 and containsPointer(n.typ): - var roots: seq[(PSym, int)] + var roots: seq[(PSym, int)] = @[] allRoots(n, roots, RootEscapes) for r in roots: potentialMutation(c, r[0], r[1], n.info) @@ -707,12 +710,16 @@ proc traverse(c: var Partitions; n: PNode) = let L = if parameters != nil: parameters.len else: 0 let m = getMagic(n) + if m == mEnsureMove and n[1].kind == nkSym: + # we know that it must be moved so it cannot be a cursor + noCursor(c, n[1].sym) + for i in 1.. 0 and child[0].kind == nkSym: # bug #22787 + let vid = variableId(c, child[0].sym) + if child[^1].kind != nkEmpty: + markAsReassigned(c, vid) of nkAsgn, nkFastAsgn, nkSinkAsgn: computeLiveRanges(c, n[0]) computeLiveRanges(c, n[1]) @@ -824,6 +835,10 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = if vid >= 0: if n[1].kind == nkSym and (c.s[vid].reassignedTo == 0 or c.s[vid].reassignedTo == n[1].sym.id): c.s[vid].reassignedTo = n[1].sym.id + if c.inConditional > 0 and c.inLoop > 0: + # bug #22200: live ranges with loops and conditionals are too + # complex for our current analysis, so we prevent the cursorfication. + c.s[vid].flags.incl isConditionallyReassigned else: markAsReassigned(c, vid) diff --git a/compiler/vm.nim b/compiler/vm.nim index 8b5faabfe6172..fd03c4baeb0b8 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -381,6 +381,7 @@ proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int = return pc + 1 proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool = + result = false if desttyp.kind == tyString: dest.ensureKind(rkNode) dest.node = newNode(nkStrLit) @@ -443,12 +444,16 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): of tyFloat..tyFloat64: dest.intVal = int(src.floatVal) else: - let srcSize = getSize(c.config, styp) let destSize = getSize(c.config, desttyp) - let srcDist = (sizeof(src.intVal) - srcSize) * 8 let destDist = (sizeof(dest.intVal) - destSize) * 8 var value = cast[BiggestUInt](src.intVal) - value = (value shl srcDist) shr srcDist + when false: + # this would make uint64(-5'i8) evaluate to 251 + # but at runtime, uint64(-5'i8) is 18446744073709551611 + # so don't do it + let srcSize = getSize(c.config, styp) + let srcDist = (sizeof(src.intVal) - srcSize) * 8 + value = (value shl srcDist) shr srcDist value = (value shl destDist) shr destDist dest.intVal = cast[BiggestInt](value) of tyBool: @@ -544,11 +549,12 @@ proc takeCharAddress(c: PCtx, src: PNode, index: BiggestInt, pc: int): TFullReg proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = + result = TFullReg(kind: rkNone) var pc = start var tos = tos # Used to keep track of where the execution is resumed. var savedPC = -1 - var savedFrame: PStackFrame + var savedFrame: PStackFrame = nil when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): template updateRegsAlias = discard template regs: untyped = tos.slots @@ -1011,7 +1017,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcLenCstring: decodeBImm(rkInt) assert regs[rb].kind == rkNode - regs[ra].intVal = regs[rb].node.strVal.cstring.len - imm + if regs[rb].node.kind == nkNilLit: + regs[ra].intVal = -imm + else: + regs[ra].intVal = regs[rb].node.strVal.cstring.len - imm of opcIncl: decodeB(rkNode) let b = regs[rb].regToNode @@ -1209,6 +1218,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcEqStr: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].node.strVal == regs[rc].node.strVal) + of opcEqCString: + decodeBC(rkInt) + let bNil = regs[rb].node.kind == nkNilLit + let cNil = regs[rc].node.kind == nkNilLit + regs[ra].intVal = ord((bNil and cNil) or + (not bNil and not cNil and regs[rb].node.strVal == regs[rc].node.strVal)) of opcLeStr: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].node.strVal <= regs[rc].node.strVal) @@ -1280,7 +1295,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ast = a.sym.ast.shallowCopy for i in 0.. PNode @@ -43,7 +43,7 @@ proc toLit*[T](a: T): PNode = reti.add ai.toLit result.add reti else: - static: doAssert false, "not yet supported: " & $T # add as needed + static: raiseAssert "not yet supported: " & $T # add as needed proc toTimeLit*(a: Time, c: PCtx, obj: PNode, info: TLineInfo): PNode = # probably refactor it into `toLit` in the future diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index f369908ba4ffd..bdb0aeed1551e 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -99,7 +99,7 @@ type opcLeFloat, opcLtFloat, opcLeu, opcLtu, opcEqRef, opcEqNimNode, opcSameNodeType, opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt, - opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet, + opcEqStr, opcEqCString, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet, opcMulSet, opcPlusSet, opcMinusSet, opcConcatStr, opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, opcIsNil, opcOf, opcIs, diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 75692fcc0eff7..a47d034c7a46b 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -133,6 +133,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; if inst: if allowRecursion: result = mapTypeToAstR(t.lastSon, info) + # keep original type info for getType calls on the output node: + result.typ = t else: result = newNodeX(nkBracketExpr) #result.add mapTypeToAst(t.lastSon, info) @@ -141,6 +143,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; result.add mapTypeToAst(t[i], info) else: result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion) + # keep original type info for getType calls on the output node: + result.typ = t of tyGenericBody: if inst: result = mapTypeToAstR(t.lastSon, info) @@ -199,7 +203,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; # only named tuples have a node, unnamed tuples don't if t.n.isNil: result = newNodeX(nkTupleConstr) - for subType in t.sons: + for subType in t: result.add mapTypeToAst(subType, info) else: result = newNodeX(nkTupleTy) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 067965469ca13..efcd0ec35b04b 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -307,6 +307,8 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister = #internalAssert c.config, tmp >= 0 # 'nim check' does not like this internalAssert. if tmp >= 0: result = TRegister(tmp) + else: + result = 0 proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} = # stmt is different from 'void' in meta programming contexts. @@ -319,10 +321,6 @@ proc isNotOpr(n: PNode): bool = n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mNot -proc isTrue(n: PNode): bool = - n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or - n.kind == nkIntLit and n.intVal != 0 - proc genWhile(c: PCtx; n: PNode) = # lab1: # cond, tmp @@ -364,7 +362,7 @@ proc genBlock(c: PCtx; n: PNode; dest: var TDest) = slotTempFloat, slotTempStr, slotTempComplex}: - doAssert false, "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind + raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind c.prc.regInfo[i] = (inUse: false, kind: slotEmpty) c.clearDest(n, dest) @@ -406,13 +404,19 @@ proc genIf(c: PCtx, n: PNode; dest: var TDest) = c.gen(it[0], tmp) elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false c.clearDest(n, dest) - c.gen(it[1], dest) # then part + if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `dest` + c.gen(it[1]) + else: + c.gen(it[1], dest) # then part if i < n.len-1: endings.add(c.xjmp(it[1], opcJmp, 0)) c.patch(elsePos) else: c.clearDest(n, dest) - c.gen(it[0], dest) + if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `dest` + c.gen(it[0]) + else: + c.gen(it[0], dest) for endPos in endings: c.patch(endPos) c.clearDest(n, dest) @@ -506,17 +510,25 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) = let it = n[i] if it.len == 1: # else stmt: - if it[0].kind != nkNilLit or it[0].typ != nil: + let body = it[0] + if body.kind != nkNilLit or body.typ != nil: # an nkNilLit with nil for typ implies there is no else branch, this # avoids unused related errors as we've already consumed the dest - c.gen(it[0], dest) + if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest` + c.gen(body) + else: + c.gen(body, dest) else: let b = rawGenLiteral(c, it) c.gABx(it, opcBranch, tmp, b) - let elsePos = c.xjmp(it.lastSon, opcFJmp, tmp) - c.gen(it.lastSon, dest) + let body = it.lastSon + let elsePos = c.xjmp(body, opcFJmp, tmp) + if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest` + c.gen(body) + else: + c.gen(body, dest) if i < n.len-1: - endings.add(c.xjmp(it.lastSon, opcJmp, 0)) + endings.add(c.xjmp(body, opcJmp, 0)) c.patch(elsePos) c.clearDest(n, dest) for endPos in endings: c.patch(endPos) @@ -532,7 +544,10 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ) var endings: seq[TPosition] = @[] let ehPos = c.xjmp(n, opcTry, 0) - c.gen(n[0], dest) + if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `dest` + c.gen(n[0]) + else: + c.gen(n[0], dest) c.clearDest(n, dest) # Add a jump past the exception handling code let jumpToFinally = c.xjmp(n, opcJmp, 0) @@ -550,7 +565,11 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) = if it.len == 1: # general except section: c.gABx(it, opcExcept, 0, 0) - c.gen(it.lastSon, dest) + let body = it.lastSon + if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest` + c.gen(body) + else: + c.gen(body, dest) c.clearDest(n, dest) if i < n.len: endings.add(c.xjmp(it, opcJmp, 0)) @@ -830,10 +849,14 @@ proc genVarargsABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = proc isInt8Lit(n: PNode): bool = if n.kind in {nkCharLit..nkUInt64Lit}: result = n.intVal >= low(int8) and n.intVal <= high(int8) + else: + result = false proc isInt16Lit(n: PNode): bool = if n.kind in {nkCharLit..nkUInt64Lit}: result = n.intVal >= low(int16) and n.intVal <= high(int16) + else: + result = false proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = if n[2].isInt8Lit: @@ -854,7 +877,9 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = # xxx consider whether to use t2 and targ2 here if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc: # don't do anything for lambda lifting conversions: - return true + result = true + else: + result = false if implicitConv(): gen(c, arg, dest) @@ -875,13 +900,15 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) = c.freeTemp(tmp) proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = - const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar} + const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar, tyEnum, tyBool} var signedIntegers = {tyInt..tyInt64} - var unsignedIntegers = {tyUInt..tyUInt64, tyChar} + var unsignedIntegers = {tyUInt..tyUInt64, tyChar, tyEnum, tyBool} let src = n[1].typ.skipTypes(abstractRange)#.kind let dst = n[0].typ.skipTypes(abstractRange)#.kind let srcSize = getSize(c.config, src) let dstSize = getSize(c.config, dst) + const unsupportedCastDifferentSize = + "VM does not support 'cast' from $1 with size $2 to $3 with size $4 due to different sizes" if src.kind in allowedIntegers and dst.kind in allowedIntegers: let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n[0].typ) @@ -898,8 +925,11 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = # is smaller than source. c.gABC(n, opcNarrowU, dest, TRegister(dstSize*8)) c.freeTemp(tmp) - elif srcSize == dstSize and src.kind in allowedIntegers and - dst.kind in {tyFloat, tyFloat32, tyFloat64}: + elif src.kind in allowedIntegers and + dst.kind in {tyFloat, tyFloat32, tyFloat64}: + if srcSize != dstSize: + globalError(c.config, n.info, unsupportedCastDifferentSize % + [$src.kind, $srcSize, $dst.kind, $dstSize]) let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n[0].typ) if dst.kind == tyFloat32: @@ -908,8 +938,11 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = c.gABC(n, opcCastIntToFloat64, dest, tmp) c.freeTemp(tmp) - elif srcSize == dstSize and src.kind in {tyFloat, tyFloat32, tyFloat64} and + elif src.kind in {tyFloat, tyFloat32, tyFloat64} and dst.kind in allowedIntegers: + if srcSize != dstSize: + globalError(c.config, n.info, unsupportedCastDifferentSize % + [$src.kind, $srcSize, $dst.kind, $dstSize]) let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n[0].typ) if src.kind == tyFloat32: @@ -1065,7 +1098,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = case n[1].typ.skipTypes(abstractVarRange).kind of tyString: genUnaryABI(c, n, dest, opcLenStr) of tyCstring: genUnaryABI(c, n, dest, opcLenCstring) - else: doAssert false, $n[1].typ.kind + else: raiseAssert $n[1].typ.kind of mSlice: var d = c.genx(n[1]) @@ -1151,7 +1184,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.gABC(n, opcNarrowU, dest, TRegister(size*8)) of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: genConv(c, n, n[1], dest) - of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) + of mEqStr: genBinaryABC(c, n, dest, opcEqStr) + of mEqCString: genBinaryABC(c, n, dest, opcEqCString) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) of mLtStr: genBinaryABC(c, n, dest, opcLtStr) of mEqSet: genBinarySet(c, n, dest, opcEqSet) @@ -1390,6 +1424,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mRunnableExamples: discard "just ignore any call to runnableExamples" of mDestroy, mTrace: discard "ignore calls to the default destructor" + of mEnsureMove: + gen(c, n[1], dest) of mMove: let arg = n[1] let a = c.genx(arg) @@ -1417,6 +1453,7 @@ proc unneededIndirection(n: PNode): bool = n.typ.skipTypes(abstractInstOwned-{tyTypeDesc}).kind == tyRef proc canElimAddr(n: PNode; idgen: IdGenerator): PNode = + result = nil case n[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: var m = n[0][0] @@ -1498,6 +1535,7 @@ proc cannotEval(c: PCtx; n: PNode) {.noinline.} = n.renderTree) proc isOwnedBy(a, b: PSym): bool = + result = false var a = a.owner while a != nil and a.kind != skModule: if a == b: return true @@ -1510,7 +1548,9 @@ proc getOwner(c: PCtx): PSym = proc importcCondVar*(s: PSym): bool {.inline.} = # see also importcCond if sfImportc in s.flags: - return s.kind in {skVar, skLet, skConst} + result = s.kind in {skVar, skLet, skConst} + else: + result = false proc checkCanEval(c: PCtx; n: PNode) = # we need to ensure that we don't evaluate 'x' here: @@ -1523,6 +1563,7 @@ proc checkCanEval(c: PCtx; n: PNode) = # little hack ahead for bug #12612: assume gensym'ed variables # are in the right scope: if sfGenSym in s.flags and c.prc.sym == nil: discard + elif s.kind == skParam and s.typ.kind == tyTypeDesc: discard else: cannotEval(c, n) elif s.kind in {skProc, skFunc, skConverter, skMethod, skIterator} and sfForward in s.flags: @@ -1633,6 +1674,7 @@ proc isEmptyBody(n: PNode): bool = proc importcCond*(c: PCtx; s: PSym): bool {.inline.} = ## return true to importc `s`, false to execute its body instead (refs #8405) + result = false if sfImportc in s.flags: if s.kind in routineKinds: return isEmptyBody(getBody(c.graph, s)) @@ -1776,7 +1818,6 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags let setLit = c.genx(checkExpr[1]) var rs = c.getTemp(getSysType(c.graph, n.info, tyBool)) c.gABC(n, opcContainsSet, rs, setLit, discVal) - c.freeTemp(discVal) c.freeTemp(setLit) # If the check fails let the user know let lab1 = c.xjmp(n, if negCheck: opcFJmp else: opcTJmp, rs) @@ -1789,6 +1830,7 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags strLit.typ = strType c.genLit(strLit, msgReg) c.gABC(n, opcInvalidField, msgReg, discVal) + c.freeTemp(discVal) c.freeTemp(msgReg) c.patch(lab1) @@ -1862,10 +1904,10 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = result = newNodeIT(nkUIntLit, info, t) of tyFloat..tyFloat128: result = newNodeIT(nkFloatLit, info, t) - of tyCstring, tyString: + of tyString: result = newNodeIT(nkStrLit, info, t) result.strVal = "" - of tyVar, tyLent, tyPointer, tyPtr, tyUntyped, + of tyCstring, tyVar, tyLent, tyPointer, tyPtr, tyUntyped, tyTyped, tyTypeDesc, tyRef, tyNil: result = newNodeIT(nkNilLit, info, t) of tyProc: @@ -1911,6 +1953,8 @@ proc genVarSection(c: PCtx; n: PNode) = let s = a[0].sym checkCanEval(c, a[0]) if s.isGlobal: + let runtimeAccessToCompileTime = c.mode == emRepl and + sfCompileTime in s.flags and s.position > 0 if s.position == 0: if importcCond(c, s): c.importcSym(a.info, s) else: @@ -1920,7 +1964,9 @@ proc genVarSection(c: PCtx; n: PNode) = assert sa.kind != nkCall c.globals.add(sa) s.position = c.globals.len - if a[2].kind != nkEmpty: + if runtimeAccessToCompileTime: + discard + elif a[2].kind != nkEmpty: let tmp = c.genx(a[0], {gfNodeAddr}) let val = c.genx(a[2]) c.genAdditionalCopy(a[2], opcWrDeref, tmp, 0, val) @@ -2042,12 +2088,13 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = proc genProc*(c: PCtx; s: PSym): int proc toKey(s: PSym): string = + result = "" var s = s while s != nil: result.add s.name.s if s.owner != nil: if sfFromGeneric in s.flags: - s = s.owner.owner + s = s.instantiatedFrom.owner else: s = s.owner result.add "." @@ -2073,8 +2120,13 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = let s = n.sym checkCanEval(c, n) case s.kind - of skVar, skForVar, skTemp, skLet, skParam, skResult: + of skVar, skForVar, skTemp, skLet, skResult: genRdVar(c, n, dest, flags) + of skParam: + if s.typ.kind == tyTypeDesc: + genTypeLit(c, s.typ, dest) + else: + genRdVar(c, n, dest, flags) of skProc, skFunc, skConverter, skMacro, skTemplate, skMethod, skIterator: # 'skTemplate' is only allowed for 'getAst' support: if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure: @@ -2176,8 +2228,6 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkVarSection, nkLetSection: unused(c, n, dest) genVarSection(c, n) - of declarativeDefs, nkMacroDef: - unused(c, n, dest) of nkLambdaKinds: #let s = n[namePos].sym #discard genProc(c, s) @@ -2197,7 +2247,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = dest = tmp0 of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt, - nkMixinStmt, nkBindStmt: + nkMixinStmt, nkBindStmt, declarativeDefs, nkMacroDef: unused(c, n, dest) of nkStringToCString, nkCStringToString: gen(c, n[0], dest) @@ -2328,7 +2378,7 @@ proc genProc(c: PCtx; s: PSym): int = #if s.name.s == "outterMacro" or s.name.s == "innerProc": # echo "GENERATING CODE FOR ", s.name.s let last = c.code.len-1 - var eofInstr: TInstr + var eofInstr: TInstr = default(TInstr) if last >= 0 and c.code[last].opcode == opcEof: eofInstr = c.code[last] c.code.setLen(last) @@ -2338,7 +2388,7 @@ proc genProc(c: PCtx; s: PSym): int = c.procToCodePos[s.id] = result # thanks to the jmp we can add top level statements easily and also nest # procs easily: - let body = transformBody(c.graph, c.idgen, s, if isCompileTimeProc(s): dontUseCache else: useCache) + let body = transformBody(c.graph, c.idgen, s, if isCompileTimeProc(s): {} else: {useCache}) let procStart = c.xjmp(body, opcJmp, 0) var p = PProc(blocks: @[], sym: s) let oldPrc = c.prc diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim index 1741574b8661f..2d7ad63e799b3 100644 --- a/compiler/vmhooks.nim +++ b/compiler/vmhooks.nim @@ -69,7 +69,7 @@ proc getVar*(a: VmArgs; i: Natural): PNode = case p.kind of rkRegisterAddr: result = p.regAddr.node of rkNodeAddr: result = p.nodeAddr[] - else: doAssert false, $p.kind + else: raiseAssert $p.kind proc getNodeAddr*(a: VmArgs; i: Natural): PNode = let nodeAddr = getX(rkNodeAddr, nodeAddr) diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index b48197aefad21..e1e69f6cc503f 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -21,6 +21,7 @@ proc ptrToInt(x: PNode): int {.inline.} = proc getField(n: PNode; position: int): PSym = case n.kind of nkRecList: + result = nil for i in 0.. infoMax.time: infoMax = info diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 21b09707530be..aa25f7fd1aa70 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -49,7 +49,7 @@ type wCompleteStruct = "completeStruct", wRequiresInit = "requiresInit", wAlign = "align", wNodecl = "nodecl", wPure = "pure", wSideEffect = "sideEffect", wHeader = "header", wNoSideEffect = "noSideEffect", wGcSafe = "gcsafe", wNoreturn = "noreturn", - wNosinks = "nosinks", wMerge = "merge", wLib = "lib", wDynlib = "dynlib", + wNosinks = "nosinks", wLib = "lib", wDynlib = "dynlib", wCompilerProc = "compilerproc", wCore = "core", wProcVar = "procvar", wBase = "base", wUsed = "used", wFatal = "fatal", wError = "error", wWarning = "warning", wHint = "hint", @@ -89,29 +89,38 @@ type wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain", wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises", wSystemRaisesDefect = "systemRaisesDefect", wRedefine = "redefine", wCallsite = "callsite", + wQuirky = "quirky", + # codegen keywords, but first the ones that are also pragmas: + wExtern = "extern", wGoto = "goto", wRegister = "register", + wUnion = "union", wPacked = "packed", wVirtual = "virtual", + wVolatile = "volatile", wMember = "member", + wByCopy = "bycopy", wByRef = "byref", + + # codegen keywords but not pragmas: wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default", wDelete = "delete", wDouble = "double", wDynamicCast = "dynamic_cast", - wExplicit = "explicit", wExtern = "extern", wFalse = "false", wFloat = "float", - wFriend = "friend", wGoto = "goto", wInt = "int", wLong = "long", wMutable = "mutable", + wExplicit = "explicit", wFalse = "false", wFloat = "float", + wFriend = "friend", wInt = "int", wLong = "long", wMutable = "mutable", wNamespace = "namespace", wNew = "new", wOperator = "operator", wPrivate = "private", - wProtected = "protected", wPublic = "public", wRegister = "register", + wProtected = "protected", wPublic = "public", wReinterpretCast = "reinterpret_cast", wRestrict = "restrict", wShort = "short", wSigned = "signed", wSizeof = "sizeof", wStaticCast = "static_cast", wStruct = "struct", wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef", wTypeid = "typeid", wTypeof = "typeof", wTypename = "typename", - wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual", - wVoid = "void", wVolatile = "volatile", wWchar = "wchar_t", + wUnsigned = "unsigned", wVoid = "void", wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype", wNullptr = "nullptr", wNoexcept = "noexcept", wThreadLocal = "thread_local", wStaticAssert = "static_assert", - wChar16 = "char16_t", wChar32 = "char32_t", + wChar16 = "char16_t", wChar32 = "char32_t", wWchar = "wchar_t", wStdIn = "stdin", wStdOut = "stdout", wStdErr = "stderr", - wInOut = "inout", wByCopy = "bycopy", wByRef = "byref", wOneWay = "oneway", + wInOut = "inout", wOneWay = "oneway", + # end of codegen keywords + wBitsize = "bitsize", wImportHidden = "all", wSendable = "sendable" @@ -124,12 +133,15 @@ const nimKeywordsLow* = ord(wAsm) nimKeywordsHigh* = ord(wYield) - ccgKeywordsLow* = ord(wAuto) + ccgKeywordsLow* = ord(wExtern) ccgKeywordsHigh* = ord(wOneWay) cppNimSharedKeywords* = { wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport, wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing} + + nonPragmaWordsLow* = wAuto + nonPragmaWordsHigh* = wOneWay from std/enumutils import genEnumCaseStmt diff --git a/config/nim.cfg b/config/nim.cfg index a9dba347a0285..7c99581396df9 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -14,14 +14,13 @@ cc = gcc # additional options always passed to the compiler: --parallel_build: "0" # 0 to auto-detect number of processors -@if nimHasAmbiguousEnumHint: - # not needed if hint is a style check - hint[AmbiguousEnum]=off +@if not nimHasNolineTooLong: + hint[LineTooLong]=off @end + #hint[XDeclaredButNotUsed]=off threads:on -define:nimPreviewRangeDefault # Examples of how to setup a cross-compiler: # Nim can target architectures and OSes different than the local host @@ -354,7 +353,7 @@ tcc.options.always = "-w" icl.cpp.options.always %= "${icl.cpp.options.always} /Qipo" gcc.options.always %= "${gcc.options.always} -flto=auto" gcc.cpp.options.always %= "${gcc.cpp.options.always} -flto=auto" - gcc.options.linker %= "${gcc.options.linker} -flto=auto" + gcc.options.linker %= "${gcc.options.linker} -flto=auto -Wno-stringop-overflow" # https://github.com/nim-lang/Nim/issues/21595 gcc.cpp.options.linker %= "${gcc.cpp.options.linker} -flto=auto" @end @if strip: @@ -363,4 +362,3 @@ tcc.options.always = "-w" clang.options.linker %= "${clang.options.linker} -s" clang.cpp.options.linker %= "${clang.cpp.options.linker} -s" @end - diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index abe039738f13f..9535aa384fc22 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -155,7 +155,7 @@ doc.body_toc_group = """
Search: + oninput="search()" />
$body_toc_groupsection $tableofcontents @@ -189,7 +189,7 @@ doc.body_toc_group = """
- Search: + Search:
Group by: @@ -237,10 +237,10 @@ doc.file = """ - + - +
diff --git a/doc/advopt.txt b/doc/advopt.txt index 5465e3a62cf50..e4d11081ae482 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -48,7 +48,7 @@ Advanced options: --unitsep:on|off use the ASCII unit separator (31) between error messages, useful for IDE-like tooling --declaredLocs:on|off show declaration locations in messages - --spellSuggest|:num show at most `num >= 0` spelling suggestions on typos. + --spellSuggest:num show at most `num >= 0` spelling suggestions on typos. if `num` is not specified (or `auto`), return an implementation defined set of suggestions. --hints:on|off|list. `on|off` enables or disables hints. @@ -175,3 +175,5 @@ Advanced options: --deepcopy:on|off enable 'system.deepCopy' for ``--mm:arc|orc`` --jsbigint64:on|off toggle the use of BigInt for 64-bit integers for the JavaScript backend (default: on) + --nimBasePattern:nimbase.h + allows to specify a custom pattern for `nimbase.h` diff --git a/doc/contributing.md b/doc/contributing.md index a472f657a7eae..420c1438e9a88 100644 --- a/doc/contributing.md +++ b/doc/contributing.md @@ -137,9 +137,9 @@ You can run the tests with which will run a good subset of tests. Some tests may fail. If you only want to see the output of failing tests, go for -```cmd + ```cmd ./koch tests --failing all -``` + ``` You can also run only a single category of tests. A category is a subdirectory in the ``tests/`` directory. There are a couple of special categories; for a @@ -336,7 +336,7 @@ To avoid accidental highlighting follow this rule in ``*.nim`` files: .. Note:: ``*.rst`` files have ``:literal:`` as their default role. So for them the rule above is only applicable if the ``:nim:`` role - is set up manually as the default \[*]: + is set up manually as the default [^1]: .. role:: nim(code) :language: nim @@ -345,7 +345,7 @@ To avoid accidental highlighting follow this rule in ``*.nim`` files: The first 2 lines are for other RST implementations, including Github one. - \[*] this is fulfilled when ``doc/rstcommon.rst`` is included. + [^1]: this is fulfilled when ``doc/rstcommon.rst`` is included. Best practices ============== diff --git a/doc/destructors.md b/doc/destructors.md index 789cb93d6d510..e192fd362cc69 100644 --- a/doc/destructors.md +++ b/doc/destructors.md @@ -30,17 +30,20 @@ Motivating example With the language mechanisms described here, a custom seq could be written as: - ```nim + ```nim test type myseq*[T] = object len, cap: int data: ptr UncheckedArray[T] - proc `=destroy`*[T](x: var myseq[T]) = + proc `=destroy`*[T](x: myseq[T]) = if x.data != nil: for i in 0..