diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 8406f607f2..0145138650 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/stale.yml b/.github/stale.yml deleted file mode 100644 index c175f64a0f..0000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,69 +0,0 @@ -# Configuration for probot-stale - https://github.com/probot/stale - -# Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 365 - -# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. -# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. -daysUntilClose: 30 - -# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) -onlyLabels: [] - -# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable -exemptLabels: - - ARC - - bounty - - Codegen - - Crash - - Generics - - High Priority - - Macros - - Next release - - Showstopper - - Static[T] - -# Set to true to ignore issues in a project (defaults to false) -exemptProjects: false - -# Set to true to ignore issues in a milestone (defaults to false) -exemptMilestones: false - -# Set to true to ignore issues with an assignee (defaults to false) -exemptAssignees: false - -# Label to use when marking as stale -staleLabel: stale - -# Comment to post when marking as stale. Set to `false` to disable -markComment: > - This pull request has been automatically marked as stale because it has not had - recent activity. - If you think it is still a valid PR, please rebase it on the latest devel; - otherwise it will be closed. Thank you for your contributions. - -# Comment to post when removing the stale label. -# unmarkComment: > -# Your comment here. - -# Comment to post when closing a stale Issue or Pull Request. -# closeComment: > -# Your comment here. - -# Limit the number of actions per hour, from 1-30. Default is 30 -limitPerRun: 20 - -# Limit to only `issues` or `pulls` -only: pulls - -# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': -# pulls: -# daysUntilStale: 30 -# markComment: > -# This pull request has been automatically marked as stale because it has not had -# recent activity. It will be closed if no further activity occurs. Thank you -# for your contributions. - -# issues: -# exemptLabels: -# - confirmed diff --git a/.github/workflows/bisects.yml b/.github/workflows/bisects.yml new file mode 100644 index 0000000000..a8200c1f96 --- /dev/null +++ b/.github/workflows/bisects.yml @@ -0,0 +1,23 @@ +# See https://github.com/juancarlospaco/nimrun-action/issues/3#issuecomment-1607344901 +name: issue comments bisects +on: + issue_comment: + types: created + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + # nimrun-action requires Nim installed. + - uses: jiro4989/setup-nim-action@v1 + with: + nim-version: 'devel' + + - name: Install Dependencies + run: sudo apt-get install --no-install-recommends -yq valgrind + + - 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 new file mode 100644 index 0000000000..68f0007228 --- /dev/null +++ b/.github/workflows/ci_bench.yml @@ -0,0 +1,115 @@ +name: Benchmarks CI +on: + pull_request: + push: + branches: + - 'devel' + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04] + cpu: [amd64] + name: '${{ matrix.os }}' + runs-on: ${{ matrix.os }} + timeout-minutes: 60 # refs bug #18178 + steps: + - name: 'Checkout' + uses: actions/checkout@v3 + with: + fetch-depth: 2 + + - name: 'Install node.js 16.x' + uses: actions/setup-node@v3 + with: + node-version: '16.x' + + - name: 'Install dependencies (Linux amd64)' + if: runner.os == 'Linux' && matrix.cpu == 'amd64' + run: | + sudo apt-fast update -qq + DEBIAN_FRONTEND='noninteractive' \ + sudo apt-fast install --no-install-recommends -yq \ + libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \ + valgrind libc6-dbg libblas-dev xorg-dev + + - name: 'Add build binaries to PATH' + shell: bash + run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" + + - name: 'Build csourcesAny' + shell: bash + run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}' + + - name: 'Build koch' + shell: bash + run: nim c koch + + - name: 'Build Nim' + shell: bash + run: ./koch boot -d:release -d:nimStrictMode --lib:lib + + - name: 'Build Nimble' + shell: bash + run: ./koch nimble + + - name: 'Action' + shell: bash + run: nim c -r -d:release ci/action.nim + + - name: 'Checkout minimize' + uses: actions/checkout@v3 + with: + repository: 'nim-lang/ci_bench' + path: minimize + + - name: 'Run minimize benchmarks' + shell: bash + run: ./minimize/minimize ci-bench + + - name: 'Restore minimize cached database' + uses: actions/cache/restore@v3 + with: + path: minimize.csv + key: minimize-db-key + + - name: 'Update minimize db' + shell: bash + run: ./minimize/minimize update-db + + - name: 'Save minimize cached database' + if: | + github.event_name == 'push' && github.ref == 'refs/heads/devel' && + matrix.target == 'linux' + uses: actions/cache/save@v3 + with: + path: minimize.csv + key: minimize-db-key + + - name: 'Generate minimize report' + shell: bash + run: ./minimize/minimize generate-report + + - name: 'Archive minimize report' + uses: actions/upload-artifact@v3 + with: + name: minimize-report + path: | + minimize/minimize.html + minimize/minimize.csv + + # Requires additional permissions, see: + # https://github.com/nim-lang/Nim/actions/runs/4778177321/jobs/8494423792?pr=21566 + # - name: 'Publish HTML report' + # uses: rossjrw/pr-preview-action@v1 + # with: + # source-dir: minimize + # umbrella-dir: minimize + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract md summary + run: | + cat minimize/summary.md >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..d89a4888a1 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,25 @@ +# https://github.com/actions/stale#usage +name: Stale pull requests + +on: + schedule: + - cron: '0 0 * * *' # Midnight. + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v8 + with: + days-before-pr-stale: 365 + days-before-pr-close: 30 + days-before-issue-stale: -1 + days-before-issue-close: -1 + exempt-pr-labels: "ARC,bounty,Codegen,Crash,Generics,High Priority,Macros,Next release,Showstopper,Static[T]" + exempt-issue-labels: "Showstopper,Severe,bounty,Compiler Crash,Medium Priority" + stale-pr-message: > + This pull request is stale because it has been open for 1 year with no activity. + Contribute more commits on the pull request and rebase it on the latest devel, + or it will be closed in 30 days. Thank you for your contributions. + close-pr-message: > + This pull request has been marked as stale and closed due to inactivity after 395 days. diff --git a/.gitignore b/.gitignore index d0c4558df7..fad7909bd8 100644 --- a/.gitignore +++ b/.gitignore @@ -110,3 +110,5 @@ htmldocs nimdoc.out.css # except here: !/nimdoc/testproject/expected/* +pkgs/ +/compiler/compiler/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 31908d2238..0000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,62 +0,0 @@ -# xxx unused, out of date - -image: ubuntu:18.04 - -stages: - - pre-build - - build - - deploy - - test - -.linux_set_path: &linux_set_path_def - before_script: - - export PATH=$(pwd)/bin${PATH:+:$PATH} - tags: - - linux - -.windows_set_path: &win_set_path_def - before_script: - - set PATH=%CD%\bin;%PATH% - tags: - - windows - - -build-windows: - stage: build - script: - - ci\build.bat - artifacts: - paths: - - bin\nim.exe - - bin\nimd.exe - - compiler\nim.exe - - koch.exe - expire_in: 1 week - tags: - - windows - -deploy-windows: - stage: deploy - script: - - koch.exe winrelease - artifacts: - paths: - - build/*.exe - - build/*.zip - expire_in: 1 week - tags: - - windows - - fast - - - -test-windows: - stage: test - <<: *win_set_path_def - script: - - call ci\deps.bat - - nim c testament\tester - - testament\tester.exe all - tags: - - windows - - fast diff --git a/changelog.md b/changelog.md index 100d12fd72..fb0102cd3a 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,8 @@ [//]: # "Additions:" +- Adds `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content. + [//]: # "Deprecations:" @@ -24,7 +26,7 @@ ## Compiler changes - - ## 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 779eec529e..457cc62a6b 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -1,494 +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. +# 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. +``` -- The `gc:v2` option is removed. +### Improved type inference -- The `mainmodule` and `m` options are 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 `threads:on` option is now the default. +For example, code like the following now compiles: -- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via - `experimental:flexibleOptionalParams`. +```nim +let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")] +``` -- Automatic dereferencing (experimental feature) is removed. +### Forbidden Tags -- 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. +[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. -- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and -`sink` for optimization purposes. +For example: -- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fullfill its promises. +```nim -- 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 +type IO = object ## input/output effect +proc readLine(): string {.tags: [IO].} = discard +proc echoLine(): void = discard - # 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`. +proc no_IO_please() {.forbids: [IO].} = + # this is OK because it didn't define any tag: + echoLine() + # the compiler prevents this: + let y = readLine() -- `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. +### New standard library modules -- Removed the `nimIncrSeqV3` define. +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. -- `macros.getImpl` for `const` symbols now returns the full definition node - (as `nnkConstDef`) rather than the AST of the constant value. +Use: -- Lock levels are deprecated, now a noop. +- `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. -- ORC is now the default memory management strategy. Use - `--mm:refc` for a transition period. +### Consistent underscore handling -- `strictEffects` are no longer experimental. - Use `legacy:laxEffects` to keep backward compatibility. +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: -- 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`. +```nim +proc foo(_: int): int = _ + 1 +echo foo(1) -- 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. - -- `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 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` +proc foo[_](t: typedesc[_]): seq[_] = @[default(_)] +echo foo[int]() -- 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 _() = echo "_" +_() -- If no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered. +type _ = int +let x: _ = 3 +``` -- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection. +Whereas the following code now compiles: -- 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) +```nim +proc foo(_, _: int): int = 123 +echo foo(1, 2) - 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). - -## 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. -- `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 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) - in `jscore` for JavaScript targets. -- 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. - -[//]: # "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-define-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. - -## 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. - -## 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. - - * and we add 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: +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 +``` + +### JavaScript codegen improvement + +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`. + + +## Docgen improvements + +`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 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: + * 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" ... ``` + +## C++ interop enhancements + +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. + +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. + +Consider the following example: + +```cpp + +struct Base { + int someValue; + Base(int inValue) { + someValue = inValue; + }; +}; + +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. + + ## 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. + 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 0000000000..a38f2b40b7 --- /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 0000000000..0a293d35fb --- /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 5d3a50fda2..ad0f4df0b6 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/ci/funs.sh b/ci/funs.sh index 8e7f8faf7a..0263661983 100644 --- a/ci/funs.sh +++ b/ci/funs.sh @@ -58,7 +58,11 @@ _nimNumCpu(){ # FreeBSD | macOS: $(sysctl -n hw.ncpu) # OpenBSD: $(sysctl -n hw.ncpuonline) # windows: $NUMBER_OF_PROCESSORS ? - echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1) + if env | grep -q '^NIMCORES='; then + echo $NIMCORES + else + echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1) + fi } _nimBuildCsourcesIfNeeded(){ diff --git a/compiler/aliasanalysis.nim b/compiler/aliasanalysis.nim index f14b815243..e24c6d8e26 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 4b50fdb282..fa9824c41e 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 b5306c423c..ce5ad0f292 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -231,7 +231,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # 49 flags! + TSymFlag* = enum # 51 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -283,7 +283,7 @@ type sfNamedParamCall, # symbol needs named parameter call syntax in target # language; for interfacing with Objective C sfDiscardable, # returned value may be discarded implicitly - sfOverriden, # proc is overridden + sfOverridden, # proc is overridden sfCallsite # A flag for template symbols to tell the # compiler it should use line information from # the calling side of the macro, not from the @@ -303,6 +303,8 @@ type sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally' sfSingleUsedTemp # For temporaries that we know will only be used once sfNoalias # 'noalias' annotation, means C's 'restrict' + # for templates and macros, means cannot be called + # as a lone symbol (cannot use alias syntax) sfEffectsDelayed # an 'effectsDelayed' parameter sfGeneratedType # A anonymous generic type that is generated by the compiler for # objects that do not have generic parameters in case one of the @@ -310,6 +312,10 @@ type # # This is disallowed but can cause the typechecking to go into # 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] @@ -335,13 +341,14 @@ const sfCompileToCpp* = sfInfixCall # compile the module as C++ code sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code - sfExperimental* = sfOverriden # module uses the .experimental switch - sfGoto* = sfOverriden # var is used for 'goto' code generation + sfExperimental* = sfOverridden # module uses the .experimental switch + sfGoto* = sfOverridden # var is used for 'goto' code generation sfWrittenTo* = sfBorrow # param is assigned to # currently unimplemented 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 @@ -513,7 +520,7 @@ type nfSkipFieldChecking # node skips field visable checking TNodeFlags* = set[TNodeFlag] - TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 46) + TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47) tfVarargs, # procedure has C styled varargs # tyArray type represeting a varargs list tfNoSideEffect, # procedure type does not allow side effects @@ -583,6 +590,8 @@ type tfIsConstructor tfEffectSystemWorkaround tfIsOutParam + tfSendable + tfImplicitStatic TTypeFlags* = set[TTypeFlag] @@ -618,13 +627,12 @@ type # file (it is loaded on demand, which may # mean: never) skPackage, # symbol is a package (used for canonicalization) - skAlias # an alias (needs to be resolved immediately) TSymKinds* = set[TSymKind] const routineKinds* = {skProc, skFunc, skMethod, skIterator, skConverter, skMacro, skTemplate} - ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub, skAlias} + routineKinds + ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub} + routineKinds tfUnion* = tfNoSideEffect tfGcSafe* = tfThread @@ -633,7 +641,7 @@ const skError* = skUnknown var - eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam} + eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam, tfSendable} ## 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}. @@ -685,7 +693,7 @@ type mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, - mMove, mWasMoved, 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, @@ -818,9 +826,6 @@ type locOther # location is something other TLocFlag* = enum lfIndirect, # backend introduced a pointer - lfFullExternalName, # only used when 'conf.cmd == cmdNimfix': Indicates - # that the symbol has been imported via 'importc: "fullname"' and - # no format string. lfNoDeepCopy, # no need for a deep copy lfNoDecl, # do not declare it in C lfDynamicLib, # link symbol to dynamic library @@ -855,7 +860,7 @@ type # keep in sync with PackedLib kind*: TLibKind generated*: bool # needed for the backends: - isOverriden*: bool + isOverridden*: bool name*: Rope path*: PNode # can be a string literal! @@ -917,7 +922,9 @@ type # for modules, an unique index corresponding # to the module's fileIdx # for variables a slot index for the evaluator - offset*: int # offset of record field + offset*: int32 # offset of record field + disamb*: int32 # disambiguation number; the basic idea is that + # `___` loc*: TLoc annex*: PLib # additional fields (seldom used, so we use a # reference to another object to save space) @@ -925,7 +932,7 @@ type cname*: string # resolved C declaration name in importc decl, e.g.: # proc fun() {.importc: "$1aux".} => cname = funaux constraint*: PNode # additional constraints like 'lit|result'; also - # misused for the codegenDecl pragma in the hope + # misused for the codegenDecl and virtual pragmas in the hope # it won't cause problems # for skModule the string literal to output for # deprecated modules. @@ -938,6 +945,7 @@ type attachedWasMoved, attachedDestructor, attachedAsgn, + attachedDup, attachedSink, attachedTrace, attachedDeepCopy @@ -950,7 +958,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 @@ -1031,6 +1039,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 @@ -1121,11 +1131,10 @@ const proc getPIdent*(a: PNode): PIdent {.inline.} = ## Returns underlying `PIdent` for `{nkSym, nkIdent}`, or `nil`. - # xxx consider whether also returning the 1st ident for {nkOpenSymChoice, nkClosedSymChoice} - # which may simplify code. case a.kind of nkSym: a.sym.name of nkIdent: a.ident + of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name else: nil const @@ -1141,13 +1150,17 @@ type symId*: int32 typeId*: int32 sealed*: bool + disambTable*: CountTable[PIdent] const PackageModuleId* = -3'i32 proc idGeneratorFromModule*(m: PSym): IdGenerator = assert m.kind == skModule - result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0) + result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0, disambTable: initCountTable[PIdent]()) + +proc idGeneratorForPackage*(nextIdWillBe: int32): IdGenerator = + result = IdGenerator(module: PackageModuleId, symId: nextIdWillBe - 1'i32, typeId: 0, disambTable: initCountTable[PIdent]()) proc nextSymId*(x: IdGenerator): ItemId {.inline.} = assert(not x.sealed) @@ -1214,6 +1227,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 @@ -1233,6 +1247,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 @@ -1248,6 +1264,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 = @@ -1339,11 +1361,15 @@ when false: echo k echo v -proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym, +proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym, info: TLineInfo; options: TOptions = {}): PSym = # generates a symbol and initializes the hash field too + assert not name.isNil + let id = nextSymId idgen result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id, - options: options, owner: owner, offset: defaultOffset) + options: options, owner: owner, offset: defaultOffset, + disamb: getOrDefault(idgen.disambTable, name).int32) + idgen.disambTable.inc name when false: if id.module == 48 and id.item == 39: writeStackTrace() @@ -1473,7 +1499,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 @@ -1503,7 +1529,7 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, const AttachedOpToStr*: array[TTypeAttachedOp, string] = [ - "=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy"] + "=wasMoved", "=destroy", "=copy", "=dup", "=sink", "=trace", "=deepcopy"] proc `$`*(s: PSym): string = if s != nil: @@ -1511,15 +1537,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 67598f9cae..756d93217c 100644 --- a/compiler/bitsets.nim +++ b/compiler/bitsets.nim @@ -87,5 +87,6 @@ const populationCount: array[uint8, uint8] = block: arr proc bitSetCard*(x: TBitSet): BiggestInt = + result = 0 for it in x: result.inc int(populationCount[it]) diff --git a/compiler/btrees.nim b/compiler/btrees.nim index c79442249d..3b737b1bc9 100644 --- a/compiler/btrees.nim +++ b/compiler/btrees.nim @@ -38,6 +38,7 @@ template less(a, b): bool = cmp(a, b) < 0 template eq(a, b): bool = cmp(a, b) == 0 proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val = + result = default(Val) var x = b.root while x.isInternal: for j in 0..Field1, $3);$n", + [addrLoc(p.config, dest), source, genTypeInfoV1(p.module, dest.t, dest.lode.info)]) of tySet: if mapSetType(p.config, ty) == ctArray: @@ -473,10 +477,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}) @@ -488,10 +491,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}) @@ -503,49 +505,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; @@ -585,11 +580,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) @@ -619,11 +613,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() @@ -645,12 +637,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 @@ -704,11 +695,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)]) @@ -724,10 +714,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) = @@ -752,23 +741,8 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} = skipTypes(typ, abstractInstOwned).kind in {tyVar} and tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags -proc derefBlock(p: BProc, e: PNode, d: var TLoc) = - # We transform (block: x)[] to (block: x[]) - let e0 = e[0] - var n = shallowCopy(e0) - n.typ = e.typ - for i in 0 ..< e0.len - 1: - n[i] = e0[i] - n[e0.len-1] = newTreeIT(nkHiddenDeref, e.info, e.typ, e0[e0.len-1]) - expr p, n, d - proc genDeref(p: BProc, e: PNode, d: var TLoc) = - if e.kind == nkHiddenDeref and e[0].kind in {nkBlockExpr, nkBlockStmt}: - # bug #20107. Watch out to not deref the pointer too late. - derefBlock(p, e, d) - return - - let mt = mapType(p.config, e[0].typ, mapTypeChooser(e[0])) + let mt = mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags: # XXX the amount of hacks for C's arrays is incredible, maybe we should # simply wrap them in a struct? --> Losing auto vectorization then? @@ -782,10 +756,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 @@ -822,8 +796,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.} = @@ -832,31 +805,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])) == ctArray or isCppRef(p, e.typ): + 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) @@ -870,6 +840,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: @@ -884,7 +855,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 @@ -896,7 +867,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)) @@ -917,9 +888,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(".") @@ -945,10 +916,17 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = var discIndex = newRopeAppender() rdSetElemLoc(p.config, v, u.t, discIndex) if optTinyRtti in p.config.globalOptions: - # not sure how to use `genEnumToStr` here - if p.config.getStdlibVersion < (1, 5, 1): - const code = "{ #raiseFieldError($1); " - linefmt(p, cpsStmts, code, [strLit]) + let base = disc.typ.skipTypes(abstractInst+{tyRange}) + case base.kind + of tyEnum: + const code = "{ #raiseFieldErrorStr($1, $2); " + 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 = default(TLoc) + expr(p, newSymNode(toStrProc), toStr) + let enumStr = "$1($2)" % [rdLoc(toStr), rdLoc(v)] + linefmt(p, cpsStmts, code, [strLit, enumStr]) else: const code = "{ #raiseFieldError2($1, (NI)$2); " linefmt(p, cpsStmts, code, [strLit, discIndex]) @@ -959,12 +937,8 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = var firstLit = newRopeAppender() int64Literal(cast[int](first), firstLit) let discName = genTypeInfo(p.config, p.module, disc.sym.typ, e.info) - if p.config.getStdlibVersion < (1,5,1): - const code = "{ #raiseFieldError($1); " - linefmt(p, cpsStmts, code, [strLit]) - else: - const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$2) + (NI)$3, $4)); " - linefmt(p, cpsStmts, code, [strLit, discIndex, firstLit, discName]) + const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$2) + (NI)$3, $4)); " + linefmt(p, cpsStmts, code, [strLit, discIndex, firstLit, discName]) raiseInstr(p, p.s(cpsStmts)) linefmt p, cpsStmts, "}$n", [] @@ -972,7 +946,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) @@ -990,17 +964,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) @@ -1033,9 +1005,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) @@ -1081,9 +1052,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: @@ -1107,9 +1077,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: @@ -1157,8 +1126,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? @@ -1183,11 +1151,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) & "))" @@ -1200,8 +1167,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) @@ -1229,7 +1195,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) @@ -1244,8 +1210,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("") @@ -1279,14 +1244,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), @@ -1372,16 +1336,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", @@ -1389,14 +1352,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 @@ -1422,12 +1384,11 @@ 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): - if canFormAcycle(a.t): + if canFormAcycle(p.module.g.graph, a.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [a.rdLoc]) else: linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [a.rdLoc]) @@ -1448,12 +1409,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) @@ -1461,10 +1420,9 @@ 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(dest.t): + if canFormAcycle(p.module.g.graph, dest.t): linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [dest.rdLoc]) else: linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [dest.rdLoc]) @@ -1487,9 +1445,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", @@ -1503,10 +1460,9 @@ 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) + if d.k == locNone: d = getTemp(p, e.typ, needsInit=false) linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n", [d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon), getSeqPayloadType(p.module, seqtype), @@ -1559,10 +1515,10 @@ 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 if useTemp: - getTemp(p, t, tmp) + tmp = getTemp(p, t) r = rdLoc(tmp) if isRef: rawGenNew(p, tmp, "", needsInit = nfAllFieldsSet notin e.flags) @@ -1578,7 +1534,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = 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))", [ @@ -1721,8 +1678,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 = "" @@ -1763,8 +1719,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: @@ -1786,7 +1741,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) @@ -1844,8 +1799,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() @@ -1853,8 +1807,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() @@ -1862,11 +1815,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) @@ -1879,11 +1831,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: @@ -1895,22 +1845,29 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)") else: unaryExpr(p, e, d, "$1Len_0") else: - if op == mHigh: unaryExpr(p, e, d, "($1.Field1-1)") - else: unaryExpr(p, e, d, "$1.Field1") + let isDeref = a.kind in {nkHiddenDeref, nkDerefExpr} + if op == mHigh: + if isDeref: + unaryExpr(p, e, d, "($1->Field1-1)") + else: + unaryExpr(p, e, d, "($1.Field1-1)") + else: + if isDeref: + unaryExpr(p, e, d, "$1->Field1") + 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)") 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]) @@ -1921,10 +1878,6 @@ 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 @@ -1937,15 +1890,14 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = 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, [ @@ -1965,12 +1917,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, {}) @@ -1983,10 +1934,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, {}) @@ -2028,10 +1978,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]) @@ -2047,19 +1996,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(")") @@ -2070,8 +2019,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) = @@ -2087,7 +2036,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 @@ -2124,14 +2074,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)]) @@ -2139,18 +2088,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", [ @@ -2167,8 +2115,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: @@ -2205,10 +2152,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 @@ -2221,9 +2175,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" @@ -2276,16 +2229,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) @@ -2296,11 +2247,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: @@ -2309,11 +2260,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)])) @@ -2339,25 +2289,46 @@ proc genWasMoved(p: BProc; n: PNode) = 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, {}) - resetLoc(p, a) + 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: + 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) = if optSeqDestructors in p.config.globalOptions: @@ -2365,8 +2336,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" & @@ -2376,8 +2346,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", @@ -2394,8 +2363,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,8 +2379,8 @@ proc genSlice(p: BProc; e: PNode; d: var TLoc) = let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.lastSon, prepareForMutation = e[1].kind == nkHiddenDeref and e[1].typ.skipTypes(abstractInst).kind == tyString and - p.config.selectedGC in {gcArc, gcOrc}) - if d.k == locNone: getTemp(p, e.typ, d) + p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}) + 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'; " & @@ -2447,11 +2415,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, @@ -2465,10 +2432,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) @@ -2503,8 +2469,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: @@ -2518,10 +2483,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mNewSeqOfCap: genNewSeqOfCap(p, e, d) of mSizeOf: let t = e[1].typ.skipTypes({tyTypeDesc}) - putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t, skVar)]) + putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t, dkVar)]) of mAlignOf: let t = e[1].typ.skipTypes({tyTypeDesc}) - putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t, skVar)]) + putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t, dkVar)]) of mOffsetOf: var dotExpr: PNode if e[1].kind == nkDotExpr: @@ -2529,9 +2494,10 @@ 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, skVar) + let tname = getTypeDesc(p.module, t, dkVar) let member = if t.kind == tyTuple: "Field" & rope(dotExpr[1].sym.position) @@ -2593,14 +2559,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e) expr(p, n, d) of mDeepCopy: - if p.config.selectedGC in {gcArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions: + 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) @@ -2610,6 +2575,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 @@ -2621,22 +2588,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() @@ -2645,7 +2613,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", @@ -2656,9 +2624,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() @@ -2668,7 +2636,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,15 +2648,30 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = if not handleConstExpr(p, n, d): let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized - if d.k == locNone: getTemp(p, t, d) + + var tmp: TLoc = default(TLoc) + # bug #16331 + let doesAlias = lhsDoesAlias(d.lode, n) + let dest = if doesAlias: addr(tmp) else: addr(d) + if doesAlias: + tmp = getTemp(p, n.typ) + elif d.k == locNone: + 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) @@ -2846,7 +2826,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = # expression not found in the cache: inc(p.module.labels) p.module.s[cfsData].addf("static NIM_CONST $1 $2 = ", - [getTypeDesc(p.module, t, skConst), tmp]) + [getTypeDesc(p.module, t, dkConst), tmp]) genBracedInit(p, n, isConst = true, t, p.module.s[cfsData]) p.module.s[cfsData].addf(";$n", []) @@ -2873,13 +2853,13 @@ proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) = if not genConstSetup(p, sym): return assert(sym.loc.r != "", $sym.name.s & $sym.itemId) if m.hcrOn: - m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]); + m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r]); m.initProc.procSec(cpsLocals).addf( "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, - getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)]) + getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(q, sym)]) else: let headerDecl = "extern NIM_CONST $1 $2;$n" % - [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r] + [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.r] m.s[cfsData].add(headerDecl) if sfExportc in sym.flags and p.module.g.generatedHeader != nil: p.module.g.generatedHeader.s[cfsData].add(headerDecl) @@ -2895,7 +2875,7 @@ proc genConstDefinition(q: BModule; p: BProc; sym: PSym) = q.s[cfsData].add data if q.hcrOn: # generate the global pointer with the real name - q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, skVar), sym.loc.r]) + q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.r]) # register it (but ignore the boolean result of hcrRegisterGlobal) q.initProc.procSec(cpsLocals).addf( "\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n", @@ -3020,7 +3000,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: @@ -3114,8 +3094,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: @@ -3211,9 +3190,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: @@ -3225,6 +3204,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = globalError(p.config, info, "cannot create null element for: " & $t.kind) proc caseObjDefaultBranch(obj: PNode; branch: Int128): int = + result = 0 for i in 1 ..< obj.len: for j in 0 .. obj[i].len - 2: if obj[i][j].kind == nkRange: @@ -3380,18 +3360,19 @@ proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) = let base = t.skipTypes(abstractInst)[0] - var data = rope"{" - for i in 0.. 0: data.addf(",$n", []) - genBracedInit(p, n[i], isConst, base, data) - data.add("}") + var data = rope"" + if n.len > 0: + data.add(", {") + for i in 0.. 0: data.addf(",$n", []) + genBracedInit(p, n[i], isConst, base, data) + data.add("}") let payload = getTempName(p.module) - appcg(p.module, cfsStrData, "static $5 struct {$n" & " NI cap; $1 data[$2];$n" & - "} $3 = {$2 | NIM_STRLIT_FLAG, $4};$n", [ + "} $3 = {$2 | NIM_STRLIT_FLAG$4};$n", [ getTypeDesc(p.module, base), n.len, payload, data, if isConst: "const" else: ""]) result.add "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload] @@ -3434,12 +3415,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) @@ -3467,10 +3446,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 5e6456704d..bd0e2a58a3 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,7 +94,7 @@ 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" + raiseAssert "unexpected set type kind" of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation, tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs, tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 63fb17527e..d6220afbf4 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -35,7 +35,9 @@ proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} = if n.kind == nkEmpty: result = false elif n.kind in nkCallKinds and n[0] != nil and n[0].typ != nil and n[0].typ.skipTypes(abstractInst).kind == tyProc: - if isInvalidReturnType(conf, n[0].typ, true): + if n[0].kind == nkSym and sfConstructor in n[0].sym.flags: + result = true + elif isInvalidReturnType(conf, n[0].typ, true): # var v = f() # is transformed into: var v; f(addr v) # where 'f' **does not** initialize the result! @@ -48,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 @@ -69,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: @@ -81,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) @@ -93,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: @@ -242,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", []) @@ -262,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)] @@ -288,11 +289,28 @@ 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() + var argsCounter = 0 + let typ = skipTypes(value[0].typ, abstractInst) + assert(typ.kind == tyProc) + for i in 1.. 0: genIfForCaseUntil(p, n, d, rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n", eqFormat = "if ($1 == $2) goto $3;$n", @@ -970,7 +971,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) = 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) @@ -996,7 +997,7 @@ proc genRestoreFrameAfterException(p: BProc) = proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = #[ code to generate: - std::exception_ptr error = nullptr; + std::exception_ptr error; try { body; } catch (Exception e) { @@ -1021,13 +1022,13 @@ 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) let etmp = p.labels - p.procSec(cpsInit).add(ropecg(p.module, "\tstd::exception_ptr T$1_ = nullptr;", [etmp])) + lineCg(p, cpsStmts, "std::exception_ptr T$1_;$n", [etmp]) let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, false, 0.Natural)) @@ -1061,7 +1062,6 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = if hasIf: lineF(p, cpsStmts, "else ", []) startBlock(p) # we handled the error: - linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp]) expr(p, t[i][0], d) linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlock(p) @@ -1124,7 +1124,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = if t[i].len == 1: # general except section: - startBlock(p, "catch (...) {", []) + startBlock(p, "catch (...) {$n", []) genExceptBranchBody(t[i][0]) endBlock(p) catchAllPresent = true @@ -1150,7 +1150,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = # general finally block: if t.len > 0 and t[^1].kind == nkFinally: if not catchAllPresent: - startBlock(p, "catch (...) {", []) + startBlock(p, "catch (...) {$n", []) genRestoreFrameAfterException(p) linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp]) endBlock(p) @@ -1187,7 +1187,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 @@ -1265,7 +1265,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) @@ -1300,7 +1300,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]) @@ -1371,7 +1372,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: @@ -1380,7 +1381,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]) @@ -1483,8 +1484,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)) @@ -1496,8 +1496,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: @@ -1599,11 +1598,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 @@ -1621,9 +1619,8 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = else: let le = e[0] let ri = e[1] - var a: TLoc - discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), skVar) - initLoc(a, locNone, le, OnUnknown) + var a: TLoc = initLoc(locNone, le, OnUnknown) + discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), dkVar) a.flags.incl(lfEnforceDeref) a.flags.incl(lfPrepareForMutation) genLineDir(p, le) # it can be a nkBracketExpr, which may raise @@ -1635,7 +1632,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 96f5869b00..adad9df3e6 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 6cc009bb96..b7363f67fe 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -11,9 +11,29 @@ # ------------------------- Name Mangling -------------------------------- -import sighashes, modulegraphs -import std/md5 +import sighashes, modulegraphs, strscans +import ../dist/checksums/src/checksums/md5 +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 + of skVar: dkVar + of skField: dkField + 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 @@ -39,10 +59,10 @@ proc fillBackendName(m: BModule; s: PSym) = var result = s.name.s.mangle.rope result.add "__" result.add m.g.graph.ifaces[s.itemId.module].uniqueName - result.add "_" - result.add rope s.itemId.item + result.add "_u" + result.addInt s.itemId.item # s.disamb # if m.hcrOn: - result.add "_" + result.add '_' result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config)) s.loc.r = result writeMangledName(m.ndi, s, m.config) @@ -50,7 +70,9 @@ proc fillBackendName(m: BModule; s: PSym) = proc fillParamName(m: BModule; s: PSym) = if s.loc.r == "": var res = s.name.s.mangle - res.add idOrSig(s, res, m.sigConflicts, m.config) + res.add "_p" + res.addInt s.position + #res.add idOrSig(s, res, m.sigConflicts, m.config) # Take into account if HCR is on because of the following scenario: # if a module gets imported and it has some more importc symbols in it, # some param names might receive the "_0" suffix to distinguish from what @@ -137,7 +159,7 @@ proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind = of 8: result = ctInt64 else: result = ctArray -proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind = +proc mapType(conf: ConfigRef; typ: PType; isParam: bool): TCTypeKind = ## Maps a Nim type to a C type case typ.kind of tyNone, tyTyped: result = ctVoid @@ -146,16 +168,16 @@ proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind = of tyNil: result = ctPtr of tySet: result = mapSetType(conf, typ) of tyOpenArray, tyVarargs: - if kind == skParam: result = ctArray + if isParam: result = ctArray else: result = ctStruct of tyArray, tyUncheckedArray: result = ctArray of tyObject, tyTuple: result = ctStruct of tyUserTypeClasses: doAssert typ.isResolvedUserTypeClass - return mapType(conf, typ.lastSon, kind) + return mapType(conf, typ.lastSon, isParam) of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned: - result = mapType(conf, lastSon(typ), kind) + result = mapType(conf, lastSon(typ), isParam) of tyEnum: if firstOrd(conf, typ) < 0: result = ctInt32 @@ -166,7 +188,7 @@ proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind = of 4: result = ctInt32 of 8: result = ctInt64 else: result = ctInt32 - of tyRange: result = mapType(conf, typ[0], kind) + of tyRange: result = mapType(conf, typ[0], isParam) of tyPtr, tyVar, tyLent, tyRef: var base = skipTypes(typ.lastSon, typedescInst) case base.kind @@ -183,14 +205,19 @@ proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind = of tyInt..tyUInt64: result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) of tyStatic: - if typ.n != nil: result = mapType(conf, lastSon typ, kind) - else: doAssert(false, "mapType: " & $typ.kind) - else: doAssert(false, "mapType: " & $typ.kind) + if typ.n != nil: result = mapType(conf, lastSon typ, isParam) + else: + result = ctVoid + doAssert(false, "mapType: " & $typ.kind) + else: + result = ctVoid + doAssert(false, "mapType: " & $typ.kind) + proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind = #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr #else: - result = mapType(conf, typ, skResult) + result = mapType(conf, typ, false) proc isImportedType(t: PType): bool = result = t.sym != nil and sfImportc in t.sym.flags @@ -203,7 +230,7 @@ proc isImportedCppType(t: PType): bool = proc isOrHasImportedCppType(typ: PType): bool = searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType) -proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope +proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope proc isObjLackingTypeField(typ: PType): bool {.inline.} = result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and @@ -223,7 +250,7 @@ proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool = getSize(conf, rettype) > conf.target.floatSize*3): result = true else: - case mapType(conf, rettype, skResult) + case mapType(conf, rettype, false) of ctArray: result = not (skipTypes(rettype, typedescInst).kind in {tyVar, tyLent, tyRef, tyPtr}) @@ -247,7 +274,7 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope = # linear search is not necessary anymore: result = tab.getOrDefault(sig) -proc addAbiCheck(m: BModule, t: PType, name: Rope) = +proc addAbiCheck(m: BModule; t: PType, name: Rope) = if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize): var msg = "backend & Nim disagree on size for: " msg.addTypeHeader(m.config, t) @@ -272,7 +299,7 @@ proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope = else: result = rope(literal) -proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = +proc getSimpleTypeDesc(m: BModule; typ: PType): Rope = const NumericalTypeToStr: array[tyInt..tyUInt64, string] = [ "NI", "NI8", "NI16", "NI32", "NI64", @@ -299,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 = "" @@ -309,13 +338,13 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = if cacheGetType(m.typeCache, sig) == "": m.typeCache[sig] = result -proc pushType(m: BModule, typ: PType) = +proc pushType(m: BModule; typ: PType) = for i in 0..high(m.typeStack): # pointer equality is good enough here: if m.typeStack[i] == typ: return m.typeStack.add(typ) -proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope = +proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope = if typ == nil: result = rope("void") else: result = getSimpleTypeDesc(m, typ) @@ -328,7 +357,7 @@ proc structOrUnion(t: PType): Rope = if tfUnion in t.flags: cachedUnion else: cachedStruct -proc addForwardStructFormat(m: BModule, structOrUnion: Rope, typename: Rope) = +proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) = if m.compileToCpp: m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename] else: @@ -338,7 +367,7 @@ proc seqStar(m: BModule): string = if optSeqDestructors in m.config.globalOptions: result = "" else: result = "*" -proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = +proc getTypeForward(m: BModule; typ: PType; sig: SigHash): Rope = result = cacheGetType(m.forwTypeCache, sig) if result != "": return result = getTypePre(m, typ, sig) @@ -355,7 +384,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = doAssert m.forwTypeCache[sig] == result else: internalError(m.config, "getTypeForward(" & $typ.kind & ')') -proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): Rope = +proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TypeDescKind): Rope = ## like getTypeDescAux but creates only a *weak* dependency. In other words ## we know we only need a pointer to it so we only generate a struct forward ## declaration: @@ -386,34 +415,30 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): R m.typeCache[sig] = result #echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s appcg(m, m.s[cfsTypes], - "struct $1 {$N" & - " NI len; $1_Content* p;$N" & - "};$N", [result]) + "struct $1 {\n" & + " NI len; $1_Content* p;\n" & + "};\n", [result]) + pushType(m, t) else: result = getTypeForward(m, t, sig) & seqStar(m) - pushType(m, t) + pushType(m, t) else: result = getTypeDescAux(m, t, check, kind) proc getSeqPayloadType(m: BModule; t: PType): Rope = var check = initIntSet() - result = getTypeDescWeak(m, t, check, skParam) & "_Content" + result = getTypeDescWeak(m, t, check, dkParam) & "_Content" #result = getTypeForward(m, t, hashType(t)) & "_Content" proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) = let sig = hashType(t, m.config) let result = cacheGetType(m.typeCache, sig) if result == "": - discard getTypeDescAux(m, t, check, skVar) + discard getTypeDescAux(m, t, check, dkVar) else: - # little hack for now to prevent multiple definitions of the same - # Seq_Content: - appcg(m, m.s[cfsTypes], """$N -$3ifndef $2_Content_PP -$3define $2_Content_PP -struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE];}; -$3endif$N - """, [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, skVar), result, rope"#"]) + appcg(m, m.s[cfsTypes], """ +struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE]; }; +""", [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, dkVar), result]) proc paramStorageLoc(param: PSym): TStorageLoc = if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin { @@ -422,35 +447,167 @@ proc paramStorageLoc(param: PSym): TStorageLoc = else: result = OnUnknown -proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, +macro unrollChars(x: static openArray[char], name, body: untyped) = + result = newStmtList() + for a in x: + result.add(newBlockStmt(newStmtList( + newConstStmt(name, newLit(a)), + copy body + ))) + +proc multiFormat*(frmt: var string, chars : static openArray[char], args: openArray[seq[string]]) = + var res : string + unrollChars(chars, c): + res = "" + let arg = args[find(chars, c)] + var i = 0 + var num = 0 + while i < frmt.len: + if frmt[i] == c: + inc(i) + case frmt[i] + of c: + res.add(c) + inc(i) + of '0'..'9': + var j = 0 + while true: + j = j * 10 + ord(frmt[i]) - ord('0') + inc(i) + if i >= frmt.len or frmt[i] notin {'0'..'9'}: break + num = j + if j > high(arg) + 1: + raiseAssert "invalid format string: " & frmt + else: + res.add(arg[j-1]) + else: + raiseAssert "invalid format string: " & frmt + var start = i + while i < frmt.len: + if frmt[i] != c: inc(i) + else: break + if i - 1 >= start: + res.add(substr(frmt, start, i - 1)) + frmt = res + +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) = + weakDep=false;) = + let t = prc.typ + let isCtor = sfConstructor in prc.flags + 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" + else: + if rettype == "": + 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 this = t.n[1].sym + fillParamName(m, this) + fillLoc(this.loc, locParam, t.n[1], + this.paramStorageLoc) + if this.typ.kind == tyPtr: + this.loc.r = "this" + else: + this.loc.r = "(*this)" + names.add this.loc.r + types.add getTypeDescWeak(m, this.typ, check, dkParam) + + let firstParam = if isCtor: 1 else: 2 + for i in firstParam..raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name]) hasField = true else: - appcg(m, result, " {$n $1 Sup;$n", - [getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, skField)]) + appcg(m, result, " {$n $1 Sup;$n", [baseType]) hasField = true else: result.addf(" {$n", [name]) - let desc = getRecordFields(m, typ, check) - if desc == "" and not hasField: - result.addf("char dummy;$n", []) +proc getRecordDesc(m: BModule; typ: PType, name: Rope, + check: var IntSet): Rope = + # declare the record: + var hasField = false + var structOrUnion: string + if tfPacked in typ.flags: + if hasAttribute in CC[m.config.cCompiler].props: + structOrUnion = structOrUnion(typ) & " __attribute__((__packed__))" + else: + structOrUnion = "#pragma pack(push, 1)\L" & structOrUnion(typ) else: - result.add(desc) - result.add("};\L") + structOrUnion = structOrUnion(typ) + var baseType: string = "" + if typ[0] != nil: + baseType = getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, dkField) + if typ.sym == nil or sfCodegenDecl notin typ.sym.flags: + result = structOrUnion & " " & name + result.add(getRecordDescAux(m, typ, name, baseType, check, hasField)) + let desc = getRecordFields(m, typ, check) + if not hasField and typ.itemId notin m.g.graph.memberProcsPerType: + if desc == "": + result.add("\tchar dummy;\n") + elif typ.len == 1 and typ.n[0].kind == nkSym: + let field = typ.n[0].sym + let fieldType = field.typ.skipTypes(abstractInst) + if fieldType.kind == tyUncheckedArray: + result.add("\tchar dummy;\n") + result.add(desc) + else: + result.add(desc) + result.add("};\L") + else: + let desc = getRecordFields(m, typ, check) + result = runtimeFormat(typ.sym.cgDeclFrmt, [name, desc, baseType]) if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props: result.add "#pragma pack(pop)\L" -proc getTupleDesc(m: BModule, typ: PType, name: Rope, +proc getTupleDesc(m: BModule; typ: PType, name: Rope, check: var IntSet): Rope = result = "$1 $2 {$n" % [structOrUnion(typ), name] var desc: Rope = "" for i in 0..= typ.len: - doAssert false, "invalid apostrophe type parameter index" + raiseAssert "invalid apostrophe type parameter index" result = typ[idx] for i in 1..stars: @@ -669,9 +855,9 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType = result = if result.kind == tyGenericInst: result[1] else: result.elemType -proc getOpenArrayDesc(m: BModule, t: PType, check: var IntSet; kind: TSymKind): Rope = +proc getOpenArrayDesc(m: BModule; t: PType, check: var IntSet; kind: TypeDescKind): Rope = let sig = hashType(t, m.config) - if kind == skParam: + if kind == dkParam: result = getTypeDescWeak(m, t[0], check, kind) & "*" else: result = cacheGetType(m.typeCache, sig) @@ -682,9 +868,8 @@ proc getOpenArrayDesc(m: BModule, t: PType, check: var IntSet; kind: TSymKind): m.s[cfsTypes].addf("typedef struct {$n$2* Field0;$nNI Field1;$n} $1;$n", [result, elemType]) -proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope = +proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope = # returns only the type's name - var t = origTyp.skipTypes(irrelevantForBackend-{tyOwned}) if containsOrIncl(check, t.id): if not (isImportedCppType(origTyp) or isImportedCppType(t)): @@ -696,6 +881,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin 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) @@ -703,6 +889,8 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin result = getTypePre(m, t, sig) if result != "" and t.kind != tyOpenArray: excl(check, t.id) + if kind == dkRefParam or kind == dkRefGenericParam and origTyp.kind == tyGenericInst: + result.add("&") return case t.kind of tyRef, tyPtr, tyVar, tyLent: @@ -710,7 +898,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin compileToCpp(m): "&" else: "*" var et = origTyp.skipTypes(abstractInst).lastSon var etB = et.skipTypes(abstractInst) - if mapType(m.config, t, kind) == ctPtrToArray and (etB.kind != tyOpenArray or kind == skParam): + if mapType(m.config, t, kind == dkParam) == ctPtrToArray and (etB.kind != tyOpenArray or kind == dkParam): if etB.kind == tySet: et = getSysType(m.g.graph, unknownLineInfo, tyUInt8) else: @@ -774,7 +962,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin 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! @@ -851,7 +1039,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin 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 @@ -915,8 +1103,9 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin result = "" # fixes bug #145: excl(check, t.id) - -proc getTypeDesc(m: BModule, typ: PType; kind = skParam): Rope = + + +proc getTypeDesc(m: BModule; typ: PType; kind = dkParam): Rope = var check = initIntSet() result = getTypeDescAux(m, typ, check, kind) @@ -926,11 +1115,11 @@ type clHalfWithEnv, ## fn(args, void* env) type with trailing 'void* env' parameter clFull ## struct {fn(args, void* env), env} -proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): Rope = +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: @@ -950,35 +1139,98 @@ proc finishTypeDescriptions(m: BModule) = if optSeqDestructors in m.config.globalOptions and t.skipTypes(abstractInst).kind == tySequence: seqV2ContentType(m, t, check) else: - discard getTypeDescAux(m, t, check, skParam) + discard getTypeDescAux(m, t, check, dkParam) inc(i) m.typeStack.setLen 0 -template cgDeclFrmt*(s: PSym): string = - s.constraint.strVal - -proc isReloadable(m: BModule, prc: PSym): bool = +proc isReloadable(m: BModule; prc: PSym): bool = return m.hcrOn and sfNonReloadable notin prc.flags -proc isNonReloadable(m: BModule, prc: PSym): bool = +proc isNonReloadable(m: BModule; prc: PSym): bool = return m.hcrOn and sfNonReloadable in prc.flags -proc genProcHeader(m: BModule, prc: PSym; result: var Rope; asPtr: bool = false) = +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: + discard scanf(afterParams, "->$s$* ", retType) + + params = "(" & params & ")" + +proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl : bool = false) = + assert sfCppMember * prc.flags != {} + let isCtor = sfConstructor in prc.flags + 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[0] + else: + 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, 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: + fnConst = " const" + if isFwdDecl: + if isVirtual: + rettype = "virtual " & rettype + if isOverride: + override = " override" + superCall = "" + else: + 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 - genProcParams(m, prc.typ, rettype, params, check) + 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 let asPtrStr = rope(if asPtr: "_PTR" else: "") var name = prc.loc.r - if isReloadable(m, prc) and not asPtr: + if not asPtr and isReloadable(m, prc): 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 " @@ -995,14 +1247,15 @@ proc genProcHeader(m: BModule, prc: PSym; result: var Rope; asPtr: bool = false) let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name result.add runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params]) + # ------------------ type info generation ------------------------------------- -proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope +proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope proc getNimNode(m: BModule): Rope = result = "$1[$2]" % [m.typeNodesName, rope(m.typeNodes)] inc(m.typeNodes) -proc tiNameForHcr(m: BModule, name: Rope): Rope = +proc tiNameForHcr(m: BModule; name: Rope): Rope = return if m.hcrOn: "(*".rope & name & ")" else: name proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; @@ -1020,7 +1273,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; if tfIncompleteStruct in typ.flags: size = rope"void*" else: - size = getTypeDesc(m, origType, skVar) + size = getTypeDesc(m, origType, dkVar) m.s[cfsTypeInit3].addf( "$1.size = sizeof($2);$n$1.align = NIM_ALIGNOF($2);$n$1.kind = $3;$n$1.base = $4;$n", [nameHcr, size, rope(nimtypeKind), base] @@ -1028,7 +1281,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; # compute type flags for GC optimization var flags = 0 if not containsGarbageCollectedRef(typ): flags = flags or 1 - if not canFormAcycle(typ): flags = flags or 2 + if not canFormAcycle(m.g.graph, typ): flags = flags or 2 #else echo("can contain a cycle: " & typeToString(typ)) if flags != 0: m.s[cfsTypeInit3].addf("$1.flags = $2;$n", [nameHcr, rope(flags)]) @@ -1051,7 +1304,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; else: m.s[cfsStrData].addf("N_LIB_PRIVATE TNimType $1;$n", [name]) -proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope; +proc genTypeInfoAux(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) = var base: Rope if typ.len > 0 and typ.lastSon != nil: @@ -1065,7 +1318,7 @@ proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope; base = rope("0") genTypeInfoAuxBase(m, typ, origType, name, base, info) -proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope = +proc discriminatorTableName(m: BModule; objtype: PType, d: PSym): Rope = # bugfix: we need to search the type that contains the discriminator: var objtype = objtype.skipTypes(abstractPtrs) while lookupInRecord(objtype.n, d.name) == nil: @@ -1076,12 +1329,12 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope = proc rope(arg: Int128): Rope = rope($arg) -proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope = +proc discriminatorTableDecl(m: BModule; objtype: PType, d: PSym): Rope = cgsym(m, "TNimNode") var tmp = discriminatorTableName(m, objtype, d) result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)] -proc genTNimNodeArray(m: BModule, name: Rope, size: Rope) = +proc genTNimNodeArray(m: BModule; name: Rope, size: Rope) = if m.hcrOn: m.s[cfsData].addf("static TNimNode** $1;$n", [name]) m.hcrCreateTypeInfosProc.addf("\thcrRegisterGlobal($3, \"$1\", sizeof(TNimNode*) * $2, NULL, (void**)&$1);$n", @@ -1089,7 +1342,7 @@ proc genTNimNodeArray(m: BModule, name: Rope, size: Rope) = else: m.s[cfsTypeInit1].addf("static TNimNode* $1[$2];$n", [name, size]) -proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; +proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope; info: TLineInfo) = case n.kind of nkRecList: @@ -1118,7 +1371,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; m.s[cfsTypeInit3].addf("$1.kind = 3;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n" & "$1.sons = &$6[0];$n" & - "$1.len = $7;$n", [expr, getTypeDesc(m, origType, skVar), field.loc.r, + "$1.len = $7;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.r, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s), tmp, rope(L)]) @@ -1155,11 +1408,11 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; internalError(m.config, n.info, "genObjectFields") m.s[cfsTypeInit3].addf("$1.kind = 1;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & - "$1.name = $5;$n", [expr, getTypeDesc(m, origType, skVar), + "$1.name = $5;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.r, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)]) else: internalError(m.config, n.info, "genObjectFields") -proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) = +proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) = if typ.kind == tyObject: if incompleteType(typ): localError(m.config, info, "request for RTTI generation for incomplete object: " & @@ -1177,7 +1430,7 @@ proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo t.flags.incl tfObjHasKids t = t[0] -proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) = +proc genTupleInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) = genTypeInfoAuxBase(m, typ, typ, name, rope("0"), info) var expr = getNimNode(m) if typ.len > 0: @@ -1191,7 +1444,7 @@ proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) "$1.offset = offsetof($2, Field$3);$n" & "$1.typ = $4;$n" & "$1.name = \"Field$3\";$n", - [tmp2, getTypeDesc(m, origType, skVar), rope(i), genTypeInfoV1(m, a, info)]) + [tmp2, getTypeDesc(m, origType, dkVar), rope(i), genTypeInfoV1(m, a, info)]) m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", [expr, rope(typ.len), tmp]) else: @@ -1199,7 +1452,7 @@ proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) [expr, rope(typ.len)]) m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), expr]) -proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = +proc genEnumInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) = # Type information for enumerations is quite heavy, so we do some # optimizations here: The ``typ`` field is never set, as it is redundant # anyway. We generate a cstring array and a loop over it. Exceptional @@ -1207,7 +1460,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: let objDisplay = genDisplay(m, t, objDepth) let objDisplayStore = getTempName(m) - m.s[cfsVars].addf("static $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), skVar), objDisplayStore, rope(objDepth+1), objDisplay]) + m.s[cfsVars].addf("static $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), dkVar), objDisplayStore, rope(objDepth+1), objDisplay]) addf(typeEntry, "$1.display = $2;$n", [name, rope(objDisplayStore)]) m.s[cfsTypeInit3].add typeEntry @@ -1397,7 +1702,7 @@ proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineIn m.s[cfsStrData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name]) var flags = 0 - if not canFormAcycle(t): flags = flags or 1 + if not canFormAcycle(m.g.graph, t): flags = flags or 1 var typeEntry = newRopeAppender() addf(typeEntry, "N_LIB_PRIVATE TNimTypeV2 $1 = {", [name]) @@ -1416,7 +1721,7 @@ proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineIn if objDepth >= 0: let objDisplay = genDisplay(m, t, objDepth) let objDisplayStore = getTempName(m) - m.s[cfsVars].addf("static NIM_CONST $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), skVar), objDisplayStore, rope(objDepth+1), objDisplay]) + m.s[cfsVars].addf("static NIM_CONST $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), dkVar), objDisplayStore, rope(objDepth+1), objDisplay]) addf(typeEntry, ", .display = $1", [rope(objDisplayStore)]) if isDefined(m.config, "nimTypeNames"): var typeName: Rope @@ -1434,7 +1739,7 @@ proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineIn if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions: discard genTypeInfoV1(m, t, info) -proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = +proc genTypeInfoV2(m: BModule; t: PType; info: TLineInfo): Rope = let origType = t # distinct types can have their own destructors var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses - {tyDistinct}) @@ -1467,7 +1772,7 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = return prefixTI.rope & result & ")".rope m.g.typeInfoMarkerV2[sig] = (str: result, owner: owner) - if m.compileToCpp: + if m.compileToCpp or m.hcrOn: genTypeInfoV2OldImpl(m, t, origType, result, info) else: genTypeInfoV2Impl(m, t, origType, result, info) @@ -1509,7 +1814,7 @@ proc typeToC(t: PType): string = # be clashes with our special meanings result.addInt ord(c) -proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = +proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope = let origType = t var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) @@ -1610,11 +1915,20 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = result = prefixTI.rope & result & ")".rope -proc genTypeSection(m: BModule, n: PNode) = - discard - -proc genTypeInfo*(config: ConfigRef, m: BModule, t: PType; info: TLineInfo): Rope = +proc genTypeInfo*(config: ConfigRef, m: BModule; t: PType; info: TLineInfo): Rope = if optTinyRtti in config.globalOptions: result = genTypeInfoV2(m, t, info) else: result = genTypeInfoV1(m, t, info) + +proc genTypeSection(m: BModule, n: PNode) = + var intSet = initIntSet() + for i in 0..= start: - strLit.add(substr(frmt, start, i - 1)) + else: + strLit.add(frmt[i]) + inc(i) flushStrLit() result.add newCall(ident"rope", resVar) @@ -273,7 +267,16 @@ proc safeLineNm(info: TLineInfo): int = proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) = assert line >= 0 if optLineDir in conf.options and line > 0: - r.addf("$N#line $2 $1$N", + r.addf("\n#line $2 $1\n", + [rope(makeSingleLineCString(filename)), rope(line)]) + +proc genCLineDir(r: var Rope, filename: string, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) = + assert line >= 0 + if optLineDir in p.config.options and line > 0: + if lastFileIndex == info.fileIndex: + r.addf("\n#line $1\n", [rope(line)]) + else: + r.addf("\n#line $2 $1\n", [rope(makeSingleLineCString(filename)), rope(line)]) proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) = @@ -286,17 +289,33 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool = p.lastLineInfo.line = info.line p.lastLineInfo.fileIndex = info.fileIndex result = true + else: + result = false + +proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) = + if optLineDir in conf.options: + let lastFileIndex = p.lastLineInfo.fileIndex + if freshLineInfo(p, info): + genCLineDir(r, toFullPath(conf, info), info.safeLineNm, p, info, lastFileIndex) proc genLineDir(p: BProc, t: PNode) = + if p == p.module.preInitProc: return let line = t.info.safeLineNm if optEmbedOrigSrc in p.config.globalOptions: p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L") - genCLineDir(p.s(cpsStmts), t.info, p.config) + let lastFileIndex = p.lastLineInfo.fileIndex + let freshLine = freshLineInfo(p, t.info) + if freshLine: + genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p, t.info, lastFileIndex) if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx: - if freshLineInfo(p, t.info): - linefmt(p, cpsStmts, "nimln_($1, $2);$n", + if freshLine: + if lastFileIndex == t.info.fileIndex: + linefmt(p, cpsStmts, "nimln_($1);", + [line]) + else: + linefmt(p, cpsStmts, "nimlf_($1, $2);", [line, quotedFilename(p.config, t.info)]) proc accessThreadLocalVar(p: BProc, s: PSym) @@ -356,19 +375,19 @@ template mapTypeChooser(n: PNode): TSymKind = template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode) proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) = - if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray: + if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray: result.add "(&" & a.r & ")" else: result.add a.r proc addrLoc(conf: ConfigRef; a: TLoc): Rope = - if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray: + if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray: result = "(&" & a.r & ")" else: result = a.r proc byRefLoc(p: BProc; a: TLoc): Rope = - if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a)) != ctArray and not + if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a) == skParam) != ctArray and not p.module.compileToCpp: result = "(&" & a.r & ")" else: @@ -414,13 +433,13 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc, linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV1(p.module, t, a.lode.info)]) of frEmbedded: if optTinyRtti in p.config.globalOptions: - var tmp: TLoc + var tmp: TLoc = default(TLoc) if mode == constructRefObj: let objType = t.skipTypes(abstractInst+{tyRef}) rawConstExpr(p, newNodeIT(nkType, a.lode.info, objType), tmp) linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n", - [rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType, mapTypeChooser(a))]) + [rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType, descKindFromSymKind mapTypeChooser(a))]) else: rawConstExpr(p, newNodeIT(nkType, a.lode.info, t), tmp) genAssignment(p, a, tmp, {}) @@ -462,8 +481,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)]) elif not isComplexValueType(typ): if containsGcRef: - var nilLoc: TLoc - initLoc(nilLoc, locTemp, loc.lode, OnStack) + var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack) nilLoc.r = rope("NIM_NIL") genRefAssign(p, loc, nilLoc) else: @@ -482,7 +500,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = # so we use getTypeDesc here rather than rdLoc(loc) linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", [addrLoc(p.config, loc), - getTypeDesc(p.module, loc.t, mapTypeChooser(loc))]) + getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc))]) # XXX: We can be extra clever here and call memset only # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, constructObj) @@ -493,20 +511,19 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) = linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)]) elif not isComplexValueType(typ): if containsGarbageCollectedRef(loc.t): - var nilLoc: TLoc - initLoc(nilLoc, locTemp, loc.lode, OnStack) + var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack) nilLoc.r = rope("NIM_NIL") genRefAssign(p, loc, nilLoc) else: linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc), - getTypeDesc(p.module, typ, mapTypeChooser(loc))]) + getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))]) else: if not isTemp or containsGarbageCollectedRef(loc.t): # don't use nimZeroMem for temporary values for performance if we can # avoid it: if not isOrHasImportedCppType(typ): linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", - [addrLoc(p.config, loc), getTypeDesc(p.module, typ, mapTypeChooser(loc))]) + [addrLoc(p.config, loc), getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))]) genObjectInit(p, cpsStmts, loc.t, loc, constructObj) proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = @@ -521,17 +538,14 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = if not immediateAsgn: constructLoc(p, v.loc) -proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = +proc getTemp(p: BProc, t: PType, needsInit=false): TLoc = inc(p.labels) - result.r = "T" & rope(p.labels) & "_" + result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, + storage: OnStack, flags: {}) if p.module.compileToCpp and isOrHasImportedCppType(t): - linefmt(p, cpsLocals, "$1 $2{};$n", [getTypeDesc(p.module, t, skVar), result.r]) + linefmt(p, cpsLocals, "$1 $2{};$n", [getTypeDesc(p.module, t, dkVar), result.r]) else: - linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, skVar), result.r]) - result.k = locTemp - result.lode = lodeTyp t - result.storage = OnStack - result.flags = {} + linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.r]) constructLoc(p, result, not needsInit) when false: # XXX Introduce a compiler switch in order to detect these easily. @@ -542,25 +556,21 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = echo "ENORMOUS TEMPORARY! ", p.config $ p.lastLineInfo writeStackTrace() -proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) = +proc getTempCpp(p: BProc, t: PType, value: Rope): TLoc = inc(p.labels) - result.r = "T" & rope(p.labels) & "_" - linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, skVar), result.r, value]) - result.k = locTemp - result.lode = lodeTyp t - result.storage = OnStack - result.flags = {} + result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t, + storage: OnStack, flags: {}) + linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, dkVar), result.r, value]) -proc getIntTemp(p: BProc, result: var TLoc) = +proc getIntTemp(p: BProc): TLoc = inc(p.labels) - result.r = "T" & rope(p.labels) & "_" + result = TLoc(r: "T" & rope(p.labels) & "_", k: locTemp, + storage: OnStack, lode: lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt), + flags: {}) linefmt(p, cpsLocals, "NI $1;$n", [result.r]) - result.k = locTemp - result.storage = OnStack - result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt) - result.flags = {} proc localVarDecl(p: BProc; n: PNode): Rope = + result = "" let s = n.sym if s.loc.k == locNone: fillLocalName(p, s) @@ -569,10 +579,10 @@ proc localVarDecl(p: BProc; n: PNode): Rope = if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: result.addf("NIM_ALIGN($1) ", [rope(s.alignment)]) - genCLineDir(result, n.info, p.config) + genCLineDir(result, p, n.info, p.config) - result.add getTypeDesc(p.module, s.typ, skVar) - if s.constraint.isNil: + result.add getTypeDesc(p.module, s.typ, dkVar) + if sfCodegenDecl notin s.flags: if sfRegister in s.flags: result.add(" register") #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: # decl.add(" GC_GUARD") @@ -587,7 +597,7 @@ proc assignLocalVar(p: BProc, n: PNode) = #assert(s.loc.k == locNone) # not yet assigned # this need not be fulfilled for inline procs; they are regenerated # for each module that uses them! - let nl = if optLineDir in p.config.options: "" else: "\L" + let nl = if optLineDir in p.config.options: "" else: "\n" let decl = localVarDecl(p, n) & (if p.module.compileToCpp and isOrHasImportedCppType(n.typ): "{};" else: ";") & nl line(p, cpsLocals, decl) @@ -601,6 +611,40 @@ proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool = # and s.owner.kind == skModule # owner isn't always a module (global pragma on local var) # and s.loc.k == locGlobalVar # loc isn't always initialized when this proc is used +proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) = + let s = n.sym + if sfCodegenDecl notin s.flags: + if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: + decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)] + if p.hcrOn: decl.add("static ") + elif sfImportc in s.flags: decl.add("extern ") + elif lfExportLib in s.loc.flags: decl.add("N_LIB_EXPORT_VAR ") + else: decl.add("N_LIB_PRIVATE ") + if s.kind == skLet and value != "": decl.add("NIM_CONST ") + decl.add(td) + if p.hcrOn: decl.add("*") + if sfRegister in s.flags: decl.add(" register") + if sfVolatile in s.flags: decl.add(" volatile") + if sfNoalias in s.flags: decl.add(" NIM_NOALIAS") + else: + if value != "": + decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value]) + else: + decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) + +proc genCppVarForCtor(p: BProc, v: PSym; vn, value: 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 = "" + 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) + p.module.s[cfsVars].add decl + proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = let s = n.sym if s.loc.k == locNone: @@ -626,20 +670,9 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = internalError(p.config, n.info, ".threadvar variables cannot have a value") else: var decl: Rope = "" - var td = getTypeDesc(p.module, s.loc.t, skVar) + let td = getTypeDesc(p.module, s.loc.t, dkVar) + genGlobalVarDecl(p, n, td, value, decl) if s.constraint.isNil: - if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: - decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)] - if p.hcrOn: decl.add("static ") - elif sfImportc in s.flags: decl.add("extern ") - elif lfExportLib in s.loc.flags: decl.add("N_LIB_EXPORT_VAR ") - else: decl.add("N_LIB_PRIVATE ") - if s.kind == skLet and value != "": decl.add("NIM_CONST ") - decl.add(td) - if p.hcrOn: decl.add("*") - if sfRegister in s.flags: decl.add(" register") - if sfVolatile in s.flags: decl.add(" volatile") - if sfNoalias in s.flags: decl.add(" NIM_NOALIAS") if value != "": if p.module.compileToCpp and value.startsWith "{{}": # TODO: taking this branch, re"\{\{\}(,\s\{\})*\}" might be emitted, resulting in @@ -660,11 +693,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]) - else: - if value != "": - decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value]) - else: - decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r]) + p.module.s[cfsVars].add(decl) if p.withinLoop > 0 and value == "": # fixes tests/run/tzeroarray: @@ -685,7 +714,7 @@ proc getLabel(p: BProc): TLabel = result = "LA" & rope(p.labels) & "_" proc fixLabel(p: BProc, labl: TLabel) = - lineF(p, cpsStmts, "$1: ;$n", [labl]) + p.s(cpsStmts).add("$1: ;$n" % [labl]) proc genVarPrototype(m: BModule, n: PNode) proc requestConstImpl(p: BProc, sym: PSym) @@ -698,12 +727,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" @@ -727,8 +756,12 @@ $1define nimfr_(proc, file) \ struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename;NI len;VarSlot s[slots];} FR_; \ FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; #nimFrame((TFrame*)&FR_); - $1define nimln_(n, file) \ + $1define nimln_(n) \ + FR_.line = n; + + $1define nimlf_(n, file) \ FR_.line = n; FR_.filename = file; + """ if p.module.s[cfsFrameDefines].len == 0: appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"]) @@ -790,11 +823,10 @@ 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, skVar), rdLoc(dest)]) + [getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)]) expr(p, lib.path, dest) m.s[cfsVars].add(p.s(cpsLocals)) @@ -826,15 +858,14 @@ 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..= prc.ast.len: internalError(m.config, prc.info, "proc has no result symbol") let resNode = prc.ast[resultPos] let res = resNode.sym # get result symbol - if not isInvalidReturnType(m.config, prc.typ): + if not isInvalidReturnType(m.config, prc.typ) and sfConstructor notin prc.flags: if sfNoInit in prc.flags: incl(res.flags, sfNoInit) if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil): var decl = localVarDecl(p, resNode) - var a: TLoc - initLocExprSingleUse(p, val, a) + var a: TLoc = initLocExprSingleUse(p, val) linefmt(p, cpsStmts, "$1 = $2;$n", [decl, rdLoc(a)]) else: # declare the result symbol: @@ -1128,6 +1168,8 @@ proc genProcAux*(m: BModule, prc: PSym) = assert(res.loc.r != "") initLocalVar(p, res, immediateAsgn=false) returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)]) + elif sfConstructor in prc.flags: + fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap) else: fillResult(p.config, resNode, prc.typ) assignParam(p, res, prc.typ[0]) @@ -1152,13 +1194,15 @@ proc genProcAux*(m: BModule, prc: PSym) = closureSetup(p, prc) genProcBody(p, procBody) - var generatedProc: Rope + prc.info = tmpInfo + + var generatedProc: Rope = "" generatedProc.genCLineDir prc.info, m.config if isNoReturn(p.module, prc): - if hasDeclspec in extccomp.CC[p.config.cCompiler].props: + if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember: header = "__declspec(noreturn) " & header if sfPure in prc.flags: - if hasDeclspec in extccomp.CC[p.config.cCompiler].props: + if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember: header = "__declspec(naked) " & header generatedProc.add ropecg(p.module, "$1 {$n$2$3$4}$N$N", [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]) @@ -1203,7 +1247,7 @@ proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} = proc genProcPrototype(m: BModule, sym: PSym) = useHeader(m, sym) - if lfNoDecl in sym.loc.flags: return + if lfNoDecl in sym.loc.flags or sfCppMember * sym.flags != {}: return if lfDynamicLib in sym.loc.flags: if sym.itemId.module != m.module.position and not containsOrIncl(m.declaredThings, sym.id): @@ -1239,6 +1283,20 @@ proc genProcNoForward(m: BModule, prc: PSym) = if lfNoDecl in prc.loc.flags: fillProcLoc(m, prc.ast[namePos]) genProcPrototype(m, prc) + elif lfDynamicLib in prc.loc.flags: + var q = findPendingModule(m, prc) + fillProcLoc(q, prc.ast[namePos]) + genProcPrototype(m, prc) + if q != nil and not containsOrIncl(q.declaredThings, prc.id): + symInDynamicLib(q, prc) + # register the procedure even though it is in a different dynamic library and will not be + # reloadable (and has no _actual suffix) - other modules will need to be able to get it through + # the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded) + if isReloadable(q, prc): + q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n", + [prc.loc.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)]) + else: + symInDynamicLibPartial(m, prc) elif prc.typ.callConv == ccInline: # We add inline procs to the calling module to enable C based inlining. # This also means that a check with ``q.declaredThings`` is wrong, we need @@ -1257,20 +1315,6 @@ proc genProcNoForward(m: BModule, prc: PSym) = # prc.loc.r = mangleName(m, prc) genProcPrototype(m, prc) genProcAux(m, prc) - elif lfDynamicLib in prc.loc.flags: - var q = findPendingModule(m, prc) - fillProcLoc(q, prc.ast[namePos]) - genProcPrototype(m, prc) - if q != nil and not containsOrIncl(q.declaredThings, prc.id): - symInDynamicLib(q, prc) - # register the procedure even though it is in a different dynamic library and will not be - # reloadable (and has no _actual suffix) - other modules will need to be able to get it through - # the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded) - if isReloadable(q, prc): - q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n", - [prc.loc.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)]) - else: - symInDynamicLibPartial(m, prc) elif sfImportc notin prc.flags: var q = findPendingModule(m, prc) fillProcLoc(q, prc.ast[namePos]) @@ -1334,14 +1378,14 @@ proc genVarPrototype(m: BModule, n: PNode) = if sym.owner.id != m.module.id: # else we already have the symbol generated! assert(sym.loc.r != "") + incl(m.declaredThings, sym.id) if sfThread in sym.flags: declareThreadVar(m, sym, true) else: - incl(m.declaredThings, sym.id) if sym.kind in {skLet, skVar, skField, skForVar} and sym.alignment > 0: m.s[cfsVars].addf "NIM_ALIGN($1) ", [rope(sym.alignment)] m.s[cfsVars].add(if m.hcrOn: "static " else: "extern ") - m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, skVar)) + m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, dkVar)) if m.hcrOn: m.s[cfsVars].add("*") if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*") if sfRegister in sym.flags: m.s[cfsVars].add(" register") @@ -1350,7 +1394,7 @@ proc genVarPrototype(m: BModule, n: PNode) = m.s[cfsVars].addf(" $1;$n", [sym.loc.r]) if m.hcrOn: m.initProc.procSec(cpsLocals).addf( "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, - getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(m, sym)]) + getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(m, sym)]) proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} = result.addf("#define NIM_INTBITS $1\L", [ @@ -1382,17 +1426,21 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope = ## Returns a mangled module name. + result = "" result.add mangleModuleName(conf, filename).mangle proc getSomeNameForModule(m: BModule): Rope = ## Returns a mangled module name. assert m.module.kind == skModule assert m.module.owner.kind == skPackage + result = "" result.add mangleModuleName(m.g.config, m.filename).mangle proc getSomeInitName(m: BModule, suffix: string): Rope = if not m.hcrOn: result = getSomeNameForModule(m) + else: + result = "" result.add suffix proc getInitName(m: BModule): Rope = @@ -1411,10 +1459,10 @@ proc genMainProc(m: BModule) = ## this function is called in cgenWriteModules after all modules are closed, ## it means raising dependency on the symbols is too late as it will not propagate ## into other modules, only simple rope manipulations are allowed - - var preMainCode: Rope + var preMainCode: Rope = "" if m.hcrOn: proc loadLib(handle: string, name: string): Rope = + result = "" let prc = magicsys.getCompilerProc(m.g.graph, name) assert prc != nil let n = newStrNode(nkStrLit, prc.annex.path.strVal) @@ -1426,20 +1474,23 @@ proc genMainProc(m: BModule) = [handle, strLit]) preMainCode.add(loadLib("hcr_handle", "hcrGetProc")) - preMainCode.add("\tvoid* rtl_handle;\L") - preMainCode.add(loadLib("rtl_handle", "nimGC_setStackBottom")) - preMainCode.add(hcrGetProcLoadCode(m, "nimGC_setStackBottom", "nimrtl_", "rtl_handle", "nimGetProcAddr")) - preMainCode.add("\tinner = PreMain;\L") - preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L") - preMainCode.add("\t(*inner)();\L") + if m.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: + preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix]) + else: + preMainCode.add("\tvoid* rtl_handle;\L") + preMainCode.add(loadLib("rtl_handle", "nimGC_setStackBottom")) + preMainCode.add(hcrGetProcLoadCode(m, "nimGC_setStackBottom", "nimrtl_", "rtl_handle", "nimGetProcAddr")) + preMainCode.add("\tinner = $1PreMain;\L" % [rope m.config.nimMainPrefix]) + preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L") + preMainCode.add("\t(*inner)();\L") else: preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix]) - var posixCmdLine: Rope + var posixCmdLine: Rope = "" if optNoMain notin m.config.globalOptions: - posixCmdLine.add "\tN_LIB_PRIVATE int cmdCount;\L" - posixCmdLine.add "\tN_LIB_PRIVATE char** cmdLine;\L" - posixCmdLine.add "\tN_LIB_PRIVATE char** gEnv;\L" + posixCmdLine.add "N_LIB_PRIVATE int cmdCount;\L" + posixCmdLine.add "N_LIB_PRIVATE char** cmdLine;\L" + posixCmdLine.add "N_LIB_PRIVATE char** gEnv;\L" const # The use of a volatile function pointer to call Pre/NimMainInner @@ -1452,15 +1503,15 @@ proc genMainProc(m: BModule) = "}$N$N" & "$4" & "N_LIB_PRIVATE void $3PreMain(void) {$N" & - "\t##if $5$N" & # 1 for volatile call, 0 for non-volatile + "##if $5$N" & # 1 for volatile call, 0 for non-volatile "\tvoid (*volatile inner)(void);$N" & "\tinner = $3PreMainInner;$N" & "$1" & "\t(*inner)();$N" & - "\t##else$N" & + "##else$N" & "$1" & "\t$3PreMainInner();$N" & - "\t##endif$N" & + "##endif$N" & "}$N$N" MainProcs = @@ -1475,17 +1526,17 @@ proc genMainProc(m: BModule) = NimMainProc = "N_CDECL(void, $5NimMain)(void) {$N" & - "\t##if $6$N" & # 1 for volatile call, 0 for non-volatile + "##if $6$N" & # 1 for volatile call, 0 for non-volatile "\tvoid (*volatile inner)(void);$N" & "$4" & "\tinner = $5NimMainInner;$N" & "$2" & "\t(*inner)();$N" & - "\t##else$N" & + "##else$N" & "$4" & "$2" & "\t$5NimMainInner();$N" & - "\t##endif$N" & + "##endif$N" & "}$N$N" NimMainBody = NimMainInner & NimMainProc @@ -1516,7 +1567,7 @@ proc genMainProc(m: BModule) = WinCDllMain = "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" & " LPVOID lpvReserved) {$N" & - "\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" & + "\tif (fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "\t}$N" & "\treturn 1;$N}$N$N" PosixNimDllMain = WinNimDllMain @@ -1550,11 +1601,11 @@ proc genMainProc(m: BModule) = m.includeHeader("") let initStackBottomCall = - if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcOrc}: "".rope + if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcAtomicArc, gcOrc}: "".rope else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []) inc(m.labels) - let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcOrc}: "1" else: "0" + let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: "1" else: "0" appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine, isVolatile]) if m.config.target.targetOS == osWindows and @@ -1694,7 +1745,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) = if sfSystemModule in m.module.flags: if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", [])) - if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}: + if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])) if m.s[cfsInitProc].len > 0: @@ -1745,10 +1796,10 @@ proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): R prc.typ.sym = nil if not containsOrIncl(m.declaredThings, prc.id): - m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, skVar)]) + m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, dkVar)]) result = "\t$1 = ($2) $3($4, $5);$n" % - [tmp, getTypeDesc(m, prc.typ, skVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)] + [tmp, getTypeDesc(m, prc.typ, dkVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)] proc genInitCode(m: BModule) = ## this function is called in cgenWriteModules after all modules are closed, @@ -1824,13 +1875,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", []) @@ -1887,8 +1938,6 @@ proc genModule(m: BModule, cfile: Cfile): Rope = openNamespaceNim(m.config.cppCustomNamespace, result) if m.s[cfsFrameDefines].len > 0: result.add(m.s[cfsFrameDefines]) - else: - result.add("#define nimfr_(x, y)\n#define nimln_(x, y)\n") for i in cfsForwardTypes..cfsProcs: if m.s[i].len > 0: @@ -1932,7 +1981,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) @@ -2109,6 +2158,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. @@ -2129,7 +2179,10 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode): PNode = if m.hcrOn: # make sure this is pulled in (meaning hcrGetGlobal() is called for it during init) - cgsym(m, "programResult") + let sym = magicsys.getCompilerProc(m.g.graph, "programResult") + # ignore when not available, could be a module imported early in `system` + if sym != nil: + cgsymImpl m, sym if m.inHcrInitGuard: endBlock(m.initProc) @@ -2146,7 +2199,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode): PNode = cgsym(m, "rawWrite") # raise dependencies on behalf of genMainProc - if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}: + if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: cgsym(m, "initStackBottomWith") if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: cgsym(m, "initThreadVarsEmulation") diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index e1309e0fdd..9cc146c4db 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 1711c5f9a4..ed8f33630e 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 @@ -113,7 +116,7 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) = s.ast[dispatcherPos] = dispatcher proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym = - var disp = copySym(s, nextSymId(idgen)) + var disp = copySym(s, idgen) incl(disp.flags, sfDispatcher) excl(disp.flags, sfExported) let old = disp.typ @@ -127,7 +130,7 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym = disp.loc.r = "" if s.typ[0] != nil: if disp.ast.len > resultPos: - disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen)) + disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen) else: # We've encountered a method prototype without a filled-in # resultPos slot. We put a placeholder in there that will @@ -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/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index 02162755c1..e35e5d2db8 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest, nimfix +## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest import options, idents, nimconf, extccomp, commands, msgs, diff --git a/compiler/commands.nim b/compiler/commands.nim index c31476b45f..ba996f77ed 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 @@ -238,9 +238,9 @@ proc processCompile(conf: ConfigRef; filename: string) = extccomp.addExternalFileToCompile(conf, found) const - errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found" + 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) = @@ -262,13 +262,18 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo of "go": result = conf.selectedGC == gcGo of "none": result = conf.selectedGC == gcNone of "stack", "regions": result = conf.selectedGC == gcRegions - else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) + of "atomicarc": result = conf.selectedGC == gcAtomicArc + 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 @@ -278,7 +283,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": @@ -287,8 +294,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 @@ -334,10 +345,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 = @@ -379,7 +394,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: @@ -516,14 +532,7 @@ proc initOrcDefines*(conf: ConfigRef) = if conf.exc == excNone and conf.backend != backendCpp: conf.exc = excGoto -proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) = - if isOrc: - conf.selectedGC = gcOrc - defineSymbol(conf.symbols, "gcorc") - else: - conf.selectedGC = gcArc - defineSymbol(conf.symbols, "gcarc") - +proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef) = defineSymbol(conf.symbols, "gcdestructors") incl conf.globalOptions, optSeqDestructors incl conf.globalOptions, optTinyRtti @@ -533,10 +542,11 @@ proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) = 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 @@ -562,9 +572,17 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass, conf.selectedGC = gcMarkAndSweep defineSymbol(conf.symbols, "gcmarkandsweep") of "destructors", "arc": - registerArcOrc(pass, conf, false) + conf.selectedGC = gcArc + defineSymbol(conf.symbols, "gcarc") + registerArcOrc(pass, conf) of "orc": - registerArcOrc(pass, conf, true) + conf.selectedGC = gcOrc + defineSymbol(conf.symbols, "gcorc") + registerArcOrc(pass, conf) + of "atomicarc": + conf.selectedGC = gcAtomicArc + defineSymbol(conf.symbols, "gcatomicarc") + registerArcOrc(pass, conf) of "hooks": conf.selectedGC = gcHooks defineSymbol(conf.symbols, "gchooks") @@ -588,8 +606,8 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass, 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) @@ -604,17 +622,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": @@ -652,6 +670,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg) if backend == backendJs: # bug #21209 conf.globalOptions.excl {optThreadAnalysis, optThreads} + if optRun in conf.globalOptions: + # for now, -r uses nodejs, so define nodejs + defineSymbol(conf.symbols, "nodejs") conf.backend = backend of "doccmd": conf.docCmd = arg of "define", "d": @@ -799,6 +820,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "dll") of "staticlib": incl(conf.globalOptions, optGenStaticLib) + incl(conf.globalOptions, optNoMain) excl(conf.globalOptions, optGenGuiApp) defineSymbol(conf.symbols, "library") defineSymbol(conf.symbols, "staticlib") @@ -822,6 +844,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} @@ -862,17 +886,24 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; setTarget(conf.target, conf.target.targetOS, cpu) of "run", "r": processOnOffSwitchG(conf, {optRun}, arg, pass, info) + if conf.backend == backendJs: + # for now, -r uses nodejs, so define nodejs + 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) @@ -886,7 +917,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) @@ -1095,7 +1128,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) @@ -1122,7 +1156,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: @@ -1133,4 +1170,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/compiler.nimble b/compiler/compiler.nimble new file mode 100644 index 0000000000..ef3f343e11 --- /dev/null +++ b/compiler/compiler.nimble @@ -0,0 +1,28 @@ +include "../lib/system/compilation.nim" +version = $NimMajor & "." & $NimMinor & "." & $NimPatch +author = "Andreas Rumpf" +description = "Compiler package providing the compiler sources as a library." +license = "MIT" +skipDirs = @["."] +installDirs = @["compiler"] + +import os + +var compilerDir = "" + +before install: + rmDir("compiler") + + let + files = listFiles(".") + dirs = listDirs(".") + + mkDir("compiler") + + for f in files: + cpFile(f, "compiler" / f) + + for d in dirs: + cpDir(d, "compiler" / d) + +requires "nim" diff --git a/compiler/concepts.nim b/compiler/concepts.nim index bcf2d4d0e1..681e4eace9 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -27,7 +27,7 @@ const proc declareSelf(c: PContext; info: TLineInfo) = ## Adds the magical 'Self' symbols to the current scope. let ow = getCurrOwner(c) - let s = newSym(skType, getIdent(c.cache, "Self"), nextSymId(c.idgen), ow, info) + let s = newSym(skType, getIdent(c.cache, "Self"), c.idgen, ow, info) s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow) s.typ.flags.incl {tfUnresolved, tfPacked} s.typ.add newType(tyEmpty, nextTypeId(c.idgen), ow) @@ -62,7 +62,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 +121,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 +387,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 +470,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: @@ -493,7 +496,7 @@ proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph = gen(c, body) if root.kind == skResult: genImplicitReturn(c) - when defined(gcArc) or defined(gcOrc): + when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): result = c.code # will move else: shallowCopy(result, c.code) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index a833bded44..7edee1663f 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: d.sharedState.currFileIdx = addRstFileIndex(d, n.info) - result = parseRst(n.comment, - toLinenumber(n.info), - toColumn(n.info) + DocColOffset, - d.conf, d.sharedState) + try: + result = parseRst(n.comment, + toLinenumber(n.info), + toColumn(n.info) + DocColOffset, + d.conf, d.sharedState) + except ERecoverableError: + result = newRstNode(rnLiteralBlock, @[newRstLeaf(n.comment)]) + else: + result = nil proc genRecCommentAux(d: PDoc, n: PNode): PRstNode = if n == nil: return nil result = genComment(d, n) if result == nil: - if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef, + if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef, nkTypeClassTy, nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkSinkAsgn, nkHiddenStdConv}: # notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}: for i in 0.. 0: return @@ -471,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 @@ -587,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 @@ -713,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 @@ -929,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(), '/') @@ -978,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: @@ -1003,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: @@ -1025,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) @@ -1132,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: @@ -1170,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: @@ -1267,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) @@ -1289,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 @@ -1302,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 @@ -1375,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 @@ -1484,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") % ( @@ -1510,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 @@ -1523,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) @@ -1555,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..= 5 else: @@ -578,7 +584,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)])) @@ -642,7 +648,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) @@ -777,6 +783,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 @@ -832,7 +839,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) @@ -856,6 +866,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) @@ -878,15 +889,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: @@ -1017,8 +1028,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 @@ -1034,7 +1046,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() @@ -1047,7 +1059,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 @@ -1057,6 +1070,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 84ed991645..d04388b96a 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 8151c0b938..8d8af6b1c8 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 8fac069715..fb0fafc985 100644 --- a/compiler/gorgeimpl.nim +++ b/compiler/gorgeimpl.nim @@ -9,12 +9,14 @@ ## Module that implements ``gorge`` for the compiler. -import msgs, std / sha1, os, osproc, streams, options, +import msgs, os, osproc, streams, options, lineinfos, pathutils when defined(nimPreviewSlimSystem): import std/syncio +import ../dist/checksums/src/checksums/sha1 + proc readOutput(p: Process): (string, int) = result[0] = "" var output = p.outputStream @@ -27,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 15c6a64e36..1366a2382c 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 1aab9d5fe9..744fddcc0f 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -20,9 +20,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 +75,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/cbackend.nim b/compiler/ic/cbackend.nim index 21f69e4852..a1922c812d 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 bc61a38dec..ce64221010 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 36a57f11e2..845e3d1fa6 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -7,13 +7,15 @@ # distribution, for details about the copyright. # -import hashes, tables, intsets, std/sha1 +import hashes, tables, intsets import packed_ast, bitabs, rodfiles import ".." / [ast, idents, lineinfos, msgs, ropes, options, pathutils, condsyms, packages, modulepaths] #import ".." / [renderer, astalgo] from os import removeFile, isAbsolute +import ../../dist/checksums/src/checksums/sha1 + when defined(nimPreviewSlimSystem): import std/[syncio, assertions, formatfloat] @@ -357,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) @@ -372,7 +374,7 @@ proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib if l.isNil: return result.kind = l.kind result.generated = l.generated - result.isOverriden = l.isOverriden + result.isOverridden = l.isOverridden result.name = toLitId($l.name, m) storeNode(result, l, path) @@ -390,7 +392,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId assert sfForward notin s.flags var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic, - position: s.position, offset: s.offset, options: s.options, + position: s.position, offset: s.offset, disamb: s.disamb, options: s.options, name: s.name.s.toLitId(m)) storeNode(p, s, ast) @@ -811,6 +813,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: @@ -830,6 +833,7 @@ proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; options: s.options, position: if s.kind in {skForVar, skVar, skLet, skTemp}: 0 else: s.position, offset: if s.kind in routineKinds: defaultOffset else: s.offset, + disamb: s.disamb, name: getIdent(c.cache, g[si].fromDisk.strings[s.name]) ) @@ -847,7 +851,7 @@ proc loadLib(c: var PackedDecoder; g: var PackedModuleGraph; if l.name.int == 0: result = nil else: - result = PLib(generated: l.generated, isOverriden: l.isOverriden, + result = PLib(generated: l.generated, isOverridden: l.isOverridden, kind: l.kind, name: rope g[si].fromDisk.strings[l.name]) loadAstBody(l, path) @@ -913,7 +917,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: @@ -1144,6 +1148,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 = @@ -1161,11 +1167,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; @@ -1198,7 +1208,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 cbba591c5a..ab49b3b7a1 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 87b1516fa6..8eafa5e968 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -45,7 +45,7 @@ type PackedLib* = object kind*: TLibKind generated*: bool - isOverriden*: bool + isOverridden*: bool name*: LitId path*: NodeId @@ -63,7 +63,8 @@ type alignment*: int # for alignment options*: TOptions position*: int - offset*: int + offset*: int32 + disamb*: int32 externalName*: LitId # instead of TLoc locFlags*: TLocFlags annex*: PackedLib @@ -304,6 +305,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 e492624d04..41e85084f1 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 84f4bb5457..f5eb5329d9 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -113,6 +113,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] @@ -228,10 +229,15 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im 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 template createModuleAliasImpl(ident): untyped = - createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options) + createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options) if n.kind != nkImportAs: discard elif n.len != 2 or n[1].kind != nkIdent: localError(c.config, n.info, "module alias must be an identifier") @@ -245,6 +251,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(result.name.id, @[]).addUnique realModule.id proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] = var ret: typeof(result) @@ -301,6 +308,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 96102d54d2..a5ec6c21a6 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 @@ -66,11 +67,11 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} = result = ast.hasDestructor(t) when toDebug.len > 0: # for more effective debugging - if not result and c.graph.config.selectedGC in {gcArc, gcOrc}: + if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: assert(not containsGarbageCollectedRef(t)) proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = - let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextSymId c.idgen, c.owner, info) + let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.idgen, c.owner, info) sym.typ = typ s.vars.add(sym) result = newSymNode(sym) @@ -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,11 +185,16 @@ 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) = +proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFromCopy = false) = var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">" - if (opname == "=" or opname == "=copy") and ri != nil: + if inferredFromCopy: + m.add ", which is inferred from unavailable '=copy'" + + if (opname == "=" or opname == "=copy" or opname == "=dup") and ri != nil: m.add "; requires a copy because it's not the last read of '" m.add renderTree(ri) m.add '\'' @@ -210,8 +216,12 @@ proc makePtrType(c: var Con, baseType: PType): PType = addSonSkipIntLit(result, baseType, c.idgen) proc genOp(c: var Con; op: PSym; dest: PNode): PNode = - let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ)) - addrExp.add(dest) + var addrExp: PNode + if op.typ != nil and op.typ.len > 1 and op.typ[1].kind != tyVar: + addrExp = dest + else: + addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ)) + addrExp.add(dest) result = newTree(nkCall, newSymNode(op), addrExp) proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = @@ -262,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: @@ -305,13 +315,13 @@ proc isCriticalLink(dest: PNode): bool {.inline.} = proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) = if c.graph.config.selectedGC == gcOrc: let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}) - if cyclicType(t): + if cyclicType(c.graph, t): result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest)) proc genMarkCyclic(c: var Con; result, dest: PNode) = if c.graph.config.selectedGC == gcOrc: let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}) - if cyclicType(t): + if cyclicType(c.graph, t): if t.kind == tyRef: result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest) else: @@ -325,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: @@ -353,7 +366,7 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode = if hasDestructor(c, objType): if getAttachedOp(c.graph, objType, attachedDestructor) != nil and - sfOverriden in getAttachedOp(c.graph, objType, attachedDestructor).flags: + sfOverridden in getAttachedOp(c.graph, objType, attachedDestructor).flags: localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor. It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""") result.add newTree(nkFastAsgn, le, tmp) @@ -381,7 +394,7 @@ proc genWasMoved(c: var Con, n: PNode): PNode = result = genOp(c, op, n) else: result = newNodeI(nkCall, n.info) - result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved))) + result.add(newSymNode(createMagic(c.graph, c.idgen, "`=wasMoved`", mWasMoved))) result.add copyTree(n) #mWasMoved does not take the address #if n.kind != nkSym: # message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")") @@ -393,13 +406,13 @@ 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: result = newNodeIT(nkStmtListExpr, n.info, n.typ) - var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextSymId c.idgen, c.owner, n.info) + var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.idgen, c.owner, n.info) temp.typ = n.typ var v = newNodeI(nkLetSection, n.info) let tempAsNode = newSymNode(temp) @@ -412,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 @@ -420,22 +434,47 @@ 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) let tmp = c.getTemp(s, n.typ, n.info) if hasDestructor(c, n.typ): - result.add c.genWasMoved(tmp) - var m = c.genCopy(tmp, n, {}) - m.add p(n, c, s, normal) - c.finishCopy(m, n, isFromSink = true) - result.add m + let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink}) + let op = getAttachedOp(c.graph, typ, attachedDup) + if op != nil and tfHasOwned notin typ.flags: + if sfError in op.flags: + c.checkForErrorPragma(n.typ, n, "=dup") + else: + let copyOp = getAttachedOp(c.graph, typ, attachedAsgn) + if copyOp != nil and sfError in copyOp.flags and + sfOverridden notin op.flags: + c.checkForErrorPragma(n.typ, n, "=dup", inferredFromCopy = true) + + let src = p(n, c, s, normal) + var newCall = newTreeIT(nkCall, src.info, src.typ, + newSymNode(op), + src) + c.finishCopy(newCall, n, isFromSink = true) + result.add newTreeI(nkFastAsgn, + src.info, tmp, + newCall + ) + else: + result.add c.genWasMoved(tmp) + var m = c.genCopy(tmp, n, {}) + m.add p(n, c, s, normal) + c.finishCopy(m, n, isFromSink = true) + result.add m if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: 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}: + if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: assert(not containsManagedMemory(n.typ)) if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter") @@ -478,7 +517,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode = result = arg proc cycleCheck(n: PNode; c: var Con) = - if c.graph.config.selectedGC != gcArc: return + if c.graph.config.selectedGC notin {gcArc, gcAtomicArc}: return var value = n[1] if value.kind == nkClosure: value = value[1] @@ -679,7 +718,27 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false, of nkWhen: # This should be a "when nimvm" node. result = copyTree(n) result[1][0] = processCall(n[1][0], s) - else: assert(false) + + of nkPragmaBlock: + var inUncheckedAssignSection = 0 + let pragmaList = n[0] + for pi in pragmaList: + if whichPragma(pi) == wCast: + case whichPragma(pi[1]) + of wUncheckedAssign: + inUncheckedAssignSection = 1 + else: + discard + result = shallowCopy(n) + inc c.inUncheckedAssignSection, inUncheckedAssignSection + for i in 0 ..< n.len-1: + result[i] = p(n[i], c, s, normal) + result[^1] = maybeVoid(n[^1], s) + dec c.inUncheckedAssignSection, inUncheckedAssignSection + + 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: @@ -706,9 +765,9 @@ 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}: + nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}: template process(child, s): untyped = p(child, c, s, mode) handleNestedTempl(n, process, tmpFlags = tmpFlags) elif mode == sinkArg: @@ -719,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 @@ -791,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 @@ -821,7 +894,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}: result[0] = copyTree(n[0]) - if c.graph.config.selectedGC in {gcHooks, gcArc, gcOrc}: + if c.graph.config.selectedGC in {gcHooks, gcArc, gcAtomicArc, gcOrc}: let destroyOld = c.genDestroy(result[1]) result = newTree(nkStmtList, destroyOld, result) else: @@ -876,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) @@ -904,25 +979,6 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing if mode == normal: result = ensureDestruction(result, n, c, s) - of nkPragmaBlock: - var inUncheckedAssignSection = 0 - let pragmaList = n[0] - for pi in pragmaList: - if whichPragma(pi) == wCast: - case whichPragma(pi[1]) - of wUncheckedAssign: - inUncheckedAssignSection = 1 - else: - discard - result = shallowCopy(n) - inc c.inUncheckedAssignSection, inUncheckedAssignSection - for i in 0 ..< n.len: - result[i] = p(n[i], c, s, normal) - dec c.inUncheckedAssignSection, inUncheckedAssignSection - if n.typ != nil and hasDestructor(c, n.typ): - if mode == normal: - result = ensureDestruction(result, n, c, s) - of nkHiddenSubConv, nkHiddenStdConv, nkConv: # we have an "ownership invariance" for all constructors C(x). # See the comment for nkBracket construction. If the caller wants @@ -979,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: @@ -1091,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: @@ -1114,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 226682715c..49ee59b0d0 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -78,6 +78,7 @@ Files: "lib" [Other] Files: "examples" Files: "dist/nimble" +Files: "dist/checksums" Files: "tests" @@ -91,6 +92,7 @@ Files: "bin/nimgrab.exe" Files: "bin/nimpretty.exe" Files: "bin/testament.exe" Files: "bin/nim-gdb.bat" +Files: "bin/atlas.exe" Files: "koch.exe" Files: "finish.exe" diff --git a/compiler/int128.nim b/compiler/int128.nim index b0341eb379..18e751f989 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 2674605dcf..5fd1b8d51b 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 @@ -607,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: @@ -625,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: @@ -665,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: @@ -682,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("", "") @@ -732,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)") @@ -759,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] @@ -803,9 +860,13 @@ proc genLineDir(p: PProc, n: PNode) = lineF(p, "$1", [lineDir(p.config, n.info, line)]) if hasFrameInfo(p): lineF(p, "F.line = $1;$n", [rope(line)]) + let currentFileName = toFilename(p.config, n.info) + if p.previousFileName != currentFileName: + lineF(p, "F.filename = $1;$n", [makeJSString(currentFileName)]) + 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) @@ -900,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("||") @@ -940,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) @@ -954,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 @@ -1088,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: @@ -1104,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 @@ -1147,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) @@ -2684,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]) @@ -2711,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: @@ -2741,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: @@ -2776,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): @@ -2819,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): @@ -2920,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 @@ -2999,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 f6bc21c010..ee19eec081 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -140,7 +140,7 @@ proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator) rawAddSon(result, intType) proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym = - result = newSym(skField, getIdent(g.cache, ":state"), nextSymId(idgen), iter, iter.info) + result = newSym(skField, getIdent(g.cache, ":state"), idgen, iter, iter.info) result.typ = createClosureIterStateType(g, iter, idgen) proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType = @@ -154,7 +154,7 @@ proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym result = iter.ast[resultPos].sym else: # XXX a bit hacky: - result = newSym(skResult, getIdent(g.cache, ":result"), nextSymId(idgen), iter, iter.info, {}) + result = newSym(skResult, getIdent(g.cache, ":result"), idgen, iter, iter.info, {}) result.typ = iter.typ[0] incl(result.flags, sfUsed) iter.ast.add newSymNode(result) @@ -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 @@ -267,7 +271,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen) env = indirectAccess(newSymNode(it), hp, hp.info) else: - let e = newSym(skLet, iter.name, nextSymId(idgen), owner, n.info) + let e = newSym(skLet, iter.name, idgen, owner, n.info) e.typ = hp.typ e.flags = hp.flags env = newSymNode(e) @@ -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 = @@ -380,7 +385,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags: localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor") else: - let result = newSym(skField, upIdent, nextSymId(c.idgen), obj.owner, obj.owner.info) + let result = newSym(skField, upIdent, c.idgen, obj.owner, obj.owner.info) result.typ = fieldType when false: if c.graph.config.selectedGC == gcDestructors: @@ -413,12 +418,15 @@ 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 let t = c.getEnvTypeForOwner(owner, info) if cp == nil: - cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextSymId(c.idgen), fn, fn.info) + cp = newSym(skParam, getIdent(c.graph.cache, paramName), c.idgen, fn, fn.info) incl(cp.flags, sfFromGeneric) cp.typ = t addHiddenParam(fn, cp) @@ -444,12 +452,15 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = 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..", "<=", ">=", "==", "!="]): # bug #9504: remember to not spacify a keyword: lastTokWasTerse = true @@ -538,7 +538,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = if not em.endsInWhite: wrSpace(em) wr(em, tok.ident.s, ltOpr) template isUnary(tok): bool = - tok.strongSpaceB == tsNone and tok.strongSpaceA + tok.spacing == {tsLeading} if not isUnary(tok): rememberSplit(splitBinary) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index a62d40e54c..93a5f80406 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -23,7 +23,6 @@ when defined(nimPreviewSlimSystem): import std/[assertions, formatfloat] const - MaxLineLength* = 80 # lines longer than this lead to a warning numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'} SymChars*: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'} SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} @@ -94,19 +93,18 @@ type base2, base8, base16 TokenSpacing* = enum - tsNone, tsTrailing, tsEof + tsLeading, tsTrailing, tsEof Token* = object # a Nim token tokType*: TokType # the type of the token + base*: NumericalBase # the numerical base; only valid for int + # or float literals + spacing*: set[TokenSpacing] # spaces around token indent*: int # the indentation; != -1 if the token has been # preceded with indentation ident*: PIdent # the parsed identifier iNumber*: BiggestInt # the parsed integer literal fNumber*: BiggestFloat # the parsed floating point literal - base*: NumericalBase # the numerical base; only valid for int - # or float literals - strongSpaceA*: bool # leading spaces of an operator - strongSpaceB*: TokenSpacing # trailing spaces of an operator literal*: string # the parsed (string) literal; and # documentation comments are here too line*, col*: int @@ -150,9 +148,11 @@ proc isNimIdentifier*(s: string): bool = var i = 1 while i < sLen: if s[i] == '_': inc(i) - if i < sLen and s[i] notin SymChars: return + if i < sLen and s[i] notin SymChars: return false inc(i) result = true + else: + result = false proc `$`*(tok: Token): string = case tok.tokType @@ -178,7 +178,7 @@ proc initToken*(L: var Token) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 - L.strongSpaceA = false + L.spacing = {} L.literal = "" L.fNumber = 0.0 L.base = base10 @@ -191,7 +191,7 @@ proc fillToken(L: var Token) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 - L.strongSpaceA = false + L.spacing = {} setLen(L.literal, 0) L.fNumber = 0.0 L.base = base10 @@ -539,8 +539,8 @@ proc getNumber(L: var Lexer, result: var Token) = of floatTypes: result.fNumber = parseFloat(result.literal) of tkUInt64Lit, tkUIntLit: - var iNumber: uint64 - var len: int + var iNumber: uint64 = uint64(0) + var len: int = 0 try: len = parseBiggestUInt(result.literal, iNumber) except ValueError: @@ -549,8 +549,8 @@ proc getNumber(L: var Lexer, result: var Token) = raise newException(ValueError, "invalid integer: " & result.literal) result.iNumber = cast[int64](iNumber) else: - var iNumber: int64 - var len: int + var iNumber: int64 = int64(0) + var len: int = 0 try: len = parseBiggestInt(result.literal, iNumber) except ValueError: @@ -737,10 +737,6 @@ proc handleCRLF(L: var Lexer, pos: int): int = template registerLine = let col = L.getColNumber(pos) - when not defined(nimpretty): - if col > MaxLineLength: - lexMessagePos(L, hintLineTooLong, pos) - case L.buf[pos] of CR: registerLine() @@ -960,13 +956,15 @@ proc getOperator(L: var Lexer, tok: var Token) = tokenEnd(tok, pos-1) # advance pos but don't store it in L.bufpos so the next token (which might # be an operator too) gets the preceding spaces: - tok.strongSpaceB = tsNone + tok.spacing = tok.spacing - {tsTrailing, tsEof} + var trailing = false while L.buf[pos] == ' ': inc pos - if tok.strongSpaceB != tsTrailing: - tok.strongSpaceB = tsTrailing + trailing = true if L.buf[pos] in {CR, LF, nimlexbase.EndOfFile}: - tok.strongSpaceB = tsEof + tok.spacing.incl(tsEof) + elif trailing: + tok.spacing.incl(tsTrailing) proc getPrecedence*(tok: Token): int = ## Calculates the precedence of the given token. @@ -1011,6 +1009,7 @@ proc getPrecedence*(tok: Token): int = else: return -10 proc newlineFollows*(L: Lexer): bool = + result = false var pos = L.bufpos while true: case L.buf[pos] @@ -1077,7 +1076,6 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int; when defined(nimpretty): tok.literal.add "\L" if isDoc: when not defined(nimpretty): tok.literal.add "\n" - inc tok.iNumber var c = toStrip while L.buf[pos] == ' ' and c > 0: inc pos @@ -1096,8 +1094,6 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int; proc scanComment(L: var Lexer, tok: var Token) = var pos = L.bufpos tok.tokType = tkComment - # iNumber contains the number of '\n' in the token - tok.iNumber = 0 assert L.buf[pos+1] == '#' when defined(nimpretty): tok.commentOffsetA = L.offsetBase + pos @@ -1140,7 +1136,6 @@ proc scanComment(L: var Lexer, tok: var Token) = while L.buf[pos] == ' ' and c > 0: inc pos dec c - inc tok.iNumber else: if L.buf[pos] > ' ': L.indentAhead = indent @@ -1153,7 +1148,7 @@ proc scanComment(L: var Lexer, tok: var Token) = proc skip(L: var Lexer, tok: var Token) = var pos = L.bufpos tokenBegin(tok, pos) - tok.strongSpaceA = false + tok.spacing.excl(tsLeading) when defined(nimpretty): var hasComment = false var commentIndent = L.currLineIndent @@ -1164,8 +1159,7 @@ proc skip(L: var Lexer, tok: var Token) = case L.buf[pos] of ' ': inc(pos) - if not tok.strongSpaceA: - tok.strongSpaceA = true + tok.spacing.incl(tsLeading) of '\t': if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead") inc(pos) @@ -1187,7 +1181,7 @@ proc skip(L: var Lexer, tok: var Token) = pos = L.bufpos else: break - tok.strongSpaceA = false + tok.spacing.excl(tsLeading) when defined(nimpretty): if L.buf[pos] == '#' and tok.line < 0: commentIndent = indent if L.buf[pos] > ' ' and (L.buf[pos] != '#' or L.buf[pos+1] == '#'): @@ -1403,8 +1397,9 @@ proc rawGetTok*(L: var Lexer, tok: var Token) = proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream; cache: IdentCache; config: ConfigRef): int = - var lex: Lexer - var tok: Token + result = 0 + var lex: Lexer = default(Lexer) + var tok: Token = default(Token) initToken(tok) openLexer(lex, fileIdx, inputstream, cache, config) var prevToken = tkEof diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 467f015d83..f28586addb 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -8,7 +8,7 @@ # ## This module implements lifting for type-bound operations -## (``=sink``, ``=copy``, ``=destroy``, ``=deepCopy``). +## (`=sink`, `=copy`, `=destroy`, `=deepCopy`, `=wasMoved`, `=dup`). import modulegraphs, lineinfos, idents, ast, renderer, semdata, sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils @@ -34,6 +34,7 @@ type template destructor*(t: PType): PSym = getAttachedOp(c.g, t, attachedDestructor) template assignment*(t: PType): PSym = getAttachedOp(c.g, t, attachedAsgn) +template dup*(t: PType): PSym = getAttachedOp(c.g, t, attachedDup) template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink) proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) @@ -49,9 +50,9 @@ proc at(a, i: PNode, elemType: PType): PNode = result[1] = i result.typ = elemType -proc destructorOverriden(g: ModuleGraph; t: PType): bool = +proc destructorOverridden(g: ModuleGraph; t: PType): bool = let op = getAttachedOp(g, t, attachedDestructor) - op != nil and sfOverriden in op.flags + op != nil and sfOverridden in op.flags proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) = for i in 0..