Compare commits
No commits in common. "devel" and "revert-2787-fix-doc-typos" have entirely different histories.
devel
...
revert-278
4017 changed files with 254707 additions and 389216 deletions
9
.gitattributes
vendored
9
.gitattributes
vendored
|
|
@ -1,9 +0,0 @@
|
||||||
# Avoids changelog conflicts by assuming additions-only, which is by far the common case.
|
|
||||||
# In the rare case where branch b1 rebases against branch b2 and both branches
|
|
||||||
# modified the same changelog entry, you'll end up with that changelog entry
|
|
||||||
# duplicated, which is easily identifiable and fixable.
|
|
||||||
/changelog.md merge=union
|
|
||||||
|
|
||||||
# bug https://github.com/dom96/choosenim/issues/256 for WSL CRLF
|
|
||||||
*.sh text eol=lf
|
|
||||||
/config/build_config.txt text eol=lf
|
|
||||||
12
.github/FUNDING.yml
vendored
12
.github/FUNDING.yml
vendored
|
|
@ -1,12 +0,0 @@
|
||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
|
||||||
patreon: 'araq' # Replace with a single Patreon username
|
|
||||||
open_collective: 'nim' # Replace with a single Open Collective username
|
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
||||||
liberapay: # Replace with a single Liberapay username
|
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
|
||||||
otechie: # Replace with a single Otechie username
|
|
||||||
custom: 'https://nim-lang.org/donate.html' # Replace with a single custom sponsorship URL
|
|
||||||
75
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
75
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -1,75 +0,0 @@
|
||||||
name: "Bug Report"
|
|
||||||
description: "Create a new bug report. Have you found an unexpected behavior? Use this form."
|
|
||||||
title: "Think about the title, twice."
|
|
||||||
labels: ["unconfirmed"]
|
|
||||||
body:
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
- **Please provide a minimal code example that reproduces the Bug!** :bug:
|
|
||||||
Reports with a reproducible example and descriptive detailed information will likely receive fixes faster.
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: |
|
|
||||||
Use DETAILED DESCRIPTIVE information about the problem.
|
|
||||||
Here, you go into more details about your Bug report. This section can be a few paragraphs long.
|
|
||||||
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: nim-version
|
|
||||||
attributes:
|
|
||||||
label: Nim Version
|
|
||||||
description: Copy and paste the output of `nim -v` on the command line. For development versions, make sure to include the commit hash.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: current-logs
|
|
||||||
attributes:
|
|
||||||
label: Current Output
|
|
||||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
|
||||||
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
|
|
||||||
render: text
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: expected-logs
|
|
||||||
attributes:
|
|
||||||
label: Expected Output
|
|
||||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
|
||||||
placeholder: Bug reports with reproducible code and detailed information will be fixed faster.
|
|
||||||
render: text
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: possible-solution
|
|
||||||
attributes:
|
|
||||||
label: Possible Solution
|
|
||||||
description: Propose a possible solution.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: extra-info
|
|
||||||
attributes:
|
|
||||||
label: Additional Information
|
|
||||||
description: Any additional relevant information.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
- Thanks for your contributions!, your Bug report will receive feedback from the community soon...
|
|
||||||
- Please check whether the problem still exists in the devel branch, see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler).
|
|
||||||
- Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
|
|
||||||
- If it's a pre-existing compiler bug, see [Debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler)
|
|
||||||
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`).
|
|
||||||
- [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)
|
|
||||||
10
.github/ISSUE_TEMPLATE/config.yml
vendored
10
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,10 +0,0 @@
|
||||||
contact_links:
|
|
||||||
- name: Forum
|
|
||||||
url: https://forum.nim-lang.org
|
|
||||||
about: Please ask and answer questions here.
|
|
||||||
- name: Discord
|
|
||||||
url: https://discord.gg/nim
|
|
||||||
about: Please ask and answer questions here.
|
|
||||||
- name: Stack Overflow
|
|
||||||
url: https://stackoverflow.com/questions/tagged/nim-lang
|
|
||||||
about: Please ask and answer questions here.
|
|
||||||
72
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
72
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
|
|
@ -1,72 +0,0 @@
|
||||||
name: "Feature request"
|
|
||||||
description: "Create a new Feature Request. Do you want to suggest a new feature? Use this form."
|
|
||||||
title: "Think about the title, twice."
|
|
||||||
labels: ["unconfirmed"]
|
|
||||||
body:
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
- Please provide a minimal code example that illustrates the basic idea behind your feature request.
|
|
||||||
Reports with full repro code and descriptive detailed information will likely receive feedback faster.
|
|
||||||
- There is a separate repository for the detailed RFCs and proposals: https://github.com/nim-lang/RFCs.
|
|
||||||
If you have a simple feature request, you can post it here using this form,
|
|
||||||
but bear in mind that adding new features to the language is currently a low priority.
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: summary
|
|
||||||
attributes:
|
|
||||||
label: Summary
|
|
||||||
description: |
|
|
||||||
Use DETAILED DESCRIPTIVE information about the feature request.
|
|
||||||
Here, you go into more details about your ideas. This section can be a few paragraphs long.
|
|
||||||
placeholder: Short summary of your proposed feature.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: Description
|
|
||||||
description: Describe your solution, what problem does it fix?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: alternatives
|
|
||||||
attributes:
|
|
||||||
label: Alternatives
|
|
||||||
description: Are there any alternatives you've considered?
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: Examples
|
|
||||||
attributes:
|
|
||||||
label: Examples
|
|
||||||
description: Provide examples for your feature request here.
|
|
||||||
placeholder: Example code here.
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: incompatibility
|
|
||||||
attributes:
|
|
||||||
label: Backwards Compatibility
|
|
||||||
description: If your Feature Request introduces backward-incompatible changes, describe them and propose how to deal with them.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: links
|
|
||||||
attributes:
|
|
||||||
label: Links
|
|
||||||
description: Please copy and paste any relevant links to projects, issues, discussions, technical documentations, code samples, etc.
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
- Thanks for your contributions!, your ideas will receive feedback from the community soon...
|
|
||||||
- **Remember to :star: Star the Nim project on GitHub!.**
|
|
||||||
- Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
|
|
||||||
- [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)
|
|
||||||
69
.github/stale.yml
vendored
69
.github/stale.yml
vendored
|
|
@ -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
|
|
||||||
118
.github/workflows/ci_docs.yml
vendored
118
.github/workflows/ci_docs.yml
vendored
|
|
@ -1,118 +0,0 @@
|
||||||
name: Nim Docs CI
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
paths:
|
|
||||||
- 'compiler/docgen.nim'
|
|
||||||
- 'compiler/renderverbatim.nim'
|
|
||||||
- 'config/nimdoc.cfg'
|
|
||||||
- 'doc/**.rst'
|
|
||||||
- 'doc/**.md'
|
|
||||||
- 'doc/nimdoc.css'
|
|
||||||
- 'lib/**.nim'
|
|
||||||
- 'nimdoc/testproject/expected/testproject.html'
|
|
||||||
- 'tools/dochack/dochack.nim'
|
|
||||||
- 'tools/kochdocs.nim'
|
|
||||||
- '.github/workflows/ci_docs.yml'
|
|
||||||
- 'koch.nim'
|
|
||||||
|
|
||||||
pull_request:
|
|
||||||
# Run only on changes on these files.
|
|
||||||
paths:
|
|
||||||
- 'compiler/docgen.nim'
|
|
||||||
- 'compiler/renderverbatim.nim'
|
|
||||||
- 'config/nimdoc.cfg'
|
|
||||||
- 'doc/**.rst'
|
|
||||||
- 'doc/**.md'
|
|
||||||
- 'doc/nimdoc.css'
|
|
||||||
- 'lib/**.nim'
|
|
||||||
- 'nimdoc/testproject/expected/testproject.html'
|
|
||||||
- 'tools/dochack/dochack.nim'
|
|
||||||
- 'tools/kochdocs.nim'
|
|
||||||
- '.github/workflows/ci_docs.yml'
|
|
||||||
- 'koch.nim'
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
target: [linux, windows, osx]
|
|
||||||
include:
|
|
||||||
- target: linux
|
|
||||||
os: ubuntu-20.04
|
|
||||||
- target: windows
|
|
||||||
os: windows-2019
|
|
||||||
- target: osx
|
|
||||||
os: macos-11
|
|
||||||
|
|
||||||
name: ${{ matrix.target }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
timeout-minutes: 60 # refs bug #18178
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: 'Checkout'
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
- name: 'Install build dependencies (macOS)'
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
run: brew install make
|
|
||||||
|
|
||||||
- name: 'Install build dependencies (Windows)'
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
. ci/funs.sh
|
|
||||||
nimInternalInstallDepsWindows
|
|
||||||
echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
|
||||||
|
|
||||||
- name: 'Add build binaries to PATH'
|
|
||||||
shell: bash
|
|
||||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
|
||||||
|
|
||||||
- name: 'System information'
|
|
||||||
shell: bash
|
|
||||||
run: . ci/funs.sh && nimCiSystemInfo
|
|
||||||
|
|
||||||
- name: 'Build csourcesAny (posix)'
|
|
||||||
# this would work on windows and other CI use this on windows,
|
|
||||||
# but we ensure here that `ci/build_autogen.bat` keeps working on windows.
|
|
||||||
if: runner.os != 'Windows'
|
|
||||||
shell: bash
|
|
||||||
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc
|
|
||||||
# was previously using caching via `actions/cache@v1` but this wasn't
|
|
||||||
# used in other CI pipelines and it's unclear the added complexity
|
|
||||||
# was worth the saving; can be revisited if needed.
|
|
||||||
|
|
||||||
- name: 'Build csourcesAny (windows)'
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
shell: cmd
|
|
||||||
run: ci/build_autogen.bat
|
|
||||||
|
|
||||||
- name: 'Build koch'
|
|
||||||
shell: bash
|
|
||||||
run: nim c koch
|
|
||||||
|
|
||||||
- name: 'Build the real compiler'
|
|
||||||
shell: bash
|
|
||||||
run: ./koch boot -d:release
|
|
||||||
|
|
||||||
- name: 'Build documentation'
|
|
||||||
shell: bash
|
|
||||||
run: ./koch doc --git.commit:devel
|
|
||||||
|
|
||||||
- name: 'Publish documentation to Github Pages'
|
|
||||||
if: |
|
|
||||||
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
|
|
||||||
matrix.target == 'linux'
|
|
||||||
uses: crazy-max/ghaction-github-pages@v3
|
|
||||||
with:
|
|
||||||
build_dir: doc/html
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
74
.github/workflows/ci_packages.yml
vendored
74
.github/workflows/ci_packages.yml
vendored
|
|
@ -1,74 +0,0 @@
|
||||||
name: Packages CI
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'devel'
|
|
||||||
- 'version-2-0'
|
|
||||||
- 'version-1-6'
|
|
||||||
- 'version-1-2'
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-20.04, macos-11]
|
|
||||||
cpu: [amd64]
|
|
||||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
|
||||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
timeout-minutes: 60 # refs bug #18178
|
|
||||||
env:
|
|
||||||
NIM_TEST_PACKAGES: "1"
|
|
||||||
NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
|
|
||||||
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: 'Install dependencies (macOS)'
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
run: brew install boehmgc make sfml gtk+3
|
|
||||||
- name: 'Install dependencies (Windows)'
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
. ci/funs.sh
|
|
||||||
nimInternalInstallDepsWindows
|
|
||||||
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
|
||||||
|
|
||||||
- name: 'Add build binaries to PATH'
|
|
||||||
shell: bash
|
|
||||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
|
||||||
|
|
||||||
- name: 'System information'
|
|
||||||
shell: bash
|
|
||||||
run: . ci/funs.sh && nimCiSystemInfo
|
|
||||||
|
|
||||||
- name: 'Build csourcesAny'
|
|
||||||
shell: bash
|
|
||||||
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
|
|
||||||
|
|
||||||
- name: 'koch, Run CI'
|
|
||||||
shell: bash
|
|
||||||
run: . ci/funs.sh && nimInternalBuildKochAndRunCI
|
|
||||||
90
.github/workflows/ci_publish.yml
vendored
90
.github/workflows/ci_publish.yml
vendored
|
|
@ -1,90 +0,0 @@
|
||||||
name: Tracking orc-booting compiler memory usage
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [closed]
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
if: github.event.pull_request.merged == true
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-20.04]
|
|
||||||
cpu: [amd64]
|
|
||||||
name: '${{ matrix.os }}'
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
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: 'Install dependencies (macOS)'
|
|
||||||
if: runner.os == 'macOS'
|
|
||||||
run: brew install boehmgc make sfml gtk+3
|
|
||||||
- name: 'Install dependencies (Windows)'
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
. ci/funs.sh
|
|
||||||
nimInternalInstallDepsWindows
|
|
||||||
echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
|
|
||||||
|
|
||||||
- name: 'Add build binaries to PATH'
|
|
||||||
shell: bash
|
|
||||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
|
||||||
|
|
||||||
- name: 'System information'
|
|
||||||
shell: bash
|
|
||||||
run: . ci/funs.sh && nimCiSystemInfo
|
|
||||||
|
|
||||||
- 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: 'Action'
|
|
||||||
shell: bash
|
|
||||||
run: nim c -r -d:release ci/action.nim
|
|
||||||
|
|
||||||
- name: 'Comment'
|
|
||||||
uses: actions/github-script@v6
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const data = fs.readFileSync('ci/nimcache/results.txt', 'utf8');
|
|
||||||
github.rest.issues.createComment({
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
body: data
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
79
.gitignore
vendored
79
.gitignore
vendored
|
|
@ -1,39 +1,26 @@
|
||||||
*
|
*
|
||||||
!**/
|
!**/
|
||||||
!*.*
|
!*.*
|
||||||
|
nimcache/
|
||||||
# Cache
|
|
||||||
nimcache*/
|
|
||||||
rnimcache*/
|
|
||||||
dnimcache*/
|
|
||||||
|
|
||||||
*.o
|
*.o
|
||||||
!/icons/*.o
|
!/icons/*.o
|
||||||
*.obj
|
|
||||||
*.ilk
|
|
||||||
*.exp
|
|
||||||
*.pdb
|
|
||||||
*.lib
|
|
||||||
*.dll
|
|
||||||
*.exe
|
*.exe
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
*.zip
|
*.zip
|
||||||
*.iss
|
*.iss
|
||||||
*.log
|
|
||||||
*.pdb
|
|
||||||
|
|
||||||
mapping.txt
|
mapping.txt
|
||||||
tags
|
tags
|
||||||
install.sh
|
install.sh
|
||||||
deinstall.sh
|
deinstall.sh
|
||||||
|
|
||||||
doc/html/
|
|
||||||
doc/*.html
|
doc/*.html
|
||||||
doc/pdf
|
doc/*.pdf
|
||||||
doc/*.idx
|
doc/*.idx
|
||||||
/web/upload
|
/web/upload
|
||||||
/build/*
|
build/*
|
||||||
bin/*
|
bin/*
|
||||||
|
|
||||||
# iOS specific wildcards.
|
# iOS specific wildcards.
|
||||||
|
|
@ -43,70 +30,14 @@ bin/*
|
||||||
*.perspectivev3
|
*.perspectivev3
|
||||||
*.swp
|
*.swp
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.tags
|
|
||||||
project.xcworkspace/
|
project.xcworkspace/
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
|
|
||||||
# Generated files.
|
# Generated files.
|
||||||
/compile.json
|
/compile.json
|
||||||
/compiler/nim.dot
|
/compiler/nimrod.dot
|
||||||
/reject.json
|
/reject.json
|
||||||
/run.json
|
/run.json
|
||||||
/tools/dochack/dochack.js
|
/testresults.html
|
||||||
*.json
|
|
||||||
/pkgstemp/**/*
|
|
||||||
# for `nim doc foo.nim`
|
|
||||||
/*.html
|
|
||||||
lib/**/*.html
|
|
||||||
#/testresults.html #covered by /*.html
|
|
||||||
|
|
||||||
/testresults.json
|
/testresults.json
|
||||||
testament.db
|
testament.db
|
||||||
/tests/**/*.json
|
|
||||||
/tests/**/*.js
|
|
||||||
|
|
||||||
/csources
|
|
||||||
/csources_v1
|
|
||||||
/csources_v2
|
|
||||||
|
|
||||||
/dist/
|
|
||||||
# /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it
|
|
||||||
|
|
||||||
# Private directories and files (IDEs)
|
|
||||||
.*/
|
|
||||||
~*
|
|
||||||
|
|
||||||
# testament cruft; TODO: generate these in a gitignore'd dir (./build) in the first place.
|
|
||||||
testresults/
|
|
||||||
test.txt
|
|
||||||
/test.ini
|
|
||||||
|
|
||||||
tweeter.db
|
|
||||||
tweeter_test.db
|
|
||||||
|
|
||||||
/tests/megatest.nim
|
|
||||||
/tests/ic/*_temp.nim
|
|
||||||
/tests/navigator/*_temp.nim
|
|
||||||
|
|
||||||
|
|
||||||
/outputExpected.txt
|
|
||||||
/outputGotten.txt
|
|
||||||
/t15148.txt
|
|
||||||
/tests/vm/tfile_rw.txt
|
|
||||||
|
|
||||||
/lib/pure/*.js
|
|
||||||
|
|
||||||
!/.builds/
|
|
||||||
|
|
||||||
!/.github
|
|
||||||
|
|
||||||
# ignore debug dirs generated by dsymutil on OSX
|
|
||||||
*.dSYM
|
|
||||||
|
|
||||||
# for `nim c -r nimdoc/tester` etc; this can be in multiple places
|
|
||||||
htmldocs
|
|
||||||
|
|
||||||
## these are not needed anymore unless checkout old older versions
|
|
||||||
nimdoc.out.css
|
|
||||||
# except here:
|
|
||||||
!/nimdoc/testproject/expected/*
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "csources"]
|
||||||
|
path = csources
|
||||||
|
url = ../../nim-lang/csources.git
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
trigger:
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- 'devel'
|
|
||||||
- 'version-*'
|
|
||||||
pr:
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
variables:
|
|
||||||
- name: skipci
|
|
||||||
value: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- job: packages
|
|
||||||
|
|
||||||
timeoutInMinutes: 90 # default `60` led to lots of cancelled jobs; use 0 for unlimited (may be undesirable)
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
Linux_amd64:
|
|
||||||
vmImage: 'ubuntu-20.04'
|
|
||||||
CPU: amd64
|
|
||||||
# regularly breaks, refs bug #17325
|
|
||||||
Linux_i386:
|
|
||||||
# on 'ubuntu-16.04' (not supported anymore anyways) it errored with:
|
|
||||||
# g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
|
|
||||||
vmImage: 'ubuntu-18.04'
|
|
||||||
CPU: i386
|
|
||||||
OSX_amd64:
|
|
||||||
vmImage: 'macOS-11'
|
|
||||||
CPU: amd64
|
|
||||||
OSX_amd64_cpp:
|
|
||||||
vmImage: 'macOS-11'
|
|
||||||
CPU: amd64
|
|
||||||
NIM_COMPILE_TO_CPP: true
|
|
||||||
Windows_amd64_batch0_3:
|
|
||||||
vmImage: 'windows-2019'
|
|
||||||
CPU: amd64
|
|
||||||
# see also: `NIM_TEST_PACKAGES`
|
|
||||||
NIM_TESTAMENT_BATCH: "0_3"
|
|
||||||
Windows_amd64_batch1_3:
|
|
||||||
vmImage: 'windows-2019'
|
|
||||||
CPU: amd64
|
|
||||||
NIM_TESTAMENT_BATCH: "1_3"
|
|
||||||
Windows_amd64_batch2_3:
|
|
||||||
vmImage: 'windows-2019'
|
|
||||||
CPU: amd64
|
|
||||||
NIM_TESTAMENT_BATCH: "2_3"
|
|
||||||
|
|
||||||
pool:
|
|
||||||
vmImage: $(vmImage)
|
|
||||||
|
|
||||||
workspace:
|
|
||||||
clean: all
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- bash: git config --global core.autocrlf false
|
|
||||||
displayName: 'Disable auto conversion to CRLF by git (Windows-only)'
|
|
||||||
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
|
|
||||||
|
|
||||||
- checkout: self
|
|
||||||
fetchDepth: 2 # see D20210329T004830
|
|
||||||
|
|
||||||
- bash: |
|
|
||||||
set -e
|
|
||||||
. ci/funs.sh
|
|
||||||
if nimIsCiSkip; then
|
|
||||||
echo '##vso[task.setvariable variable=skipci]true'
|
|
||||||
fi
|
|
||||||
displayName: 'Check whether to skip CI'
|
|
||||||
|
|
||||||
- task: NodeTool@0
|
|
||||||
inputs:
|
|
||||||
versionSpec: '16.x'
|
|
||||||
displayName: 'Install node.js 16.x'
|
|
||||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
|
||||||
|
|
||||||
- bash: |
|
|
||||||
set -e
|
|
||||||
. ci/funs.sh
|
|
||||||
echo_run sudo apt-fast update -qq
|
|
||||||
DEBIAN_FRONTEND='noninteractive' \
|
|
||||||
echo_run sudo apt-fast install --no-install-recommends -yq \
|
|
||||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
|
||||||
displayName: 'Install dependencies (amd64 Linux)'
|
|
||||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64'))
|
|
||||||
|
|
||||||
- bash: |
|
|
||||||
set -e
|
|
||||||
. ci/funs.sh
|
|
||||||
echo_run sudo dpkg --add-architecture i386
|
|
||||||
# Downgrade llvm:
|
|
||||||
# - llvm has to be downgraded to have 32bit version installed for sfml.
|
|
||||||
|
|
||||||
cat << EOF | sudo tee /etc/apt/preferences.d/pin-to-rel
|
|
||||||
Package: libllvm6.0
|
|
||||||
Pin: origin "azure.archive.ubuntu.com"
|
|
||||||
Pin-Priority: 1001
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# echo_run sudo apt-fast update -qq
|
|
||||||
echo_run sudo apt-fast update -qq || echo "failed, see bug #17343"
|
|
||||||
# `:i386` (e.g. in `libffi-dev:i386`) is needed otherwise you may get:
|
|
||||||
# `could not load: libffi.so` during dynamic loading.
|
|
||||||
DEBIAN_FRONTEND='noninteractive' \
|
|
||||||
echo_run sudo apt-fast install --no-install-recommends --allow-downgrades -yq \
|
|
||||||
g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \
|
|
||||||
libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 libffi-dev:i386
|
|
||||||
|
|
||||||
cat << EOF > bin/gcc
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
exec $(which gcc) -m32 "\$@"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat << EOF > bin/g++
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
exec $(which g++) -m32 "\$@"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo_run chmod 755 bin/gcc
|
|
||||||
echo_run chmod 755 bin/g++
|
|
||||||
|
|
||||||
displayName: 'Install dependencies (i386 Linux)'
|
|
||||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386'))
|
|
||||||
|
|
||||||
- bash: brew install boehmgc make sfml
|
|
||||||
displayName: 'Install dependencies (OSX)'
|
|
||||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
|
|
||||||
|
|
||||||
- bash: |
|
|
||||||
set -e
|
|
||||||
. ci/funs.sh
|
|
||||||
nimInternalInstallDepsWindows
|
|
||||||
echo_run echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin'
|
|
||||||
|
|
||||||
displayName: 'Install dependencies (Windows)'
|
|
||||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Windows_NT'))
|
|
||||||
|
|
||||||
- bash: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/bin'
|
|
||||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
|
||||||
displayName: 'Add build binaries to PATH'
|
|
||||||
|
|
||||||
- bash: . ci/funs.sh && nimCiSystemInfo
|
|
||||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
|
||||||
displayName: 'System information'
|
|
||||||
|
|
||||||
- bash: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu=$(CPU)
|
|
||||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
|
||||||
displayName: 'Build csourcesAny'
|
|
||||||
|
|
||||||
# this could be revived if performance justifies it (needs a few updates)
|
|
||||||
# - task: Cache@2
|
|
||||||
# inputs:
|
|
||||||
# key: 'csourcesAny | "$(Agent.OS)" | $(CPU) | $(csources_version)'
|
|
||||||
# path: $(nim_csources)
|
|
||||||
# condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
|
||||||
# displayName: 'Restore built csourcesAny'
|
|
||||||
|
|
||||||
- bash: . ci/funs.sh && nimInternalBuildKochAndRunCI
|
|
||||||
# would could also show on failure: echo '##vso[task.complete result=Failed]'
|
|
||||||
condition: and(succeeded(), eq(variables['skipci'], 'false'))
|
|
||||||
displayName: 'koch, Run CI'
|
|
||||||
env:
|
|
||||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
This file keeps several tools from deleting this subdirectory.
|
This file keeps several tools from deleting this subdirectory.
|
||||||
|
|
|
||||||
25
bin/nim-gdb
25
bin/nim-gdb
|
|
@ -1,25 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Exit if anything fails
|
|
||||||
set -e
|
|
||||||
|
|
||||||
which nim > /dev/null || (echo "nim not in PATH"; exit 1)
|
|
||||||
which gdb > /dev/null || (echo "gdb not in PATH"; exit 1)
|
|
||||||
which readlink > /dev/null || (echo "readlink not in PATH."; exit 1)
|
|
||||||
|
|
||||||
if [[ $(uname -s) == Darwin || $(uname -s) == *BSD ]]; then
|
|
||||||
NIM_SYSROOT=$(dirname $(dirname $(readlink -f $(which nim))))
|
|
||||||
else
|
|
||||||
NIM_SYSROOT=$(dirname $(dirname $(readlink -e $(which nim))))
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Find out where the pretty printer Python module is
|
|
||||||
GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/debug/nim-gdb.py"
|
|
||||||
|
|
||||||
# Run GDB with the additional arguments that load the pretty printers
|
|
||||||
# Set the environment variable `NIM_GDB` to overwrite the call to a
|
|
||||||
# different/specific command (defaults to `gdb`).
|
|
||||||
NIM_GDB="${NIM_GDB:-gdb}"
|
|
||||||
# exec replaces the new process of bash with gdb. It is always good to
|
|
||||||
# have fewer processes.
|
|
||||||
exec "${NIM_GDB}" -eval-command="source $GDB_PYTHON_MODULE_PATH" "$@"
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
@echo off
|
|
||||||
for %%i in (nim.exe) do (set NIM_BIN=%%~dp$PATH:i)
|
|
||||||
|
|
||||||
for %%i in ("%NIM_BIN%\..\") do (set NIM_ROOT=%%~fi)
|
|
||||||
|
|
||||||
set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\debug\nim-gdb.py
|
|
||||||
set @NIM_GDB=gdb.exe
|
|
||||||
|
|
||||||
@echo source %@GDB_PYTHON_MODULE_PATH%> wingdbcommand.txt
|
|
||||||
%@NIM_GDB% --command="wingdbcommand.txt" %*
|
|
||||||
del wingdbcommand.txt /f /q
|
|
||||||
|
|
||||||
EXIT /B %ERRORLEVEL%
|
|
||||||
@echo on
|
|
||||||
19
build.sh
Executable file
19
build.sh
Executable file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
if [ ! -e csources/.git ]; then
|
||||||
|
git submodule update --init --depth 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "csources"
|
||||||
|
sh build.sh
|
||||||
|
cd ".."
|
||||||
|
|
||||||
|
./bin/nim c koch
|
||||||
|
./koch boot -d:release
|
||||||
|
|
||||||
|
cp -f install.sh.template install.sh
|
||||||
|
chmod +x install.sh
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
@ -1 +1 @@
|
||||||
This file keeps several tools from deleting this subdirectory.
|
This file keeps several tools from deleting this subdirectory.
|
||||||
|
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
@echo off
|
|
||||||
rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
|
||||||
rem Build development version of the compiler; can be rerun safely
|
|
||||||
rem bare bones version of ci/funs.sh adapted for windows.
|
|
||||||
|
|
||||||
rem Read in some common shared variables (shared with other tools),
|
|
||||||
rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file
|
|
||||||
for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H
|
|
||||||
SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe
|
|
||||||
echo "building from csources: %nim_csources%"
|
|
||||||
|
|
||||||
if not exist %nim_csourcesDir% (
|
|
||||||
git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir%
|
|
||||||
)
|
|
||||||
|
|
||||||
if not exist %nim_csources% (
|
|
||||||
cd %nim_csourcesDir%
|
|
||||||
git checkout %nim_csourcesHash%
|
|
||||||
echo "%PROCESSOR_ARCHITECTURE%"
|
|
||||||
if "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
|
|
||||||
SET ARCH=64
|
|
||||||
)
|
|
||||||
CALL build.bat
|
|
||||||
cd ..
|
|
||||||
copy /y bin\nim.exe %nim_csources%
|
|
||||||
)
|
|
||||||
bin\nim.exe c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
|
||||||
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
|
||||||
koch tools --skipUserCfg --skipParentCfg --hints:off
|
|
||||||
17
build_all.sh
17
build_all.sh
|
|
@ -1,17 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
|
||||||
|
|
||||||
# build development version of the compiler; can be rerun safely.
|
|
||||||
# arguments can be passed, e.g.:
|
|
||||||
# CC=gcc ucpu=amd64 uos=darwin
|
|
||||||
|
|
||||||
set -u # error on undefined variables
|
|
||||||
set -e # exit on first error
|
|
||||||
|
|
||||||
. ci/funs.sh
|
|
||||||
nimBuildCsourcesIfNeeded "$@"
|
|
||||||
|
|
||||||
echo_run bin/nim c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
|
||||||
echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
|
||||||
echo_run ./koch tools --skipUserCfg --skipParentCfg --hints:off
|
|
||||||
|
|
||||||
31
changelog.md
31
changelog.md
|
|
@ -1,31 +0,0 @@
|
||||||
# v2.2.0 - yyyy-mm-dd
|
|
||||||
|
|
||||||
|
|
||||||
## Changes affecting backward compatibility
|
|
||||||
|
|
||||||
|
|
||||||
## Standard library additions and changes
|
|
||||||
|
|
||||||
[//]: # "Changes:"
|
|
||||||
|
|
||||||
|
|
||||||
[//]: # "Additions:"
|
|
||||||
- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes.
|
|
||||||
|
|
||||||
[//]: # "Deprecations:"
|
|
||||||
|
|
||||||
|
|
||||||
[//]: # "Removals:"
|
|
||||||
|
|
||||||
|
|
||||||
## Language changes
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Compiler changes
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Tool changes
|
|
||||||
|
|
||||||
|
|
@ -1,663 +0,0 @@
|
||||||
## v0.18.0 - 01/03/2018
|
|
||||||
|
|
||||||
### Changes affecting backwards compatibility
|
|
||||||
|
|
||||||
#### Breaking changes in the standard library
|
|
||||||
|
|
||||||
- The ``[]`` proc for strings now raises an ``IndexError`` exception when
|
|
||||||
the specified slice is out of bounds. See issue
|
|
||||||
[#6223](https://github.com/nim-lang/Nim/issues/6223) for more details.
|
|
||||||
You can use ``substr(str, start, finish)`` to get the old behaviour back,
|
|
||||||
see [this commit](https://github.com/nim-lang/nimbot/commit/98cc031a27ea89947daa7f0bb536bcf86462941f) for an example.
|
|
||||||
|
|
||||||
- ``strutils.split`` and ``strutils.rsplit`` with an empty string and a
|
|
||||||
separator now returns that empty string.
|
|
||||||
See issue [#4377](https://github.com/nim-lang/Nim/issues/4377).
|
|
||||||
|
|
||||||
- Arrays of char cannot be converted to ``cstring`` anymore, pointers to
|
|
||||||
arrays of char can! This means ``$`` for arrays can finally exist
|
|
||||||
in ``system.nim`` and do the right thing. This means ``$myArrayOfChar`` changed
|
|
||||||
its behaviour! Compile with ``-d:nimNoArrayToString`` to see where to fix your
|
|
||||||
code.
|
|
||||||
|
|
||||||
- `reExtended` is no longer default for the `re` constructor in the `re`
|
|
||||||
module.
|
|
||||||
|
|
||||||
- The behavior of ``$`` has been changed for all standard library collections. The
|
|
||||||
collection-to-string implementations now perform proper quoting and escaping of
|
|
||||||
strings and chars.
|
|
||||||
|
|
||||||
- `newAsyncSocket` taking an `AsyncFD` now runs `setBlocking(false)` on the
|
|
||||||
fd.
|
|
||||||
|
|
||||||
- ``mod`` and bitwise ``and`` do not produce ``range`` subtypes anymore. This
|
|
||||||
turned out to be more harmful than helpful and the language is simpler
|
|
||||||
without this special typing rule.
|
|
||||||
|
|
||||||
- ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero
|
|
||||||
precision digits. The previous ``precision = 0`` behavior (default formatting)
|
|
||||||
is now available via ``precision = -1``.
|
|
||||||
|
|
||||||
- Moved from stdlib into Nimble packages:
|
|
||||||
- [``basic2d``](https://github.com/nim-lang/basic2d)
|
|
||||||
_deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
|
|
||||||
- [``basic3d``](https://github.com/nim-lang/basic3d)
|
|
||||||
_deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
|
|
||||||
- [``gentabs``](https://github.com/lcrees/gentabs)
|
|
||||||
- [``libuv``](https://github.com/lcrees/libuv)
|
|
||||||
- [``numeric``](https://github.com/lcrees/polynumeric)
|
|
||||||
- [``poly``](https://github.com/lcrees/polynumeric)
|
|
||||||
- [``pdcurses``](https://github.com/lcrees/pdcurses)
|
|
||||||
- [``romans``](https://github.com/lcrees/romans)
|
|
||||||
- [``libsvm``](https://github.com/nim-lang/libsvm_legacy)
|
|
||||||
- [``joyent_http_parser``](https://github.com/nim-lang/joyent_http_parser)
|
|
||||||
|
|
||||||
- Proc [toCountTable](https://nim-lang.org/docs/tables.html#toCountTable,openArray[A])
|
|
||||||
now produces a `CountTable` with values correspoding to the number of occurrences
|
|
||||||
of the key in the input. It used to produce a table with all values set to `1`.
|
|
||||||
|
|
||||||
Counting occurrences in a sequence used to be:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
let mySeq = @[1, 2, 1, 3, 1, 4]
|
|
||||||
var myCounter = initCountTable[int]()
|
|
||||||
|
|
||||||
for item in mySeq:
|
|
||||||
myCounter.inc item
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, you can simply do:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
let
|
|
||||||
mySeq = @[1, 2, 1, 3, 1, 4]
|
|
||||||
myCounter = mySeq.toCountTable()
|
|
||||||
```
|
|
||||||
|
|
||||||
- If you use ``--dynlibOverride:ssl`` with OpenSSL 1.0.x, you now have to
|
|
||||||
define ``openssl10`` symbol (``-d:openssl10``). By default OpenSSL 1.1.x is
|
|
||||||
assumed.
|
|
||||||
|
|
||||||
- ``newNativeSocket`` is now named ``createNativeSocket``.
|
|
||||||
|
|
||||||
- ``newAsyncNativeSocket`` is now named ``createAsyncNativeSocket``
|
|
||||||
and it no longer raises an OS error but returns an ``osInvalidSocket`` when
|
|
||||||
creation fails.
|
|
||||||
|
|
||||||
- The ``securehash`` module is now deprecated. Instead import ``std / sha1``.
|
|
||||||
|
|
||||||
- The ``readPasswordFromStdin`` proc has been moved from the ``rdstdin``
|
|
||||||
to the ``terminal`` module, thus it does not depend on linenoise anymore.
|
|
||||||
|
|
||||||
#### Breaking changes in the compiler
|
|
||||||
|
|
||||||
- ``\n`` is now only the single line feed character like in most
|
|
||||||
other programming languages. The new platform specific newline escape sequence is
|
|
||||||
written as ``\p``. This change only affects the Windows platform.
|
|
||||||
|
|
||||||
- The overloading rules changed slightly so that constrained generics are
|
|
||||||
preferred over unconstrained generics. (Bug #6526)
|
|
||||||
|
|
||||||
- We changed how array accesses "from backwards" like ``a[^1]`` or ``a[0..^1]`` are
|
|
||||||
implemented. These are now implemented purely in ``system.nim`` without compiler
|
|
||||||
support. There is a new "heterogeneous" slice type ``system.HSlice`` that takes 2
|
|
||||||
generic parameters which can be ``BackwardsIndex`` indices. ``BackwardsIndex`` is
|
|
||||||
produced by ``system.^``.
|
|
||||||
This means if you overload ``[]`` or ``[]=`` you need to ensure they also work
|
|
||||||
with ``system.BackwardsIndex`` (if applicable for the accessors).
|
|
||||||
|
|
||||||
- The parsing rules of ``if`` expressions were changed so that multiple
|
|
||||||
statements are allowed in the branches. We found few code examples that
|
|
||||||
now fail because of this change, but here is one:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
t[ti] = if exp_negative: '-' else: '+'; inc(ti)
|
|
||||||
```
|
|
||||||
|
|
||||||
This now needs to be written as:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
|
|
||||||
```
|
|
||||||
|
|
||||||
- The experimental overloading of the dot ``.`` operators now take
|
|
||||||
an ``untyped``` parameter as the field name, it used to be
|
|
||||||
a ``static[string]``. You can use ``when defined(nimNewDot)`` to make
|
|
||||||
your code work with both old and new Nim versions.
|
|
||||||
See [special-operators](https://nim-lang.org/docs/manual.html#special-operators)
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
- ``yield`` (or ``await`` which is mapped to ``yield``) never worked reliably
|
|
||||||
in an array, seq or object constructor and is now prevented at compile-time.
|
|
||||||
|
|
||||||
### Library additions
|
|
||||||
|
|
||||||
- **Added ``sequtils.mapLiterals`` for easier construction of array and tuple literals.**
|
|
||||||
|
|
||||||
- Added ``system.runnableExamples`` to make examples in Nim's documentation easier
|
|
||||||
to write and test. The examples are tested as the last step of
|
|
||||||
``nim doc``.
|
|
||||||
|
|
||||||
- Implemented ``getIoHandler`` proc in the ``asyncdispatch`` module that allows
|
|
||||||
you to retrieve the underlying IO Completion Port or ``Selector[AsyncData]``
|
|
||||||
object in the specified dispatcher.
|
|
||||||
|
|
||||||
- For string formatting / interpolation a new module
|
|
||||||
called [strformat](https://nim-lang.org/docs/strformat.html) has been added
|
|
||||||
to the stdlib.
|
|
||||||
|
|
||||||
- The `ReadyKey` type in the selectors module now contains an ``errorCode``
|
|
||||||
field to help distinguish between ``Event.Error`` events.
|
|
||||||
|
|
||||||
- Implemented an `accept` proc that works on a `SocketHandle` in
|
|
||||||
``nativesockets``.
|
|
||||||
|
|
||||||
- Added ``algorithm.rotateLeft``.
|
|
||||||
|
|
||||||
- Added ``typetraits.$`` as an alias for ``typetraits.name``.
|
|
||||||
|
|
||||||
- Added ``system.getStackTraceEntries`` that allows you to access the stack
|
|
||||||
trace in a structured manner without string parsing.
|
|
||||||
|
|
||||||
- Added ``parseutils.parseSaturatedNatural``.
|
|
||||||
|
|
||||||
- Added ``macros.unpackVarargs``.
|
|
||||||
|
|
||||||
- Added support for asynchronous programming for the JavaScript backend using
|
|
||||||
the `asyncjs` module.
|
|
||||||
|
|
||||||
- Added true color support for some terminals. Example:
|
|
||||||
```nim
|
|
||||||
import colors, terminal
|
|
||||||
|
|
||||||
const Nim = "Efficient and expressive programming."
|
|
||||||
|
|
||||||
var
|
|
||||||
fg = colYellow
|
|
||||||
bg = colBlue
|
|
||||||
int = 1.0
|
|
||||||
|
|
||||||
enableTrueColors()
|
|
||||||
|
|
||||||
for i in 1..15:
|
|
||||||
styledEcho bgColor, bg, fgColor, fg, Nim, resetStyle
|
|
||||||
int -= 0.01
|
|
||||||
fg = intensity(fg, int)
|
|
||||||
|
|
||||||
setForegroundColor colRed
|
|
||||||
setBackgroundColor colGreen
|
|
||||||
styledEcho "Red on Green.", resetStyle
|
|
||||||
```
|
|
||||||
|
|
||||||
### Library changes
|
|
||||||
|
|
||||||
- ``echo`` now works with strings that contain ``\0`` (the binary zero is not
|
|
||||||
shown) and ``nil`` strings are equal to empty strings.
|
|
||||||
|
|
||||||
- JSON: Deprecated `getBVal`, `getFNum`, and `getNum` in favour of
|
|
||||||
`getBool`, `getFloat`, `getBiggestInt`. A new `getInt` procedure was also
|
|
||||||
added.
|
|
||||||
|
|
||||||
- ``rationals.toRational`` now uses an algorithm based on continued fractions.
|
|
||||||
This means its results are more precise and it can't run into an infinite loop
|
|
||||||
anymore.
|
|
||||||
|
|
||||||
- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
|
|
||||||
what to return if the environment variable does not exist.
|
|
||||||
|
|
||||||
- The ``random`` procs in ``random.nim`` have all been deprecated. Instead use
|
|
||||||
the new ``rand`` procs. The module now exports the state of the random
|
|
||||||
number generator as type ``Rand`` so multiple threads can easily use their
|
|
||||||
own random number generators that do not require locking. For more information
|
|
||||||
about this rename see issue [#6934](https://github.com/nim-lang/Nim/issues/6934)
|
|
||||||
|
|
||||||
- ``writeStackTrace`` is now proclaimed to have no IO effect (even though it does)
|
|
||||||
so that it is more useful for debugging purposes.
|
|
||||||
|
|
||||||
- ``db_mysql`` module: ``DbConn`` is now a ``distinct`` type that doesn't expose the
|
|
||||||
details of the underlying ``PMySQL`` type.
|
|
||||||
|
|
||||||
- ``parseopt2`` is now deprecated, use ``parseopt`` instead.
|
|
||||||
|
|
||||||
### Language additions
|
|
||||||
|
|
||||||
- It is now possible to forward declare object types so that mutually
|
|
||||||
recursive types can be created across module boundaries. See
|
|
||||||
[package level objects](https://nim-lang.org/docs/manual.html#package-level-objects)
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
- Added support for casting between integers of same bitsize in VM (compile time and nimscript).
|
|
||||||
This allows to, among other things, reinterpret signed integers as unsigned.
|
|
||||||
|
|
||||||
- Custom pragmas are now supported using pragma ``pragma``, please see language
|
|
||||||
manual for details.
|
|
||||||
|
|
||||||
- Standard library modules can now also be imported via the ``std`` pseudo-directory.
|
|
||||||
This is useful in order to distinguish between standard library and nimble package
|
|
||||||
imports:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
import std / [strutils, os, osproc]
|
|
||||||
import someNimblePackage / [strutils, os]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Language changes
|
|
||||||
|
|
||||||
- The **unary** ``<`` is now deprecated, for ``.. <`` use ``..<`` for other usages
|
|
||||||
use the ``pred`` proc.
|
|
||||||
|
|
||||||
- Bodies of ``for`` loops now get their own scope:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
# now compiles:
|
|
||||||
for i in 0..4:
|
|
||||||
let i = i + 1
|
|
||||||
echo i
|
|
||||||
```
|
|
||||||
|
|
||||||
- To make Nim even more robust the system iterators ``..`` and ``countup``
|
|
||||||
now only accept a single generic type ``T``. This means the following code
|
|
||||||
doesn't die with an "out of range" error anymore:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
var b = 5.Natural
|
|
||||||
var a = -5
|
|
||||||
for i in a..b:
|
|
||||||
echo i
|
|
||||||
```
|
|
||||||
|
|
||||||
- ``atomic`` and ``generic`` are no longer keywords in Nim. ``generic`` used to be
|
|
||||||
an alias for ``concept``, ``atomic`` was not used for anything.
|
|
||||||
|
|
||||||
- The memory manager now uses a variant of the TLSF algorithm that has much
|
|
||||||
better memory fragmentation behaviour. According
|
|
||||||
to [http://www.gii.upv.es/tlsf/](http://www.gii.upv.es/tlsf/) the maximum
|
|
||||||
fragmentation measured is lower than 25%. As a nice bonus ``alloc`` and
|
|
||||||
``dealloc`` became O(1) operations.
|
|
||||||
|
|
||||||
- The compiler is now more consistent in its treatment of ambiguous symbols:
|
|
||||||
Types that shadow procs and vice versa are marked as ambiguous (bug #6693).
|
|
||||||
|
|
||||||
- codegenDecl pragma now works for the JavaScript backend. It returns an empty
|
|
||||||
string for function return type placeholders.
|
|
||||||
|
|
||||||
- Extra semantic checks for procs with noreturn pragma: return type is not allowed,
|
|
||||||
statements after call to noreturn procs are no longer allowed.
|
|
||||||
|
|
||||||
- Noreturn proc calls and raising exceptions branches are now skipped during common type
|
|
||||||
deduction in ``if`` and ``case`` expressions. The following code snippets now compile:
|
|
||||||
```nim
|
|
||||||
import strutils
|
|
||||||
let str = "Y"
|
|
||||||
let a = case str:
|
|
||||||
of "Y": true
|
|
||||||
of "N": false
|
|
||||||
else: raise newException(ValueError, "Invalid boolean")
|
|
||||||
let b = case str:
|
|
||||||
of nil, "": raise newException(ValueError, "Invalid boolean")
|
|
||||||
elif str.startsWith("Y"): true
|
|
||||||
elif str.startsWith("N"): false
|
|
||||||
else: false
|
|
||||||
let c = if str == "Y": true
|
|
||||||
elif str == "N": false
|
|
||||||
else:
|
|
||||||
echo "invalid bool"
|
|
||||||
quit("this is the end")
|
|
||||||
```
|
|
||||||
|
|
||||||
- Pragmas now support call syntax, for example: ``{.exportc"myname".}`` and
|
|
||||||
``{.exportc("myname").}``
|
|
||||||
|
|
||||||
- The ``deprecated`` pragma now supports a user-definable warning message for procs.
|
|
||||||
|
|
||||||
```nim
|
|
||||||
proc bar {.deprecated: "use foo instead".} =
|
|
||||||
return
|
|
||||||
|
|
||||||
bar()
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tool changes
|
|
||||||
|
|
||||||
- The ``nim doc`` command is now an alias for ``nim doc2``, the second version of
|
|
||||||
the documentation generator. The old version 1 can still be accessed
|
|
||||||
via the new ``nim doc0`` command.
|
|
||||||
|
|
||||||
- Nim's ``rst2html`` command now supports the testing of code snippets via an RST
|
|
||||||
extension that we called ``:test:``::
|
|
||||||
|
|
||||||
```rst
|
|
||||||
.. code-block:: nim
|
|
||||||
:test:
|
|
||||||
# shows how the 'if' statement works
|
|
||||||
if true: echo "yes"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compiler changes
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
||||||
- Fixed "ReraiseError when using try/except within finally block"
|
|
||||||
([#5871](https://github.com/nim-lang/Nim/issues/5871))
|
|
||||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
|
||||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
|
||||||
- Fixed "JSON % operator can fail in extern procs with dynamic types"
|
|
||||||
([#6385](https://github.com/nim-lang/Nim/issues/6385))
|
|
||||||
- Fixed ""intVal is not accessible" in VM"
|
|
||||||
([#6083](https://github.com/nim-lang/Nim/issues/6083))
|
|
||||||
- Fixed "Add excl for OrderedSet"
|
|
||||||
([#2467](https://github.com/nim-lang/Nim/issues/2467))
|
|
||||||
- Fixed "newSeqOfCap actually doesn't reserve memory"
|
|
||||||
([#6403](https://github.com/nim-lang/Nim/issues/6403))
|
|
||||||
- Fixed "[Regression] Nim segfaults"
|
|
||||||
([#6435](https://github.com/nim-lang/Nim/issues/6435))
|
|
||||||
- Fixed "Seq assignment is slower than expected"
|
|
||||||
([#6433](https://github.com/nim-lang/Nim/issues/6433))
|
|
||||||
- Fixed "json module issues with empty dicts and lists"
|
|
||||||
([#6438](https://github.com/nim-lang/Nim/issues/6438))
|
|
||||||
- Fixed "mingw installed via finish.exe fails to link if Nim located in path with whitespace"
|
|
||||||
([#6452](https://github.com/nim-lang/Nim/issues/6452))
|
|
||||||
- Fixed "unittest.check does not perform short-circuit evaluation"
|
|
||||||
([#5784](https://github.com/nim-lang/Nim/issues/5784))
|
|
||||||
- Fixed "Error while concatenating an array of chars."
|
|
||||||
([#5861](https://github.com/nim-lang/Nim/issues/5861))
|
|
||||||
- Fixed "range initialization: [ProveInit] hint: Cannot prove that"
|
|
||||||
([#6474](https://github.com/nim-lang/Nim/issues/6474))
|
|
||||||
- Fixed "scanf can call procs with side-effects multiple times"
|
|
||||||
([#6487](https://github.com/nim-lang/Nim/issues/6487))
|
|
||||||
- Fixed "gcsafe detection problem"
|
|
||||||
([#5620](https://github.com/nim-lang/Nim/issues/5620))
|
|
||||||
- Fixed "C++ codegen: `mitems` generates invalid code."
|
|
||||||
([#4910](https://github.com/nim-lang/Nim/issues/4910))
|
|
||||||
- Fixed "strange runtime behavior on macOS"
|
|
||||||
([#6496](https://github.com/nim-lang/Nim/issues/6496))
|
|
||||||
- Fixed "stdtmpl: invalid indentation after a line ending in question mark"
|
|
||||||
([#5070](https://github.com/nim-lang/Nim/issues/5070))
|
|
||||||
- Fixed "Windows: NAN troubles on c backend"
|
|
||||||
([#6511](https://github.com/nim-lang/Nim/issues/6511))
|
|
||||||
- Fixed "lib/nim/system/cellsets.nim(33, 31) Error: type mismatch while attempting to compile for 16bit CPUs"
|
|
||||||
([#3558](https://github.com/nim-lang/Nim/issues/3558))
|
|
||||||
- Fixed "Can't compile dynlib with ``-d:useNimRtl`` and ``--threads:on``"
|
|
||||||
([#5143](https://github.com/nim-lang/Nim/issues/5143))
|
|
||||||
- Fixed "var s = @[0,1,2,...] can generate thousand of single assignments in C code"
|
|
||||||
([#5007](https://github.com/nim-lang/Nim/issues/5007))
|
|
||||||
- Fixed "`echo` discards everything after a null character"
|
|
||||||
([#1137](https://github.com/nim-lang/Nim/issues/1137))
|
|
||||||
- Fixed "Turn off reExtended by default"
|
|
||||||
([#5627](https://github.com/nim-lang/Nim/issues/5627))
|
|
||||||
- Fixed "Bad Links in docs/backends.html"
|
|
||||||
([#5914](https://github.com/nim-lang/Nim/issues/5914))
|
|
||||||
- Fixed "Index out of bounds error in db_postgres when executing non parameter-substituted queries containing "?""
|
|
||||||
([#6571](https://github.com/nim-lang/Nim/issues/6571))
|
|
||||||
- Fixed "Please add pipe2 support to posix stdlib"
|
|
||||||
([#6553](https://github.com/nim-lang/Nim/issues/6553))
|
|
||||||
- Fixed "Return semantics vary depending on return style"
|
|
||||||
([#6422](https://github.com/nim-lang/Nim/issues/6422))
|
|
||||||
- Fixed "parsecsv.open reports SIGSEGV when calling 'open' on missing file"
|
|
||||||
([#6148](https://github.com/nim-lang/Nim/issues/6148))
|
|
||||||
- Fixed "VCC: Nim generates non-compilable code for system.nim"
|
|
||||||
([#6606](https://github.com/nim-lang/Nim/issues/6606))
|
|
||||||
- Fixed "Generic subtype matches worse than a generic"
|
|
||||||
([#6526](https://github.com/nim-lang/Nim/issues/6526))
|
|
||||||
- Fixed "formatFloat inconsistent scientific notation"
|
|
||||||
([#6589](https://github.com/nim-lang/Nim/issues/6589))
|
|
||||||
- Fixed "Generated c code calls function twice"
|
|
||||||
([#6292](https://github.com/nim-lang/Nim/issues/6292))
|
|
||||||
- Fixed "Range type inference leads to counter-intuitive behvaiour"
|
|
||||||
([#5854](https://github.com/nim-lang/Nim/issues/5854))
|
|
||||||
- Fixed "New backward indexing is too limited"
|
|
||||||
([#6631](https://github.com/nim-lang/Nim/issues/6631))
|
|
||||||
- Fixed "Table usage in a macro (SIGSEGV: Illegal storage access.)"
|
|
||||||
([#1860](https://github.com/nim-lang/Nim/issues/1860))
|
|
||||||
- Fixed "Incorrect deprecation error"
|
|
||||||
([#6634](https://github.com/nim-lang/Nim/issues/6634))
|
|
||||||
- Fixed "Wrong indices in arrays not starting with 0"
|
|
||||||
([#6675](https://github.com/nim-lang/Nim/issues/6675))
|
|
||||||
- Fixed "if expressions"
|
|
||||||
([#6609](https://github.com/nim-lang/Nim/issues/6609))
|
|
||||||
- Fixed "BackwardsIndex: converter + `[]` + unrelated type[^1]: lib/system.nim(3536, 3) Error"
|
|
||||||
([#6692](https://github.com/nim-lang/Nim/issues/6692))
|
|
||||||
- Fixed "BackwardsIndex: converter + `[]` + unrelated type[^1]: lib/system.nim(3536, 3) Error"
|
|
||||||
([#6692](https://github.com/nim-lang/Nim/issues/6692))
|
|
||||||
- Fixed "js backend 0.17.3: array bounds check for non zero based arrays is buggy"
|
|
||||||
([#6532](https://github.com/nim-lang/Nim/issues/6532))
|
|
||||||
- Fixed "HttpClient's new API doesn't work through a proxy for https URLs"
|
|
||||||
([#6685](https://github.com/nim-lang/Nim/issues/6685))
|
|
||||||
- Fixed "isServing isn't declared and isn't compiling"
|
|
||||||
([#6707](https://github.com/nim-lang/Nim/issues/6707))
|
|
||||||
- Fixed "[Regression] value out of range"
|
|
||||||
([#6710](https://github.com/nim-lang/Nim/issues/6710))
|
|
||||||
|
|
||||||
- Fixed "Error when using `multisync` macro"
|
|
||||||
([#6708](https://github.com/nim-lang/Nim/issues/6708))
|
|
||||||
|
|
||||||
- Fixed "formatFloat inconsistent scientific notation"
|
|
||||||
([#6589](https://github.com/nim-lang/Nim/issues/6589))
|
|
||||||
- Fixed "Using : (constructor arguments) for passing values to functions with default arguments causes a compiler crash."
|
|
||||||
([#6765](https://github.com/nim-lang/Nim/issues/6765))
|
|
||||||
- Fixed "In-place object initialization leads to vcc incompatible code"
|
|
||||||
([#6757](https://github.com/nim-lang/Nim/issues/6757))
|
|
||||||
- Fixed "Improve parseCookies doc"
|
|
||||||
([#5721](https://github.com/nim-lang/Nim/issues/5721))
|
|
||||||
- Fixed "Parser regression with nested do notation inside conditional"
|
|
||||||
([#6166](https://github.com/nim-lang/Nim/issues/6166))
|
|
||||||
- Fixed "Request for better error message"
|
|
||||||
([#6776](https://github.com/nim-lang/Nim/issues/6776))
|
|
||||||
- Fixed "Testament tester does not execute test with `exitcode` only"
|
|
||||||
([#6775](https://github.com/nim-lang/Nim/issues/6775))
|
|
||||||
- Fixed "JS integer division off by one"
|
|
||||||
([#6753](https://github.com/nim-lang/Nim/issues/6753))
|
|
||||||
- Fixed "Regression: cannot prove not nil"
|
|
||||||
([#5781](https://github.com/nim-lang/Nim/issues/5781))
|
|
||||||
- Fixed "SIGSEGV: Illegal storage access. (Attempt to read from nil?) in generic proc"
|
|
||||||
([#6073](https://github.com/nim-lang/Nim/issues/6073))
|
|
||||||
- Fixed "Request for better error message"
|
|
||||||
([#6776](https://github.com/nim-lang/Nim/issues/6776))
|
|
||||||
- Fixed "Nim #head: sorting via reference hangs compiler"
|
|
||||||
([#6724](https://github.com/nim-lang/Nim/issues/6724))
|
|
||||||
- Fixed "Cannot cast pointer to char in cpp"
|
|
||||||
([#5979](https://github.com/nim-lang/Nim/issues/5979))
|
|
||||||
- Fixed "asynchttpserver replies with several errors on single request"
|
|
||||||
([#6386](https://github.com/nim-lang/Nim/issues/6386))
|
|
||||||
- Fixed "object variants superclass trigger bad codegen"
|
|
||||||
([#5521](https://github.com/nim-lang/Nim/issues/5521))
|
|
||||||
- Fixed "JS integer division off by one"
|
|
||||||
([#6753](https://github.com/nim-lang/Nim/issues/6753))
|
|
||||||
- Fixed "js backend compiler crash with tables indexed by certain types"
|
|
||||||
([#6568](https://github.com/nim-lang/Nim/issues/6568))
|
|
||||||
- Fixed "Jsgen bug with is"
|
|
||||||
([#6445](https://github.com/nim-lang/Nim/issues/6445))
|
|
||||||
- Fixed "Subrange definition with ..<"
|
|
||||||
([#6788](https://github.com/nim-lang/Nim/issues/6788))
|
|
||||||
- Fixed "fields not initialized: array with enum index type as object field."
|
|
||||||
([#6682](https://github.com/nim-lang/Nim/issues/6682))
|
|
||||||
- Fixed "Can not delete data in table when table's data type is kind of "not nil""
|
|
||||||
([#6555](https://github.com/nim-lang/Nim/issues/6555))
|
|
||||||
- Fixed "tables.nim: Cannot prove that 'n' is initialized"
|
|
||||||
([#6121](https://github.com/nim-lang/Nim/issues/6121))
|
|
||||||
- Fixed "issues with 'not nil' applied to a closure proc"
|
|
||||||
([#6489](https://github.com/nim-lang/Nim/issues/6489))
|
|
||||||
- Fixed "`not nil` not working in some cases"
|
|
||||||
([#4686](https://github.com/nim-lang/Nim/issues/4686))
|
|
||||||
- Fixed "Cannot prove '@[v]' is not nil"
|
|
||||||
([#3993](https://github.com/nim-lang/Nim/issues/3993))
|
|
||||||
|
|
||||||
- Fixed "Feature: support TCP_NODELAY in net.sockets"
|
|
||||||
([#6795](https://github.com/nim-lang/Nim/issues/6795))
|
|
||||||
- Fixed "Code that makes the compiler throw an error message and then hangs"
|
|
||||||
([#6820](https://github.com/nim-lang/Nim/issues/6820))
|
|
||||||
- Fixed "Code that makes the compiler throw an error message and then hangs"
|
|
||||||
([#6820](https://github.com/nim-lang/Nim/issues/6820))
|
|
||||||
- Fixed "Inconsistent behavior with sequence and string slicing"
|
|
||||||
([#6223](https://github.com/nim-lang/Nim/issues/6223))
|
|
||||||
- Fixed "Wrong behavior of "split" (proc and iterator)"
|
|
||||||
([#4377](https://github.com/nim-lang/Nim/issues/4377))
|
|
||||||
- Fixed "[Documentation] Invalid module name: [foo, bar]"
|
|
||||||
([#6831](https://github.com/nim-lang/Nim/issues/6831))
|
|
||||||
- Fixed "The destructor is not called for temporary objects"
|
|
||||||
([#4214](https://github.com/nim-lang/Nim/issues/4214))
|
|
||||||
- Fixed "Destructors does not work with implicit items iterator in for loop"
|
|
||||||
([#985](https://github.com/nim-lang/Nim/issues/985))
|
|
||||||
- Fixed "Error in template when using the type of the parameter inside it"
|
|
||||||
([#6756](https://github.com/nim-lang/Nim/issues/6756))
|
|
||||||
- Fixed "should json.to() respect parent attributes?"
|
|
||||||
([#5856](https://github.com/nim-lang/Nim/issues/5856))
|
|
||||||
- Fixed "json 'to' macro can not marshalize into tuples"
|
|
||||||
([#6095](https://github.com/nim-lang/Nim/issues/6095))
|
|
||||||
- Fixed "json.to fails with seq[T]"
|
|
||||||
([#6604](https://github.com/nim-lang/Nim/issues/6604))
|
|
||||||
- Fixed "json.to() is not worth using compared to marshal.to[T]"
|
|
||||||
([#5848](https://github.com/nim-lang/Nim/issues/5848))
|
|
||||||
- Fixed "Memory not being released in time, running out of memory"
|
|
||||||
([#6031](https://github.com/nim-lang/Nim/issues/6031))
|
|
||||||
- Fixed "[Regression] Bad C codegen for generic code"
|
|
||||||
([#6889](https://github.com/nim-lang/Nim/issues/6889))
|
|
||||||
- Fixed "rollingFileLogger deletes file on every start."
|
|
||||||
([#6264](https://github.com/nim-lang/Nim/issues/6264))
|
|
||||||
- Fixed "Remove/deprecate securehash module."
|
|
||||||
([#6033](https://github.com/nim-lang/Nim/issues/6033))
|
|
||||||
- Fixed "[bug or not] object construction for seq[T] failed without space after colon"
|
|
||||||
([#5999](https://github.com/nim-lang/Nim/issues/5999))
|
|
||||||
- Fixed "issues with the random module"
|
|
||||||
([#4726](https://github.com/nim-lang/Nim/issues/4726))
|
|
||||||
- Fixed "Reassigning local var to seq of objects results in nil element in Object's seq field"
|
|
||||||
([#668](https://github.com/nim-lang/Nim/issues/668))
|
|
||||||
- Fixed "Compilation error with "newseq[string]""
|
|
||||||
([#6726](https://github.com/nim-lang/Nim/issues/6726))
|
|
||||||
- Fixed "await inside array/dict literal produces invalid code - Part 2"
|
|
||||||
([#6626](https://github.com/nim-lang/Nim/issues/6626))
|
|
||||||
- Fixed "terminal.eraseline() gives OverflowError on Windows"
|
|
||||||
([#6931](https://github.com/nim-lang/Nim/issues/6931))
|
|
||||||
- Fixed "[Regression] `sequtils.any` conflicts with `system.any`"
|
|
||||||
([#6932](https://github.com/nim-lang/Nim/issues/6932))
|
|
||||||
- Fixed "C++ codegen: `mitems` generates invalid code."
|
|
||||||
([#4910](https://github.com/nim-lang/Nim/issues/4910))
|
|
||||||
- Fixed "seq.mitems produces invalid cpp codegen"
|
|
||||||
([#6892](https://github.com/nim-lang/Nim/issues/6892))
|
|
||||||
- Fixed "Concepts regression"
|
|
||||||
([#6108](https://github.com/nim-lang/Nim/issues/6108))
|
|
||||||
- Fixed "Generic iterable concept with array crashes compiler"
|
|
||||||
([#6277](https://github.com/nim-lang/Nim/issues/6277))
|
|
||||||
- Fixed "C code generation "‘a’ is a pointer; did you mean to use ‘->’?""
|
|
||||||
([#6462](https://github.com/nim-lang/Nim/issues/6462))
|
|
||||||
- Fixed "`--NimblePath` fails if a `-` in path which is not followed by a number"
|
|
||||||
([#6949](https://github.com/nim-lang/Nim/issues/6949))
|
|
||||||
- Fixed ""not registered in the selector" in asyncfile.close() for something that clearly was registered"
|
|
||||||
([#6906](https://github.com/nim-lang/Nim/issues/6906))
|
|
||||||
- Fixed "strange frexp behavior"
|
|
||||||
([#6353](https://github.com/nim-lang/Nim/issues/6353))
|
|
||||||
|
|
||||||
- Fixed "noreturn branches of case statements shouldn't contribute to type"
|
|
||||||
([#6885](https://github.com/nim-lang/Nim/issues/6885))
|
|
||||||
- Fixed "Type inference for 'if' statements changed"
|
|
||||||
([#6980](https://github.com/nim-lang/Nim/issues/6980))
|
|
||||||
- Fixed "newest asyncdispatch recursion"
|
|
||||||
([#6100](https://github.com/nim-lang/Nim/issues/6100))
|
|
||||||
- Fixed "Ambiguous identifier between set type and proc"
|
|
||||||
([#6965](https://github.com/nim-lang/Nim/issues/6965))
|
|
||||||
|
|
||||||
- Fixed "Inconsistent behavior with sequence and string slicing"
|
|
||||||
([#6223](https://github.com/nim-lang/Nim/issues/6223))
|
|
||||||
|
|
||||||
- Fixed "Unsupported OpenSSL library imported dynamically"
|
|
||||||
([#5000](https://github.com/nim-lang/Nim/issues/5000))
|
|
||||||
- Fixed "`nim check` segfaults"
|
|
||||||
([#6972](https://github.com/nim-lang/Nim/issues/6972))
|
|
||||||
- Fixed "GC deadlock"
|
|
||||||
([#6988](https://github.com/nim-lang/Nim/issues/6988))
|
|
||||||
- Fixed "Create a seq without memory initialization"
|
|
||||||
([#6401](https://github.com/nim-lang/Nim/issues/6401))
|
|
||||||
- Fixed "Fix bug for getch on Windows while using the arrow keys"
|
|
||||||
([#6966](https://github.com/nim-lang/Nim/issues/6966))
|
|
||||||
- Fixed "runnableExamples doesn't work in templates"
|
|
||||||
([#7018](https://github.com/nim-lang/Nim/issues/7018))
|
|
||||||
- Fixed "runnableExamples doesn't work with untyped statement blocks"
|
|
||||||
([#7019](https://github.com/nim-lang/Nim/issues/7019))
|
|
||||||
|
|
||||||
- Fixed "Critical bug in parseBiggestFloat"
|
|
||||||
([#7060](https://github.com/nim-lang/Nim/issues/7060))
|
|
||||||
- Fixed "[RFC] strformat.% should be gone"
|
|
||||||
([#7078](https://github.com/nim-lang/Nim/issues/7078))
|
|
||||||
- Fixed "compiler crash on simple macro"
|
|
||||||
([#7093](https://github.com/nim-lang/Nim/issues/7093))
|
|
||||||
- Fixed "Make newlines sane again"
|
|
||||||
([#7089](https://github.com/nim-lang/Nim/issues/7089))
|
|
||||||
- Fixed "JS - Unicode enum string representation issue"
|
|
||||||
([#6741](https://github.com/nim-lang/Nim/issues/6741))
|
|
||||||
- Fixed "Strange behaviour of 0.17.3 (working ok in 0.17.2)"
|
|
||||||
([#6989](https://github.com/nim-lang/Nim/issues/6989))
|
|
||||||
- Fixed "Strange behaviour of 0.17.3 (working ok in 0.17.2)"
|
|
||||||
([#6989](https://github.com/nim-lang/Nim/issues/6989))
|
|
||||||
- Fixed "Compiler crash: try expression with infix as"
|
|
||||||
([#7116](https://github.com/nim-lang/Nim/issues/7116))
|
|
||||||
- Fixed "nimsuggest crash"
|
|
||||||
([#7140](https://github.com/nim-lang/Nim/issues/7140))
|
|
||||||
- Fixed "[RFC] Reintroduce readChar"
|
|
||||||
([#7072](https://github.com/nim-lang/Nim/issues/7072))
|
|
||||||
- Fixed "Copyright line needs updating"
|
|
||||||
([#7129](https://github.com/nim-lang/Nim/issues/7129))
|
|
||||||
- Fixed "-0.0 doesn't result in negative zero in VM"
|
|
||||||
([#7079](https://github.com/nim-lang/Nim/issues/7079))
|
|
||||||
- Fixed "Windows large filesize"
|
|
||||||
([#7121](https://github.com/nim-lang/Nim/issues/7121))
|
|
||||||
- Fixed "Securehash is not parsimonious with MD5 and other hash modules"
|
|
||||||
([#6961](https://github.com/nim-lang/Nim/issues/6961))
|
|
||||||
- Fixed "os.findExe() shouldn't look in current directory on posix, unless exe has a /"
|
|
||||||
([#6939](https://github.com/nim-lang/Nim/issues/6939))
|
|
||||||
- Fixed "`compiles(...)` with `fatal` pragma causes compiler to exit early"
|
|
||||||
([#7080](https://github.com/nim-lang/Nim/issues/7080))
|
|
||||||
- Fixed "NPE when compile macro that returns concrete value"
|
|
||||||
([#5450](https://github.com/nim-lang/Nim/issues/5450))
|
|
||||||
- Fixed "Using a variable of type `int | float` causes internal compiler error"
|
|
||||||
([#6946](https://github.com/nim-lang/Nim/issues/6946))
|
|
||||||
- Fixed "Unsigned integers could not be used as array indexes."
|
|
||||||
([#7153](https://github.com/nim-lang/Nim/issues/7153))
|
|
||||||
- Fixed "countdown with uint causes underflow"
|
|
||||||
([#4220](https://github.com/nim-lang/Nim/issues/4220))
|
|
||||||
- Fixed "Inconsistent method call syntax"
|
|
||||||
([#7200](https://github.com/nim-lang/Nim/issues/7200))
|
|
||||||
- Fixed "Impossible to create an empty const array"
|
|
||||||
([#6853](https://github.com/nim-lang/Nim/issues/6853))
|
|
||||||
- Fixed "Strange UINT handling"
|
|
||||||
([#3985](https://github.com/nim-lang/Nim/issues/3985))
|
|
||||||
- Fixed "Bad codegen when passing arg that is part of return value destination"
|
|
||||||
([#6960](https://github.com/nim-lang/Nim/issues/6960))
|
|
||||||
- Fixed "No info about gcsafety in error message when global var is accessed in async proc"
|
|
||||||
([#6186](https://github.com/nim-lang/Nim/issues/6186))
|
|
||||||
- Fixed "Resolving package vs. local import ambiguities"
|
|
||||||
([#2819](https://github.com/nim-lang/Nim/issues/2819))
|
|
||||||
- Fixed "Internal error with type() operator"
|
|
||||||
([#3711](https://github.com/nim-lang/Nim/issues/3711))
|
|
||||||
- Fixed "newAsyncSocket should raise an OS error plus other inconsistencies"
|
|
||||||
([#4995](https://github.com/nim-lang/Nim/issues/4995))
|
|
||||||
- Fixed "mapLiterals fails with negative values"
|
|
||||||
([#7215](https://github.com/nim-lang/Nim/issues/7215))
|
|
||||||
- Fixed "fmWrite doesn't truncate file with openAsync, unlike open()"
|
|
||||||
([#5531](https://github.com/nim-lang/Nim/issues/5531))
|
|
||||||
- Fixed "Move libsvm to an external nimble module"
|
|
||||||
([#5786](https://github.com/nim-lang/Nim/issues/5786))
|
|
||||||
- Fixed "Prevent acceptAddr gotcha with newSocket"
|
|
||||||
([#7227](https://github.com/nim-lang/Nim/issues/7227))
|
|
||||||
- Fixed "strtabs.getOrDefault is inconsistent with tables.getOrDefault"
|
|
||||||
([#4265](https://github.com/nim-lang/Nim/issues/4265))
|
|
||||||
|
|
||||||
- Fixed "Code falling through into exception handler when no exception thrown."
|
|
||||||
([#7232](https://github.com/nim-lang/Nim/issues/7232))
|
|
||||||
- Fixed "the new generic inference rules are broken"
|
|
||||||
([#7247](https://github.com/nim-lang/Nim/issues/7247))
|
|
||||||
- Fixed "Odd `..<` regression"
|
|
||||||
([#6992](https://github.com/nim-lang/Nim/issues/6992))
|
|
||||||
- Fixed "Different proc type inferred from default parameter"
|
|
||||||
([#4659](https://github.com/nim-lang/Nim/issues/4659))
|
|
||||||
- Fixed "Different proc type inferred from default parameter"
|
|
||||||
([#4659](https://github.com/nim-lang/Nim/issues/4659))
|
|
||||||
- Fixed "Testament sometimes ignores test failures"
|
|
||||||
([#7236](https://github.com/nim-lang/Nim/issues/7236))
|
|
||||||
- Fixed "New Allocator Fails On >=4GB Requests"
|
|
||||||
([#7120](https://github.com/nim-lang/Nim/issues/7120))
|
|
||||||
- Fixed "User pragmas hide effect specifications from sempass2"
|
|
||||||
([#7216](https://github.com/nim-lang/Nim/issues/7216))
|
|
||||||
- Fixed "C++: SIGABRT instead of IndexError for out-of-bounds"
|
|
||||||
([#6512](https://github.com/nim-lang/Nim/issues/6512))
|
|
||||||
- Fixed "An uncaught exception in cpp mode doesn't show the exception name/msg"
|
|
||||||
([#6431](https://github.com/nim-lang/Nim/issues/6431))
|
|
||||||
|
|
@ -1,253 +0,0 @@
|
||||||
## v0.19.X - XX/XX/2018
|
|
||||||
|
|
||||||
### Changes affecting backwards compatibility
|
|
||||||
|
|
||||||
- The stdlib module ``future`` has been renamed to ``sugar``.
|
|
||||||
- ``macros.callsite`` is now deprecated. Since the introduction of ``varargs``
|
|
||||||
parameters this became unnecessary.
|
|
||||||
- Anonymous tuples with a single element can now be written as ``(1,)`` with a
|
|
||||||
trailing comma. The underlying AST is ``nnkTupleConstr(newLit 1)`` for this
|
|
||||||
example. ``nnkTupleConstr`` is a new node kind your macros need to be able
|
|
||||||
to deal with!
|
|
||||||
- Indexing into a ``cstring`` for the JS target is now mapped
|
|
||||||
to ``charCodeAt``.
|
|
||||||
- Assignments that would "slice" an object into its supertype are now prevented
|
|
||||||
at runtime. Use ``ref object`` with inheritance rather than ``object`` with
|
|
||||||
inheritance to prevent this issue.
|
|
||||||
- The ``not nil`` type annotation now has to be enabled explicitly
|
|
||||||
via ``{.experimental: "notnil".}`` as we are still not pleased with how this
|
|
||||||
feature works with Nim's containers.
|
|
||||||
- The parser now warns about inconsistent spacing around binary operators as
|
|
||||||
these can easily be confused with unary operators. This warning will likely
|
|
||||||
become an error in the future.
|
|
||||||
- The ``'c`` and ``'C'`` suffix for octal literals is now deprecated to
|
|
||||||
bring the language in line with the standard library (e.g. ``parseOct``).
|
|
||||||
- The dot style for import paths (e.g ``import path.to.module`` instead of
|
|
||||||
``import path/to/module``) has been deprecated.
|
|
||||||
|
|
||||||
#### Breaking changes in the standard library
|
|
||||||
|
|
||||||
- ``re.split`` for empty regular expressions now yields every character in
|
|
||||||
the string which is what other programming languages chose to do.
|
|
||||||
- The returned tuple of ``system.instantiationInfo`` now has a third field
|
|
||||||
containing the column of the instantiation.
|
|
||||||
|
|
||||||
- ``cookies.setCookie`` no longer assumes UTC for the expiration date.
|
|
||||||
- ``strutils.formatEng`` does not distinguish between ``nil`` and ``""``
|
|
||||||
strings anymore for its ``unit`` parameter. Instead the space is controlled
|
|
||||||
by a new parameter ``useUnitSpace``.
|
|
||||||
|
|
||||||
- The ``times.parse`` and ``times.format`` procs have been rewritten.
|
|
||||||
The proc signatures are the same so it should generally not break anything.
|
|
||||||
However, the new implementation is a bit stricter, which is a breaking change.
|
|
||||||
For example ``parse("2017-01-01 foo", "yyyy-MM-dd")`` will now raise an error.
|
|
||||||
|
|
||||||
- ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
|
|
||||||
to ``times.Duration`` in order to support higher time resolutions.
|
|
||||||
The proc is no longer deprecated.
|
|
||||||
|
|
||||||
- The ``times.Timezone`` is now an immutable ref-type that must be initialized
|
|
||||||
with an explicit constructor (``newTimezone``).
|
|
||||||
|
|
||||||
- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
|
|
||||||
|
|
||||||
- ``math.`mod` `` for floats now behaves the same as ``mod`` for integers
|
|
||||||
(previously it used floor division like Python). Use ``math.floorMod`` for the old behavior.
|
|
||||||
|
|
||||||
- For string inputs, ``unicode.isUpper`` and ``unicode.isLower`` now require a
|
|
||||||
second mandatory parameter ``skipNonAlpha``.
|
|
||||||
|
|
||||||
- For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now
|
|
||||||
require a second mandatory parameter ``skipNonAlpha``.
|
|
||||||
|
|
||||||
- ``osLastError`` is now marked with ``sideEffect``
|
|
||||||
- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings
|
|
||||||
and strings containing only valid prefixes, e.g. "0x" for hex integers.
|
|
||||||
|
|
||||||
- ``terminal.setCursorPos`` and ``terminal.setCursorXPos`` now work correctly
|
|
||||||
with 0-based coordinates on POSIX (previously, you needed to use
|
|
||||||
1-based coordinates on POSIX for correct behaviour; the Windows behaviour
|
|
||||||
was always correct).
|
|
||||||
|
|
||||||
- ``lineInfoObj`` now returns absolute path instead of project path.
|
|
||||||
It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
|
|
||||||
|
|
||||||
- ``net.sendTo`` no longer returns an int and now raises an ``OSError``.
|
|
||||||
- `threadpool`'s `await` and derivatives have been renamed to `blockUntil`
|
|
||||||
to avoid confusions with `await` from the `async` macro.
|
|
||||||
|
|
||||||
|
|
||||||
#### Breaking changes in the compiler
|
|
||||||
|
|
||||||
- The undocumented ``#? braces`` parsing mode was removed.
|
|
||||||
- The undocumented PHP backend was removed.
|
|
||||||
- The default location of ``nimcache`` for the native code targets was
|
|
||||||
changed. Read [the compiler user guide](https://nim-lang.org/docs/nimc.html#generated-c-code-directory)
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
### Library additions
|
|
||||||
|
|
||||||
- ``re.split`` now also supports the ``maxsplit`` parameter for consistency
|
|
||||||
with ``strutils.split``.
|
|
||||||
- Added ``system.toOpenArray`` in order to support zero-copy slicing
|
|
||||||
operations. This is currently not yet available for the JavaScript target.
|
|
||||||
- Added ``getCurrentDir``, ``findExe``, ``cpDir`` and ``mvDir`` procs to
|
|
||||||
``nimscript``.
|
|
||||||
- The ``times`` module now supports up to nanosecond time resolution when available.
|
|
||||||
- Added the type ``times.Duration`` for representing fixed durations of time.
|
|
||||||
- Added the proc ``times.convert`` for converting between different time units,
|
|
||||||
e.g days to seconds.
|
|
||||||
- Added the proc ``algorithm.binarySearch[T, K]`` with the ```cmp``` parameter.
|
|
||||||
- Added the proc ``algorithm.upperBound``.
|
|
||||||
- Added inverse hyperbolic functions, ``math.arcsinh``, ``math.arccosh`` and ``math.arctanh`` procs.
|
|
||||||
- Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs.
|
|
||||||
- Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division.
|
|
||||||
- Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
|
|
||||||
- Added the proc ``math.prod`` for product of elements in openArray.
|
|
||||||
- Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value.
|
|
||||||
- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt``.
|
|
||||||
- Added the proc ``flush`` for memory mapped files.
|
|
||||||
- Added the ``MemMapFileStream``.
|
|
||||||
- Added a simple interpreting event parser template ``eventParser`` to the ``pegs`` module.
|
|
||||||
- Added ``macros.copyLineInfo`` to copy lineInfo from other node.
|
|
||||||
- Added ``system.ashr`` an arithmetic right shift for integers.
|
|
||||||
|
|
||||||
### Library changes
|
|
||||||
|
|
||||||
- ``macros.astGenRepr``, ``macros.lispRepr`` and ``macros.treeRepr``
|
|
||||||
now escapes the content of string literals consistently.
|
|
||||||
- ``macros.NimSym`` and ``macros.NimIdent`` is now deprecated in favor
|
|
||||||
of the more general ``NimNode``.
|
|
||||||
- ``macros.getImpl`` now includes the pragmas of types, instead of omitting them.
|
|
||||||
- ``macros.hasCustomPragma`` and ``macros.getCustomPragmaVal`` now
|
|
||||||
also support ``ref`` and ``ptr`` types, pragmas on types and variant
|
|
||||||
fields.
|
|
||||||
- ``system.SomeReal`` is now called ``SomeFloat`` for consistency and
|
|
||||||
correctness.
|
|
||||||
- ``algorithm.smartBinarySearch`` and ``algorithm.binarySearch`` is
|
|
||||||
now joined in ``binarySearch``. ``smartbinarySearch`` is now
|
|
||||||
deprecated.
|
|
||||||
- The `terminal` module now exports additional procs for generating ANSI color
|
|
||||||
codes as strings.
|
|
||||||
- Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc.
|
|
||||||
- An exception raised from a ``test`` block of ``unittest`` now shows its type in
|
|
||||||
error message.
|
|
||||||
- The ``compiler/nimeval`` API was rewritten to simplify the "compiler as an
|
|
||||||
API". Using the Nim compiler and its VM as a scripting engine has never been
|
|
||||||
easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to
|
|
||||||
use the Nim VM in a native Nim application.
|
|
||||||
- Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc.
|
|
||||||
- The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated.
|
|
||||||
- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal``
|
|
||||||
object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs``
|
|
||||||
iterators.
|
|
||||||
- The ``accept`` socket procedure defined in the ``net`` module can now accept
|
|
||||||
a nil socket.
|
|
||||||
|
|
||||||
### Language additions
|
|
||||||
|
|
||||||
- Dot calls combined with explicit generic instantiations can now be written
|
|
||||||
as ``x.y[:z]`` which is transformed into ``y[z](x)`` by the parser.
|
|
||||||
- ``func`` is now an alias for ``proc {.noSideEffect.}``.
|
|
||||||
- In order to make ``for`` loops and iterators more flexible to use Nim now
|
|
||||||
supports so called "for-loop macros". See
|
|
||||||
the [manual](manual.html#macros-for-loop-macros) for more details.
|
|
||||||
This feature enables a Python-like generic ``enumerate`` implementation.
|
|
||||||
|
|
||||||
- Case statements can now be rewritten via macros. See the [manual](manual.html#macros-case-statement-macros) for more information.
|
|
||||||
This feature enables custom pattern matchers.
|
|
||||||
|
|
||||||
|
|
||||||
- the `typedesc` special type has been renamed to just `type`.
|
|
||||||
- `static` and `type` are now also modifiers similar to `ref` and `ptr`.
|
|
||||||
They denote the special types `static[T]` and `type[T]`.
|
|
||||||
- Forcing compile-time evaluation with `static` now supports specifying
|
|
||||||
the desired target type (as a concrete type or as a type class)
|
|
||||||
- The `type` operator now supports checking that the supplied expression
|
|
||||||
matches an expected type constraint.
|
|
||||||
|
|
||||||
### Language changes
|
|
||||||
|
|
||||||
- The `importcpp` pragma now allows importing the listed fields of generic
|
|
||||||
C++ types. Support for numeric parameters have also been added through
|
|
||||||
the use of `static[T]` types.
|
|
||||||
(#6415)
|
|
||||||
|
|
||||||
- Native C++ exceptions can now be imported with `importcpp` pragma.
|
|
||||||
Imported exceptions can be raised and caught just like Nim exceptions.
|
|
||||||
More details in language manual.
|
|
||||||
|
|
||||||
- ``nil`` for strings/seqs is finally gone. Instead the default value for
|
|
||||||
these is ``"" / @[]``. Use ``--nilseqs:on`` for a transition period.
|
|
||||||
|
|
||||||
- Accessing the binary zero terminator in Nim's native strings
|
|
||||||
is now invalid. Internally a Nim string still has the trailing zero for
|
|
||||||
zero-copy interoperability with ``cstring``. Compile your code with the
|
|
||||||
new switch ``--laxStrings:on`` if you need a transition period.
|
|
||||||
|
|
||||||
- The command syntax now supports keyword arguments after the first comma.
|
|
||||||
|
|
||||||
- Thread-local variables can now be declared inside procs. This implies all
|
|
||||||
the effects of the ``global`` pragma.
|
|
||||||
|
|
||||||
- Nim now supports the ``except`` clause in the export statement.
|
|
||||||
|
|
||||||
- Range float types, example ``range[0.0 .. Inf]``. More details in language manual.
|
|
||||||
- The ``{.this.}`` pragma has been deprecated. It never worked within generics and
|
|
||||||
we found the resulting code harder to read than the more explicit ``obj.field``
|
|
||||||
syntax.
|
|
||||||
- "Memory regions" for pointer types have been deprecated, they were hardly used
|
|
||||||
anywhere. Note that this has **nothing** to do with the ``--gc:regions`` switch
|
|
||||||
of managing memory.
|
|
||||||
|
|
||||||
- The exception hierarchy was slightly reworked, ``SystemError`` was renamed to
|
|
||||||
``CatchableError`` and is the new base class for any exception that is guaranteed to
|
|
||||||
be catchable. This change should have minimal impact on most existing Nim code.
|
|
||||||
|
|
||||||
|
|
||||||
### Tool changes
|
|
||||||
|
|
||||||
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
|
|
||||||
``doc``. The old ``jsondoc`` can still be invoked with ``jsondoc0``.
|
|
||||||
|
|
||||||
### Compiler changes
|
|
||||||
|
|
||||||
- The VM's instruction count limit was raised to 3 million instructions in
|
|
||||||
order to support more complex computations at compile-time.
|
|
||||||
|
|
||||||
- Support for hot code reloading has been implemented for the JavaScript
|
|
||||||
target. To use it, compile your code with `--hotCodeReloading:on` and use a
|
|
||||||
helper library such as LiveReload or BrowserSync.
|
|
||||||
|
|
||||||
- A new compiler option `--cppCompileToNamespace` puts the generated C++ code
|
|
||||||
into the namespace "Nim" in order to avoid naming conflicts with existing
|
|
||||||
C++ code. This is done for all Nim code - internal and exported.
|
|
||||||
|
|
||||||
- Added ``macros.getProjectPath`` and ``os.putEnv`` procs to Nim's virtual
|
|
||||||
machine.
|
|
||||||
|
|
||||||
- The ``deadCodeElim`` option is now always turned on and the switch has no
|
|
||||||
effect anymore, but is recognized for backwards compatibility.
|
|
||||||
|
|
||||||
- ``experimental`` is now a pragma / command line switch that can enable specific
|
|
||||||
language extensions, it is not an all-or-nothing switch anymore.
|
|
||||||
|
|
||||||
- Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html)
|
|
||||||
for more info.
|
|
||||||
|
|
||||||
- macros.bindSym now capable to accepts not only literal string or string constant expression.
|
|
||||||
bindSym enhancement make it also can accepts computed string or ident node inside macros /
|
|
||||||
compile time functions / static blocks. Only in templates / regular code it retains it's old behavior.
|
|
||||||
This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch.
|
|
||||||
|
|
||||||
- On Posix systems the global system wide configuration is now put under ``/etc/nim/nim.cfg``,
|
|
||||||
it used to be ``/etc/nim.cfg``. Usually it does not exist, however.
|
|
||||||
|
|
||||||
- On Posix systems the user configuration is now looked under ``$XDG_CONFIG_HOME/nim/nim.cfg``
|
|
||||||
(if ``XDG_CONFIG_HOME`` is not defined, then under ``~/.config/nim/nim.cfg``). It used to be
|
|
||||||
``$XDG_CONFIG_DIR/nim.cfg`` (and ``~/.config/nim.cfg``).
|
|
||||||
|
|
||||||
Similarly, on Windows, the user configuration is now looked under ``%APPDATA%/nim/nim.cfg``.
|
|
||||||
This used to be ``%APPDATA%/nim.cfg``.
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
@ -1,294 +0,0 @@
|
||||||
## v0.20.0 - 2019-06-06
|
|
||||||
|
|
||||||
|
|
||||||
### Changes affecting backwards compatibility
|
|
||||||
|
|
||||||
- `shr` is now sign preserving. Use `-d:nimOldShiftRight` to enable
|
|
||||||
the old behavior globally.
|
|
||||||
|
|
||||||
- The ``isLower``, ``isUpper`` family of procs in strutils/unicode
|
|
||||||
operating on **strings** have been
|
|
||||||
deprecated since it was unclear what these do. Note that the much more
|
|
||||||
useful procs that operate on ``char`` or ``Rune`` are not affected.
|
|
||||||
|
|
||||||
- `strutils.editDistance` has been deprecated,
|
|
||||||
use `editdistance.editDistance` or `editdistance.editDistanceAscii`
|
|
||||||
instead.
|
|
||||||
|
|
||||||
- The OpenMP parallel iterator \``||`\` now supports any `#pragma omp directive`
|
|
||||||
and not just `#pragma omp parallel for`. See
|
|
||||||
[OpenMP documentation](https://www.openmp.org/wp-content/uploads/OpenMP-4.5-1115-CPP-web.pdf).
|
|
||||||
|
|
||||||
The default annotation is `parallel for`, if you used OpenMP without annotation
|
|
||||||
the change is transparent, if you used annotations you will have to prefix
|
|
||||||
your previous annotations with `parallel for`.
|
|
||||||
|
|
||||||
Furthermore, an overload with positive stepping is available.
|
|
||||||
|
|
||||||
- The `unchecked` pragma was removed, instead use `system.UncheckedArray`.
|
|
||||||
|
|
||||||
- The undocumented ``#? strongSpaces`` parsing mode has been removed.
|
|
||||||
|
|
||||||
- The `not` operator is now always a unary operator, this means that code like
|
|
||||||
``assert not isFalse(3)`` compiles.
|
|
||||||
|
|
||||||
- `getImpl` on a `var` or `let` symbol will now return the full `IdentDefs`
|
|
||||||
tree from the symbol declaration instead of just the initializer portion.
|
|
||||||
|
|
||||||
- Methods are now ordinary "single" methods, only the first parameter is
|
|
||||||
used to select the variant at runtime. For backwards compatibility
|
|
||||||
use the new `--multimethods:on` switch.
|
|
||||||
|
|
||||||
- Generic methods are now deprecated; they never worked well.
|
|
||||||
|
|
||||||
- Compile time checks for integer and float conversions are now stricter.
|
|
||||||
For example, `const x = uint32(-1)` now gives a compile time error instead
|
|
||||||
of being equivalent to `const x = 0xFFFFFFFF'u32`.
|
|
||||||
|
|
||||||
- Using `typed` as the result type in templates/macros now means
|
|
||||||
"expression with a type". The old meaning of `typed` is preserved
|
|
||||||
as `void` or no result type at all.
|
|
||||||
|
|
||||||
- A bug allowed `macro foo(): int = 123` to compile even though a
|
|
||||||
macro has to return a `NimNode`. This has been fixed.
|
|
||||||
|
|
||||||
- With the exception of `uint` and `uint64`, conversion to unsigned types
|
|
||||||
are now range checked during runtime.
|
|
||||||
|
|
||||||
- Macro arguments of type `typedesc` are now passed to the macro as
|
|
||||||
`NimNode` like every other type except `static`. Use `typed` for a
|
|
||||||
behavior that is identical in new and old
|
|
||||||
Nim. See the RFC [Pass typedesc as NimNode to macros](https://github.com/nim-lang/RFCs/issues/148)
|
|
||||||
for more details.
|
|
||||||
|
|
||||||
|
|
||||||
#### Breaking changes in the standard library
|
|
||||||
|
|
||||||
- `osproc.execProcess` now also takes a `workingDir` parameter.
|
|
||||||
|
|
||||||
- `std/sha1.secureHash` now accepts `openArray[char]`, not `string`. (Former
|
|
||||||
successful matches should keep working, though former failures will not.)
|
|
||||||
|
|
||||||
- `options.UnpackError` is no longer a ref type and inherits from `system.Defect`
|
|
||||||
instead of `system.ValueError`.
|
|
||||||
|
|
||||||
- `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`.
|
|
||||||
|
|
||||||
- The procs `parseutils.parseBiggestInt`, `parseutils.parseInt`,
|
|
||||||
`parseutils.parseBiggestUInt` and `parseutils.parseUInt` now raise a
|
|
||||||
`ValueError` when the parsed integer is outside of the valid range.
|
|
||||||
Previously they sometimes raised an `OverflowError` and sometimes they
|
|
||||||
returned `0`.
|
|
||||||
|
|
||||||
- The procs `parseutils.parseBin`, `parseutils.parseOct` and `parseutils.parseHex`
|
|
||||||
were not clearing their `var` parameter `number` and used to push its value to
|
|
||||||
the left when storing the parsed string into it. Now they always set the value
|
|
||||||
of the parameter to `0` before storing the result of the parsing, unless the
|
|
||||||
string to parse is not valid (then the value of `number` is not changed).
|
|
||||||
|
|
||||||
- `streams.StreamObject` now restricts its fields to only raise `system.Defect`,
|
|
||||||
`system.IOError` and `system.OSError`.
|
|
||||||
This change only affects custom stream implementations.
|
|
||||||
|
|
||||||
- nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or
|
|
||||||
`nil`/`""`, respectively. Use the newly added `n in p.captures` method to
|
|
||||||
check if a group is captured, otherwise you'll receive an exception.
|
|
||||||
|
|
||||||
- nre's `RegexMatch.{captureBounds,captures}.toTable` no longer accept a
|
|
||||||
default parameter. Instead uncaptured entries are left empty. Use
|
|
||||||
`Table.getOrDefault()` if you need defaults.
|
|
||||||
|
|
||||||
- nre's `RegexMatch.captures.{items,toSeq}` now returns an `Option[string]`
|
|
||||||
instead of a `string`. With the removal of `nil` strings, this is the only
|
|
||||||
way to indicate a missing match. Inside your loops, instead
|
|
||||||
of `capture == ""` or `capture == nil`, use `capture.isSome` to check if a capture is
|
|
||||||
present, and `capture.get` to get its value.
|
|
||||||
|
|
||||||
- nre's `replace()` no longer throws `ValueError` when the replacement string
|
|
||||||
has missing captures. It instead throws `KeyError` for named captures, and
|
|
||||||
`IndexError` for unnamed captures. This is consistent with
|
|
||||||
`RegexMatch.{captureBounds,captures}[]`.
|
|
||||||
|
|
||||||
- `splitFile` now correctly handles edge cases, see #10047.
|
|
||||||
|
|
||||||
- `isNil` is no longer false for undefined in the JavaScript backend:
|
|
||||||
now it's true for both nil and undefined.
|
|
||||||
Use `isNull` or `isUndefined` if you need exact equality:
|
|
||||||
`isNil` is consistent with `===`, `isNull` and `isUndefined` with `==`.
|
|
||||||
|
|
||||||
- several deprecated modules were removed: `ssl`, `matchers`, `httpserver`,
|
|
||||||
`unsigned`, `actors`, `parseurl`
|
|
||||||
|
|
||||||
- two poorly documented and not used modules (`subexes`, `scgi`) were moved to
|
|
||||||
graveyard (they are available as Nimble packages)
|
|
||||||
|
|
||||||
- procs `string.add(int)` and `string.add(float)` which implicitly convert
|
|
||||||
ints and floats to string have been deprecated.
|
|
||||||
Use `string.addInt(int)` and `string.addFloat(float)` instead.
|
|
||||||
|
|
||||||
- ``case object`` branch transitions via ``system.reset`` are deprecated.
|
|
||||||
Compile your code with ``-d:nimOldCaseObjects`` for a transition period.
|
|
||||||
|
|
||||||
- base64 module: The default parameter `newLine` for the `encode` procs
|
|
||||||
was changed from `"\13\10"` to the empty string `""`.
|
|
||||||
|
|
||||||
|
|
||||||
#### Breaking changes in the compiler
|
|
||||||
|
|
||||||
- The compiler now implements the "generic symbol prepass" for `when` statements
|
|
||||||
in generics, see bug #8603. This means that code like this does not compile
|
|
||||||
anymore:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
proc enumToString*(enums: openArray[enum]): string =
|
|
||||||
# typo: 'e' instead 'enums'
|
|
||||||
when e.low.ord >= 0 and e.high.ord < 256:
|
|
||||||
result = newString(enums.len)
|
|
||||||
else:
|
|
||||||
result = newString(enums.len * 2)
|
|
||||||
```
|
|
||||||
|
|
||||||
- ``discard x`` is now illegal when `x` is a function symbol.
|
|
||||||
|
|
||||||
- Implicit imports via ``--import: module`` in a config file are now restricted
|
|
||||||
to the main package.
|
|
||||||
|
|
||||||
|
|
||||||
### Library additions
|
|
||||||
|
|
||||||
- There is a new stdlib module `std/editdistance` as a replacement for the
|
|
||||||
deprecated `strutils.editDistance`.
|
|
||||||
|
|
||||||
- There is a new stdlib module `std/wordwrap` as a replacement for the
|
|
||||||
deprecated `strutils.wordwrap`.
|
|
||||||
|
|
||||||
- Added `split`, `splitWhitespace`, `size`, `alignLeft`, `align`,
|
|
||||||
`strip`, `repeat` procs and iterators to `unicode.nim`.
|
|
||||||
|
|
||||||
- Added `or` for `NimNode` in `macros`.
|
|
||||||
|
|
||||||
- Added `system.typeof` for more control over how `type` expressions
|
|
||||||
can be deduced.
|
|
||||||
|
|
||||||
- Added `macros.isInstantiationOf` for checking if the proc symbol
|
|
||||||
is instantiation of generic proc symbol.
|
|
||||||
|
|
||||||
- Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc.
|
|
||||||
|
|
||||||
- There is a new stdlib module `std/diff` to compute the famous "diff"
|
|
||||||
of two texts by line.
|
|
||||||
|
|
||||||
- Added `os.relativePath`.
|
|
||||||
|
|
||||||
- Added `parseopt.remainingArgs`.
|
|
||||||
|
|
||||||
- Added `os.getCurrentCompilerExe` (implemented as `getAppFilename` at CT),
|
|
||||||
can be used to retrieve the currently executing compiler.
|
|
||||||
|
|
||||||
- Added `xmltree.toXmlAttributes`.
|
|
||||||
|
|
||||||
- Added ``std/sums`` module for fast summation functions.
|
|
||||||
|
|
||||||
- Added `Rusage`, `getrusage`, `wait4` to the posix interface.
|
|
||||||
|
|
||||||
- Added the `posix_utils` module.
|
|
||||||
|
|
||||||
- Added `system.default`.
|
|
||||||
|
|
||||||
- Added `sequtils.items` for closure iterators, allows closure iterators
|
|
||||||
to be used by the `mapIt`, `filterIt`, `allIt`, `anyIt`, etc.
|
|
||||||
|
|
||||||
|
|
||||||
### Library changes
|
|
||||||
|
|
||||||
- The string output of `macros.lispRepr` proc has been tweaked
|
|
||||||
slightly. The `dumpLisp` macro in this module now outputs an
|
|
||||||
indented proper Lisp, devoid of commas.
|
|
||||||
|
|
||||||
- Added `macros.signatureHash` that returns a stable identifier
|
|
||||||
derived from the signature of a symbol.
|
|
||||||
|
|
||||||
- In `strutils` empty strings now no longer matched as substrings
|
|
||||||
anymore.
|
|
||||||
|
|
||||||
- The `Complex` type is now a generic object and not a tuple anymore.
|
|
||||||
|
|
||||||
- The `ospaths` module is now deprecated, use `os` instead. Note that
|
|
||||||
`os` is available in a NimScript environment but unsupported
|
|
||||||
operations produce a compile-time error.
|
|
||||||
|
|
||||||
- The `parseopt` module now supports a new flag `allowWhitespaceAfterColon`
|
|
||||||
(default value: true) that can be set to `false` for better Posix
|
|
||||||
interoperability. (Bug #9619.)
|
|
||||||
|
|
||||||
- `os.joinPath` and `os.normalizePath` handle edge cases like ``"a/b/../../.."``
|
|
||||||
differently.
|
|
||||||
|
|
||||||
- `securehash` was moved to `lib/deprecated`.
|
|
||||||
|
|
||||||
- The switch ``-d:useWinAnsi`` is not supported anymore.
|
|
||||||
|
|
||||||
- In `times` module, procs `format` and `parse` accept a new optional
|
|
||||||
`DateTimeLocale` argument for formatting/parsing dates in other languages.
|
|
||||||
|
|
||||||
|
|
||||||
### Language additions
|
|
||||||
|
|
||||||
- Vm support for float32<->int32 and float64<->int64 casts was added.
|
|
||||||
- There is a new pragma block `noSideEffect` that works like
|
|
||||||
the `gcsafe` pragma block.
|
|
||||||
- added `os.getCurrentProcessId`.
|
|
||||||
- User defined pragmas are now allowed in the pragma blocks.
|
|
||||||
- Pragma blocks are no longer eliminated from the typed AST tree to preserve
|
|
||||||
pragmas for further analysis by macros.
|
|
||||||
- Custom pragmas are now supported for `var` and `let` symbols.
|
|
||||||
- Tuple unpacking is now supported for constants and for loop variables.
|
|
||||||
- Case object branches can be initialized with a runtime discriminator if
|
|
||||||
possible discriminator values are constrained within a case statement.
|
|
||||||
|
|
||||||
### Language changes
|
|
||||||
|
|
||||||
- The standard extension for SCF (source code filters) files was changed from
|
|
||||||
``.tmpl`` to ``.nimf``,
|
|
||||||
it's more recognizable and allows tools like Github to recognize it as Nim,
|
|
||||||
see [#9647](https://github.com/nim-lang/Nim/issues/9647).
|
|
||||||
The previous extension will continue to work.
|
|
||||||
|
|
||||||
- Pragma syntax is now consistent. Previous syntax where type pragmas did not
|
|
||||||
follow the type name is now deprecated. Also pragma before generic parameter
|
|
||||||
list is deprecated to be consistent with how pragmas are used with a proc. See
|
|
||||||
[#8514](https://github.com/nim-lang/Nim/issues/8514) and
|
|
||||||
[#1872](https://github.com/nim-lang/Nim/issues/1872) for further details.
|
|
||||||
|
|
||||||
- Hash sets and tables are initialized by default. The explicit `initHashSet`,
|
|
||||||
`initTable`, etc. are not needed anymore.
|
|
||||||
|
|
||||||
|
|
||||||
### Tool changes
|
|
||||||
|
|
||||||
- `jsondoc` now includes a `moduleDescription` field with the module
|
|
||||||
description. `jsondoc0` shows comments as its own objects as shown in the
|
|
||||||
documentation.
|
|
||||||
- `nimpretty`: --backup now defaults to `off` instead of `on` and the flag was
|
|
||||||
undocumented; use `git` instead of relying on backup files.
|
|
||||||
- `koch` now defaults to build the latest *stable* Nimble version unless you
|
|
||||||
explicitly ask for the latest master version via `--latest`.
|
|
||||||
|
|
||||||
|
|
||||||
### Compiler changes
|
|
||||||
|
|
||||||
- The deprecated `fmod` proc is now unavailable on the VM.
|
|
||||||
- A new `--outdir` option was added.
|
|
||||||
- The compiled JavaScript file for the project produced by executing `nim js`
|
|
||||||
will no longer be placed in the nimcache directory.
|
|
||||||
- The `--hotCodeReloading` has been implemented for the native targets.
|
|
||||||
The compiler also provides a new more flexible API for handling the
|
|
||||||
hot code reloading events in the code.
|
|
||||||
- The compiler now supports a ``--expandMacro:macroNameHere`` switch
|
|
||||||
for easy introspection into what a macro expands into.
|
|
||||||
- The `-d:release` switch now does not disable runtime checks anymore.
|
|
||||||
For a release build that also disables runtime checks
|
|
||||||
use `-d:release -d:danger` or simply `-d:danger`.
|
|
||||||
|
|
||||||
|
|
||||||
### Bugfixes
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
# v0.20.2 - 2019-07-17
|
|
||||||
|
|
||||||
|
|
||||||
## Changes affecting backwards compatibility
|
|
||||||
|
|
||||||
- All `strutils.rfind` procs now take `start` and `last` like `strutils.find`
|
|
||||||
with the same data slice/index meaning. This is backwards compatible for
|
|
||||||
calls *not* changing the `rfind` `start` parameter from its default. (#11487)
|
|
||||||
|
|
||||||
In the unlikely case that you were using `rfind X, start=N`, or `rfind X, N`,
|
|
||||||
then you need to change that to `rfind X, last=N` or `rfind X, 0, N`. (This
|
|
||||||
should minimize gotchas porting code from other languages like Python or C++.)
|
|
||||||
|
|
||||||
- On Windows stderr/stdout/stdin are not opened as binary files anymore. Use the switch
|
|
||||||
`-d:nimBinaryStdFiles` for a transition period.
|
|
||||||
|
|
||||||
### Breaking changes in the standard library
|
|
||||||
|
|
||||||
- Mac OS X / BSD: TSa_Family is now the ``uint8`` type, so type
|
|
||||||
conversions like ``x.sin_family = uint16 toInt(nativesockets.AF_INET)``
|
|
||||||
need to be changed into ``x.sin_family = TSa_Family toInt(nativesockets.AF_INET)``.
|
|
||||||
|
|
||||||
|
|
||||||
### Breaking changes in the compiler
|
|
||||||
|
|
||||||
|
|
||||||
## Library additions
|
|
||||||
|
|
||||||
- `toOpenArray` is now available for the JS target.
|
|
||||||
|
|
||||||
## Library changes
|
|
||||||
|
|
||||||
- Fix async IO operations stalling even after socket is closed. (#11232)
|
|
||||||
|
|
||||||
- More informative error message for `streams.openFileStream`. (#11438)
|
|
||||||
|
|
||||||
|
|
||||||
## Language additions
|
|
||||||
|
|
||||||
|
|
||||||
## Language changes
|
|
||||||
|
|
||||||
|
|
||||||
### Tool changes
|
|
||||||
|
|
||||||
|
|
||||||
### Compiler changes
|
|
||||||
|
|
||||||
- Better error message for IndexError for empty containers. (#11476)
|
|
||||||
|
|
||||||
- Fix regression in semfold for old right shift. (#11477)
|
|
||||||
|
|
||||||
- Fix for passing tuples as static params to macros. (#11423)
|
|
||||||
|
|
||||||
## Bugfixes
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
# v1.0 - 2019-09-23
|
|
||||||
|
|
||||||
|
|
||||||
## Changes affecting backwards compatibility
|
|
||||||
|
|
||||||
- The switch ``-d:nimBinaryStdFiles`` does not exist anymore. Instead
|
|
||||||
stdin/stdout/stderr are binary files again. This change only affects
|
|
||||||
Windows.
|
|
||||||
|
|
||||||
- On Windows console applications the code-page is set at program startup
|
|
||||||
to UTF-8. Use the new switch `-d:nimDontSetUtf8CodePage` to disable this
|
|
||||||
feature.
|
|
||||||
|
|
||||||
- The language definition and compiler are now stricter about ``gensym``'ed
|
|
||||||
symbols in hygienic templates. See the section in the
|
|
||||||
[manual](https://nim-lang.org/docs/manual.html#templates-hygiene-in-templates)
|
|
||||||
for further details. Use the compiler switch `--oldgensym:on` for a
|
|
||||||
transition period.
|
|
||||||
|
|
||||||
|
|
||||||
### Breaking changes in the standard library
|
|
||||||
|
|
||||||
- We removed `unicode.Rune16` without any deprecation period as the name
|
|
||||||
was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details)
|
|
||||||
and we didn't find any usages of it in the wild. If you still need it, add this
|
|
||||||
piece of code to your project:
|
|
||||||
```nim
|
|
||||||
type
|
|
||||||
Rune16* = distinct int16
|
|
||||||
```
|
|
||||||
|
|
||||||
- `exportc` now uses C instead of C++ mangling with `nim cpp`, matching behavior
|
|
||||||
of `importc`, see #10578.
|
|
||||||
Use the new `exportcpp` to mangle as C++ when using `nim cpp`.
|
|
||||||
|
|
||||||
|
|
||||||
### Breaking changes in the compiler
|
|
||||||
|
|
||||||
- A bug allowing `int` to be implicitly converted to range types of smaller size
|
|
||||||
(e.g `range[0'i8..10'i8]`) has been fixed.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Library additions
|
|
||||||
|
|
||||||
- `encodings.getCurrentEncoding` now distinguishes between the console's
|
|
||||||
encoding and the OS's encoding. This distinction is only meaningful on
|
|
||||||
Windows.
|
|
||||||
- Added `system.getOsFileHandle` which is usually more useful
|
|
||||||
than `system.getFileHandle`. This distinction is only meaningful on
|
|
||||||
Windows.
|
|
||||||
- Added a `json.parseJsonFragments` iterator that can be used to speedup
|
|
||||||
JSON processing substantially when there are JSON fragments separated
|
|
||||||
by whitespace.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Library changes
|
|
||||||
|
|
||||||
- Added `os.delEnv` and `nimscript.delEnv`. (#11466)
|
|
||||||
|
|
||||||
- Enabled Oid usage in hashtables. (#11472)
|
|
||||||
|
|
||||||
- Added `unsafeColumnAt` procs, that return unsafe cstring from InstantRow. (#11647)
|
|
||||||
|
|
||||||
- Make public `Sha1Digest` and `Sha1State` types and `newSha1State`,
|
|
||||||
`update` and `finalize` procedures from `sha1` module. (#11694)
|
|
||||||
|
|
||||||
- Added the `std/monotimes` module which implements monotonic timestamps.
|
|
||||||
|
|
||||||
- Consistent error handling of two `exec` overloads. (#10967)
|
|
||||||
|
|
||||||
- Officially the following modules now have an unstable API:
|
|
||||||
- std/varints
|
|
||||||
- core/allocators
|
|
||||||
- core/hotcodereloading
|
|
||||||
- asyncstreams
|
|
||||||
- base64
|
|
||||||
- browsers
|
|
||||||
- collections/rtarrays
|
|
||||||
- collections/sharedlist
|
|
||||||
- collections/sharedtable
|
|
||||||
- concurrency/atomics
|
|
||||||
- concurrency/cpuload
|
|
||||||
- concurrency/threadpool
|
|
||||||
- coro
|
|
||||||
- endians
|
|
||||||
- httpcore
|
|
||||||
- parsesql
|
|
||||||
- pathnorm
|
|
||||||
- reservedmem
|
|
||||||
- typetraits
|
|
||||||
|
|
||||||
Every other stdlib module is API stable with respect to version 1.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Language additions
|
|
||||||
|
|
||||||
- Inline iterators returning `lent T` types are now supported, similarly to
|
|
||||||
iterators returning `var T`:
|
|
||||||
```nim
|
|
||||||
iterator myitems[T](x: openarray[T]): lent T
|
|
||||||
iterator mypairs[T](x: openarray[T]): tuple[idx: int, val: lent T]
|
|
||||||
```
|
|
||||||
|
|
||||||
- Added an `importjs` pragma that can now be used instead of `importcpp`
|
|
||||||
and `importc` to import symbols from JavaScript. `importjs` for routines always
|
|
||||||
takes a "pattern" for maximum flexibility.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Language changes
|
|
||||||
|
|
||||||
- `uint64` is now finally a regular ordinal type. This means `high(uint64)` compiles
|
|
||||||
and yields the correct value.
|
|
||||||
|
|
||||||
|
|
||||||
### Tool changes
|
|
||||||
|
|
||||||
- The Nim compiler now does not recompile the Nim project via ``nim c -r`` if
|
|
||||||
no dependent Nim file changed. This feature can be overridden by
|
|
||||||
the ``--forceBuild`` command line option.
|
|
||||||
- The Nim compiler now warns about unused module imports. You can use a
|
|
||||||
top level ``{.used.}`` pragma in the module that you want to be importable
|
|
||||||
without producing this warning.
|
|
||||||
- The "testament" testing tool's name was changed
|
|
||||||
from `tester` to `testament` and is generally available as a tool to run Nim
|
|
||||||
tests automatically.
|
|
||||||
|
|
||||||
|
|
||||||
### Compiler changes
|
|
||||||
|
|
||||||
- VM can now cast integer type arbitrarily. (#11459)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Bugfixes
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
# v1.0.2 - 2019-10-23
|
|
||||||
|
|
||||||
|
|
||||||
## Bugfixes
|
|
||||||
|
|
||||||
* fixes the --verbosity:2 regression
|
|
||||||
* Fixed "Fail to compile a file twice under Windows (v1.0 bug)." [#12242](https://github.com/nim-lang/Nim/issues/12242)
|
|
||||||
* fix nimpretty removing space before pragma
|
|
||||||
* JS: gensym is stricter for 'this'
|
|
||||||
* Fixed "VM Assertion Error with newruntime" [#12294](https://github.com/nim-lang/Nim/issues/12294)
|
|
||||||
* Fixed "Assertion error when running `nim check` on compiler/nim.nim" [#12281](https://github.com/nim-lang/Nim/issues/12281)
|
|
||||||
* Fixed "Compiler crash with empty array and generic instantiation with int as parameter" [#12264](https://github.com/nim-lang/Nim/issues/12264)
|
|
||||||
* Fixed "Regression in JS backend codegen "Error: request to generate code for .compileTime proc"" [#12240](https://github.com/nim-lang/Nim/issues/12240)
|
|
||||||
* Fix how `relativePath` handle case sensitiviy
|
|
||||||
* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336)
|
|
||||||
* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291)
|
|
||||||
* VM: no special casing for big endian machines
|
|
||||||
* Fixed "`internal error: environment misses` with a simple template inside one of Jester macros" [#12323](https://github.com/nim-lang/Nim/issues/12323)
|
|
||||||
* nimsuggest: fix tcp socket leak
|
|
||||||
* nimsuggest: fix tcp socket leak for epc backend
|
|
||||||
* Fixed "`writeFile` and `write(f, str)` skip null bytes on Windows" [#12315](https://github.com/nim-lang/Nim/issues/12315)
|
|
||||||
* Fixed "Crash in intsets symmetric_difference" [#12366](https://github.com/nim-lang/Nim/issues/12366)
|
|
||||||
* Fixed "[regression] VM crash when dealing with var param of a proc result" [#12244](https://github.com/nim-lang/Nim/issues/12244)
|
|
||||||
* fixes a koch regression that made 'koch boot --listcmd' not work anymore
|
|
||||||
* Fixed "[regression] inconsistent signed int `mod` operator between runtime, compiletime, and semfold" [#12332](https://github.com/nim-lang/Nim/issues/12332)
|
|
||||||
* Fixed "Boehm disables interior pointer checking" [#12286](https://github.com/nim-lang/Nim/issues/12286)
|
|
||||||
* Fixes semCustomPragma when nkSym
|
|
||||||
* Fixed yield in nkCheckedFieldExpr
|
|
||||||
* Fixed "`randomize()` from `random` not working on JS" [#12418](https://github.com/nim-lang/Nim/issues/12418)
|
|
||||||
* Fixed "Compiler crash with invalid object variant" [#12379](https://github.com/nim-lang/Nim/issues/12379)
|
|
||||||
* fix type's case in random.nim
|
|
||||||
* Fixed "Update docs with a better way to signal unimplemented methods" [#10804](https://github.com/nim-lang/Nim/issues/10804)
|
|
||||||
* Fixed "Nim language manual, push pragma is not explained well" [#10824](https://github.com/nim-lang/Nim/issues/10824)
|
|
||||||
* Fixed "[regression] Importing more than one module with same name from different packages produce bad codegen" [#12420](https://github.com/nim-lang/Nim/issues/12420)
|
|
||||||
* Namespace unittest enums to avoid name conflicts
|
|
||||||
* Fixed "VM checks unsigned integers for overflow." [#12310](https://github.com/nim-lang/Nim/issues/12310)
|
|
||||||
* Fixed "line directive is not generated for first line of function definition" [#12426](https://github.com/nim-lang/Nim/issues/12426)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Documentation improvements
|
|
||||||
|
|
||||||
* threadpool: fix link in docs (#12258)
|
|
||||||
* Fix spellings (#12277)
|
|
||||||
* fix #12278, don't expose internal PCRE documentation
|
|
||||||
* Fixed "Documentation of quitprocs is wrong" [#12279(https://github.com/nim-lang/Nim/issues/12279)
|
|
||||||
* Fix typo in docs
|
|
||||||
* Fix reference to parseSpec proc in readme
|
|
||||||
* [doc/tut1] removed discard discussion in comments
|
|
||||||
* Documentation improvements around the db interface
|
|
||||||
* Easier build instructions for windows - just run `build_all.bat`.
|
|
||||||
* fix a few dead links and a missing sentence in documentation
|
|
||||||
* Macro docs additions
|
|
||||||
* Updated the code example in the os module to use better grammar.
|
|
||||||
* Mention "lambdas" and `=>` in the manual
|
|
||||||
* Better documentation on Garbage Collector
|
|
||||||
|
|
@ -1,535 +0,0 @@
|
||||||
# v1.2.0 - 2020-04-02
|
|
||||||
|
|
||||||
|
|
||||||
## Standard library additions and changes
|
|
||||||
- Added overloaded `strformat.fmt` macro that use specified characters as
|
|
||||||
delimiter instead of '{' and '}'.
|
|
||||||
- Added new procs in `tables.nim`: `OrderedTable.pop`, `CountTable.del`,
|
|
||||||
`CountTable.pop`, `Table.pop`.
|
|
||||||
- Added `strtabs.clear` overload that reuses the existing mode.
|
|
||||||
- Added `browsers.osOpen` const alias for the operating system specific *"open"* command.
|
|
||||||
- Added `sugar.dup` for turning in-place algorithms like `sort` and `shuffle`
|
|
||||||
into operations that work on a copy of the data and return the mutated copy,
|
|
||||||
like the existing `sorted` does.
|
|
||||||
- Added `sugar.collect` that does comprehension for seq/set/table collections.
|
|
||||||
- Added `sugar.capture` for capturing some local loop variables when creating a
|
|
||||||
closure. This is an enhanced version of `closureScope`.
|
|
||||||
- Added `typetraits.tupleLen` to get number of elements of a tuple/type tuple,
|
|
||||||
and `typetraits.get` to get the ith element of a type tuple.
|
|
||||||
- Added `typetraits.genericParams` to return a tuple of generic params from a
|
|
||||||
generic instantiation.
|
|
||||||
- `options` now treats `proc` like other pointer types, meaning `nil` proc variables
|
|
||||||
are converted to `None`.
|
|
||||||
- Added `os.normalizePathEnd` for additional path sanitization.
|
|
||||||
- Added `times.fromUnixFloat,toUnixFloat`, sub-second resolution versions of
|
|
||||||
`fromUnix`,`toUnixFloat`.
|
|
||||||
- Added `wrapnils` module for chains of field-access and indexing where the LHS
|
|
||||||
can be nil. This simplifies code by reducing need for if-else branches around
|
|
||||||
intermediate maybe nil values. E.g. `echo ?.n.typ.kind`.
|
|
||||||
- Added `minIndex`, `maxIndex` and `unzip` to the `sequtils` module.
|
|
||||||
- Added `os.isRelativeTo` to tell whether a path is relative to another.
|
|
||||||
- Added `resetOutputFormatters` to `unittest`.
|
|
||||||
- Added `expectIdent` to the `macros` module.
|
|
||||||
- Added `os.isValidFilename` that returns `true` if `filename` argument is valid
|
|
||||||
for cross-platform use.
|
|
||||||
- Added `times.isLeapDay`.
|
|
||||||
- `base64` adds URL-Safe Base64, implements RFC-4648 Section-7.
|
|
||||||
- Added a new module, `std / compilesettings` for querying the compiler about
|
|
||||||
diverse configuration settings.
|
|
||||||
- Added `net.getPeerCertificates` and `asyncnet.getPeerCertificates` for
|
|
||||||
retrieving the verified certificate chain of the peer we are connected to
|
|
||||||
through an SSL-wrapped `Socket`/`AsyncSocket`.
|
|
||||||
- Added `browsers.openDefaultBrowser` without URL, implements IETF RFC-6694 Section-3.
|
|
||||||
- Added `jsconsole.trace`, `jsconsole.table`, `jsconsole.exception` for JavaScript target.
|
|
||||||
- Added `distinctBase` overload for values: `assert 12.MyInt.distinctBase == 12`
|
|
||||||
- Added new module `std/stackframes`, in particular `setFrameMsg`, which enables
|
|
||||||
custom runtime annotation of stackframes, see #13351 for examples.
|
|
||||||
Turn on/off via `--stackTraceMsgs:on/off`.
|
|
||||||
- Added `sequtils.countIt`, allowing for counting items using a predicate.
|
|
||||||
- Added a `with` macro for easy function chaining that's available everywhere,
|
|
||||||
there is no need to concern your APIs with returning the first argument
|
|
||||||
to enable "chaining", instead use the dedicated macro `with` that
|
|
||||||
was designed for it. For example:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
import std/with
|
|
||||||
|
|
||||||
type
|
|
||||||
Foo = object
|
|
||||||
col, pos: string
|
|
||||||
|
|
||||||
proc setColor(f: var Foo; r, g, b: int) = f.col = $(r, g, b)
|
|
||||||
proc setPosition(f: var Foo; x, y: float) = f.pos = $(x, y)
|
|
||||||
|
|
||||||
var f: Foo
|
|
||||||
with(f, setColor(2, 3, 4), setPosition(0.0, 1.0))
|
|
||||||
echo f
|
|
||||||
```
|
|
||||||
|
|
||||||
- `macros.newLit` now works for ref object types.
|
|
||||||
- `macro pragmas` can now be used in type sections.
|
|
||||||
- 5 new pragmas were added to Nim in order to make the upcoming tooling more
|
|
||||||
convenient to use. Nim compiler checks these pragmas for syntax but otherwise
|
|
||||||
ignores them. The pragmas are `requires`, `ensures`, `assume`, `assert`, `invariant`.
|
|
||||||
- `system.writeFile` has been overloaded to also support `openarray[byte]`.
|
|
||||||
- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations`
|
|
||||||
and only returns once all pending async operations are guaranteed to have completed.
|
|
||||||
- `sequtils.zip` now returns a sequence of anonymous tuples i.e. those tuples
|
|
||||||
now do not have fields named "a" and "b".
|
|
||||||
- `distinctBase` has been moved from `sugar` to `typetraits` and now it is
|
|
||||||
implemented as compiler type trait instead of macro. `distinctBase` in sugar
|
|
||||||
module is now deprecated.
|
|
||||||
- `CountTable.mget` has been removed from `tables.nim`. It didn't work, and it
|
|
||||||
was an oversight to be included in v1.0.
|
|
||||||
- `tables.merge(CountTable, CountTable): CountTable` has been removed.
|
|
||||||
It didn't work well together with the existing inplace version of the same proc
|
|
||||||
(`tables.merge(var CountTable, CountTable)`).
|
|
||||||
It was an oversight to be included in v1.0.
|
|
||||||
- `asyncdispatch.drain` now consistently uses the passed timeout value for all
|
|
||||||
iterations of the event loop, and not just the first iteration.
|
|
||||||
This is more consistent with the other asyncdispatch APIs, and allows
|
|
||||||
`asyncdispatch.drain` to be more efficient.
|
|
||||||
- `base64.encode` and `base64.decode` were made faster by about 50%.
|
|
||||||
- `htmlgen` adds [MathML](https://wikipedia.org/wiki/MathML) support
|
|
||||||
(ISO 40314).
|
|
||||||
- `macros.eqIdent` is now invariant to export markers and backtick quotes.
|
|
||||||
- `htmlgen.html` allows `lang` in the `<html>` tag and common valid attributes.
|
|
||||||
- `macros.basename` and `basename=` got support for `PragmaExpr`,
|
|
||||||
so that an expression like `MyEnum {.pure.}` is handled correctly.
|
|
||||||
- `httpclient.maxredirects` changed from `int` to `Natural`, because negative values
|
|
||||||
serve no purpose whatsoever.
|
|
||||||
- `httpclient.newHttpClient` and `httpclient.newAsyncHttpClient` added `headers`
|
|
||||||
argument to set initial HTTP Headers, instead of a hardcoded empty `newHttpHeader()`.
|
|
||||||
- `parseutils.parseUntil` has now a different behaviour if the `until` parameter is
|
|
||||||
empty. This was required for intuitive behaviour of the strscans module
|
|
||||||
(see bug #13605).
|
|
||||||
- `strutils.formatFloat` with `precision = 0` has the same behavior in all
|
|
||||||
backends, and it is compatible with Python's behavior,
|
|
||||||
e.g. `formatFloat(3.14159, precision = 0)` is now `3`, not `3.`.
|
|
||||||
- `times.parse` now only uses input to compute its result, and not `now`:
|
|
||||||
`parse("2020", "YYYY", utc())` is now `2020-01-01T00:00:00Z` instead of
|
|
||||||
`2020-03-02T00:00:00Z` if run on 03-02; it also doesn't crash anymore when
|
|
||||||
used on 29th, 30th, 31st of each month.
|
|
||||||
- `httpcore.==(string, HttpCode)` is now deprecated due to lack of practical
|
|
||||||
usage. The `$` operator can be used to obtain the string form of `HttpCode`
|
|
||||||
for comparison if desired.
|
|
||||||
- `std/oswalkdir` was buggy, it's now deprecated and reuses `std/os` procs.
|
|
||||||
- `os.walkDir` and `os.walkDirRec` now have new flag, `checkDir` (default: false).
|
|
||||||
If it is set to true, it will throw if input dir is invalid instead of a noop
|
|
||||||
(which is the default behaviour, as it was before this change),
|
|
||||||
`os.walkDirRec` only throws if top-level dir is invalid, but ignores errors for
|
|
||||||
subdirs, otherwise it would be impossible to resume iteration.
|
|
||||||
- The `FD` variant of `selector.unregister` for `ioselector_epoll` and
|
|
||||||
`ioselector_select` now properly handle the `Event.User` select event type.
|
|
||||||
- `joinPath` path normalization when `/` is the first argument works correctly:
|
|
||||||
`assert "/" / "/a" == "/a"`. Fixed the edge case: `assert "" / "" == ""`.
|
|
||||||
- `xmltree` now adds indentation consistently to child nodes for any number
|
|
||||||
of children nodes.
|
|
||||||
- `os.splitPath()` behavior synchronized with `os.splitFile()` to return "/"
|
|
||||||
as the dir component of `/root_sub_dir` instead of the empty string.
|
|
||||||
- The deprecated `lc` macro has been removed from `sugar`. It is now replaced
|
|
||||||
with the more powerful `collect` macro.
|
|
||||||
- `os.relativePath("foo", "foo")` is now `"."`, not `""`, as `""` means invalid
|
|
||||||
path and shouldn't be conflated with `"."`; use `-d:nimOldRelativePathBehavior`
|
|
||||||
to restore the old behavior.
|
|
||||||
- `os.joinPath(a, b)` now honors trailing slashes in `b` (or `a` if `b` = "").
|
|
||||||
- `base64.encode` no longer supports `lineLen` and `newLine`.
|
|
||||||
Use `base64.encodeMime` instead.
|
|
||||||
|
|
||||||
|
|
||||||
### Breaking changes
|
|
||||||
|
|
||||||
- `net.newContext` now performs SSL Certificate checking on Linux and OSX.
|
|
||||||
Define `nimDisableCertificateValidation` to disable it globally.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Language changes
|
|
||||||
|
|
||||||
- An `align` pragma can now be used for variables and object fields, similar
|
|
||||||
to the `alignas` declaration modifier in C/C++.
|
|
||||||
- The `=sink` type bound operator is now optional. The compiler can now use a
|
|
||||||
combination of `=destroy` and `copyMem` to move objects efficiently.
|
|
||||||
- Unsigned integer operators have been fixed to allow promotion of the first operand.
|
|
||||||
- Conversions to unsigned integers are unchecked at runtime, imitating earlier Nim
|
|
||||||
versions. The documentation was improved to acknowledge this special case.
|
|
||||||
See https://github.com/nim-lang/RFCs/issues/175 for more details.
|
|
||||||
- There is a new syntax for lvalue references: `var b {.byaddr.} = expr` enabled by
|
|
||||||
`import std/decls`.
|
|
||||||
- `var a {.foo.}: MyType = expr` now lowers to `foo(a, MyType, expr)` for
|
|
||||||
non-builtin pragmas, enabling things like lvalue references (see `decls.byaddr`).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Compiler changes
|
|
||||||
|
|
||||||
- The generated JS code uses spaces, instead of mixing spaces and tabs.
|
|
||||||
- The Nim compiler now supports the ``--asm`` command option for easier
|
|
||||||
inspection of the produced assembler code.
|
|
||||||
- The Nim compiler now supports a new pragma called ``.localPassc`` to
|
|
||||||
pass specific compiler options to the C(++) backend for the C(++) file
|
|
||||||
that was produced from the current Nim module.
|
|
||||||
- The compiler now inferes "sink parameters". To disable this for a specific routine,
|
|
||||||
annotate it with `.nosinks`. To disable it for a section of code, use
|
|
||||||
`{.push sinkInference: off.}`...`{.pop.}`.
|
|
||||||
- The compiler now supports a new switch `--panics:on` that turns runtime
|
|
||||||
errors like `IndexError` or `OverflowError` into fatal errors that **cannot**
|
|
||||||
be caught via Nim's `try` statement. `--panics:on` can improve the
|
|
||||||
runtime efficiency and code size of your program significantly.
|
|
||||||
- The compiler now warns about inheriting directly from `system.Exception` as
|
|
||||||
this is **very bad** style. You should inherit from `ValueError`, `IOError`,
|
|
||||||
`OSError` or from a different specific exception type that inherits from
|
|
||||||
`CatchableError` and cannot be confused with a `Defect`.
|
|
||||||
- The error reporting for Nim's effect system has been improved.
|
|
||||||
- Implicit conversions for `const` behave correctly now, meaning that code like
|
|
||||||
`const SOMECONST = 0.int; procThatTakesInt32(SOMECONST)` will be illegal now.
|
|
||||||
Simply write `const SOMECONST = 0` instead.
|
|
||||||
- The `{.dynlib.}` pragma is now required for exporting symbols when making
|
|
||||||
shared objects on POSIX and macOS, which make it consistent with the behavior
|
|
||||||
on Windows.
|
|
||||||
- The compiler is now more strict about type conversions concerning proc
|
|
||||||
types: Type conversions cannot be used to hide `.raise` effects or side
|
|
||||||
effects, instead a `cast` must be used. With the flag `--useVersion:1.0` the
|
|
||||||
old behaviour is emulated.
|
|
||||||
- The Nim compiler now implements a faster way to detect overflows based
|
|
||||||
on GCC's `__builtin_sadd_overflow` family of functions. (Clang also
|
|
||||||
supports these). Some versions of GCC lack this feature and unfortunately
|
|
||||||
we cannot detect this case reliably. So if you get compilation errors like
|
|
||||||
"undefined reference to `__builtin_saddll_overflow`" compile your programs
|
|
||||||
with `-d:nimEmulateOverflowChecks`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Bugfixes
|
|
||||||
|
|
||||||
- Fixed "`nimgrep --nocolor` is ignored on posix; should be instead: `--nimgrep --color=[auto]|true|false`"
|
|
||||||
([#7591](https://github.com/nim-lang/Nim/issues/7591))
|
|
||||||
- Fixed "Runtime index on const array (of converted obj) causes C-compiler error"
|
|
||||||
([#10514](https://github.com/nim-lang/Nim/issues/10514))
|
|
||||||
- Fixed "windows x86 with vcc compile error with "asmNoStackFrame""
|
|
||||||
([#12298](https://github.com/nim-lang/Nim/issues/12298))
|
|
||||||
- Fixed "[TODO] regression: Error: Locks requires --threads:on option"
|
|
||||||
([#12330](https://github.com/nim-lang/Nim/issues/12330))
|
|
||||||
- Fixed "Add --cc option to --help or --fullhelp output"
|
|
||||||
([#12010](https://github.com/nim-lang/Nim/issues/12010))
|
|
||||||
- Fixed "questionable `csize` definition in `system.nim`"
|
|
||||||
([#12187](https://github.com/nim-lang/Nim/issues/12187))
|
|
||||||
- Fixed "os.getAppFilename() returns incorrect results on OpenBSD"
|
|
||||||
([#12389](https://github.com/nim-lang/Nim/issues/12389))
|
|
||||||
- Fixed "HashSet[uint64] slow insertion depending on values"
|
|
||||||
([#11764](https://github.com/nim-lang/Nim/issues/11764))
|
|
||||||
- Fixed "Differences between compiling 'classic call syntax' vs 'method call syntax' ."
|
|
||||||
([#12453](https://github.com/nim-lang/Nim/issues/12453))
|
|
||||||
- Fixed "c -d:nodejs --> SIGSEGV: Illegal storage access"
|
|
||||||
([#12502](https://github.com/nim-lang/Nim/issues/12502))
|
|
||||||
- Fixed "Closure iterator crashes on --newruntime due to "dangling references""
|
|
||||||
([#12443](https://github.com/nim-lang/Nim/issues/12443))
|
|
||||||
- Fixed "No `=destroy` for elements of closure environments other than for latest devel --gc:destructors"
|
|
||||||
([#12577](https://github.com/nim-lang/Nim/issues/12577))
|
|
||||||
- Fixed "strutils:formatBiggestFloat() gives different results in JS mode"
|
|
||||||
([#8242](https://github.com/nim-lang/Nim/issues/8242))
|
|
||||||
- Fixed "Regression (devel): the new `csize_t` definition isn't consistently used, nor tested thoroughly..."
|
|
||||||
([#12597](https://github.com/nim-lang/Nim/issues/12597))
|
|
||||||
- Fixed "tables.take() is defined only for `Table` and missed for other table containers"
|
|
||||||
([#12519](https://github.com/nim-lang/Nim/issues/12519))
|
|
||||||
- Fixed "`pthread_key_t` errors on OpenBSD"
|
|
||||||
([#12135](https://github.com/nim-lang/Nim/issues/12135))
|
|
||||||
- Fixed "newruntime: simple seq pop at ct results in compile error"
|
|
||||||
([#12644](https://github.com/nim-lang/Nim/issues/12644))
|
|
||||||
- Fixed "[Windows] finish.exe C:\Users\<USERNAME>\.nimble\bin is not in your PATH environment variable."
|
|
||||||
([#12319](https://github.com/nim-lang/Nim/issues/12319))
|
|
||||||
- Fixed "Error with strformat + asyncdispatch + const"
|
|
||||||
([#12612](https://github.com/nim-lang/Nim/issues/12612))
|
|
||||||
- Fixed "MultipartData needs $"
|
|
||||||
([#11863](https://github.com/nim-lang/Nim/issues/11863))
|
|
||||||
- Fixed "Nim stdlib style issues with --styleCheck:error"
|
|
||||||
([#12687](https://github.com/nim-lang/Nim/issues/12687))
|
|
||||||
- Fixed "new $nimbleDir path substitution yields unexpected search paths"
|
|
||||||
([#12767](https://github.com/nim-lang/Nim/issues/12767))
|
|
||||||
- Fixed "Regression: inlined procs now get multiple rounds of destructor injection"
|
|
||||||
([#12766](https://github.com/nim-lang/Nim/issues/12766))
|
|
||||||
- Fixed "newruntime: compiler generates defective code"
|
|
||||||
([#12669](https://github.com/nim-lang/Nim/issues/12669))
|
|
||||||
- Fixed "broken windows modules path handling because of 'os.relativePath' breaking changes"
|
|
||||||
([#12734](https://github.com/nim-lang/Nim/issues/12734))
|
|
||||||
- Fixed "for loop tuple syntax not rendered correctly"
|
|
||||||
([#12740](https://github.com/nim-lang/Nim/issues/12740))
|
|
||||||
- Fixed "Crash when trying to use `type.name[0]`"
|
|
||||||
([#12804](https://github.com/nim-lang/Nim/issues/12804))
|
|
||||||
- Fixed "Enums should be considered Trivial types in Atomics"
|
|
||||||
([#12812](https://github.com/nim-lang/Nim/issues/12812))
|
|
||||||
- Fixed "Produce static/const initializations for variables when possible"
|
|
||||||
([#12216](https://github.com/nim-lang/Nim/issues/12216))
|
|
||||||
- Fixed "Assigning descriminator field leads to internal assert with --gc:destructors"
|
|
||||||
([#12821](https://github.com/nim-lang/Nim/issues/12821))
|
|
||||||
- Fixed "nimsuggest `use` command does not return all instances of symbol"
|
|
||||||
([#12832](https://github.com/nim-lang/Nim/issues/12832))
|
|
||||||
- Fixed "@[] is a problem for --gc:destructors"
|
|
||||||
([#12820](https://github.com/nim-lang/Nim/issues/12820))
|
|
||||||
- Fixed "Codegen ICE in allPathsAsgnResult"
|
|
||||||
([#12827](https://github.com/nim-lang/Nim/issues/12827))
|
|
||||||
- Fixed "seq[Object with ref and destructor type] doesn't work in old runtime"
|
|
||||||
([#12882](https://github.com/nim-lang/Nim/issues/12882))
|
|
||||||
- Fixed "Destructor not invoked because it is instantiated too late, old runtime"
|
|
||||||
([#12883](https://github.com/nim-lang/Nim/issues/12883))
|
|
||||||
- Fixed "The collect macro does not handle if/case correctly"
|
|
||||||
([#12874](https://github.com/nim-lang/Nim/issues/12874))
|
|
||||||
- Fixed "allow typed/untyped params in magic procs (even if not in stdlib)"
|
|
||||||
([#12911](https://github.com/nim-lang/Nim/issues/12911))
|
|
||||||
- Fixed "ARC/newruntime memory corruption"
|
|
||||||
([#12899](https://github.com/nim-lang/Nim/issues/12899))
|
|
||||||
- Fixed "tasyncclosestall.nim still flaky test: Address already in use"
|
|
||||||
([#12919](https://github.com/nim-lang/Nim/issues/12919))
|
|
||||||
- Fixed "newruntime and computed goto: variables inside the loop are in generated code uninitialised"
|
|
||||||
([#12785](https://github.com/nim-lang/Nim/issues/12785))
|
|
||||||
- Fixed "osx: dsymutil needs to be called for debug builds to keep debug info"
|
|
||||||
([#12735](https://github.com/nim-lang/Nim/issues/12735))
|
|
||||||
- Fixed "codegen ICE with ref objects, gc:destructors"
|
|
||||||
([#12826](https://github.com/nim-lang/Nim/issues/12826))
|
|
||||||
- Fixed "mutable iterator cannot yield named tuples"
|
|
||||||
([#12945](https://github.com/nim-lang/Nim/issues/12945))
|
|
||||||
- Fixed "parsecfg stores "\r\n" line breaks just as "\n""
|
|
||||||
([#12970](https://github.com/nim-lang/Nim/issues/12970))
|
|
||||||
- Fixed "db_postgres.getValue issues warning when no rows found"
|
|
||||||
([#12973](https://github.com/nim-lang/Nim/issues/12973))
|
|
||||||
- Fixed "ARC: Unpacking tuple with seq causes segfault"
|
|
||||||
([#12989](https://github.com/nim-lang/Nim/issues/12989))
|
|
||||||
- Fixed "ARC/newruntime: strutils.join on seq with only empty strings causes segfault"
|
|
||||||
([#12965](https://github.com/nim-lang/Nim/issues/12965))
|
|
||||||
- Fixed "regression (1.0.4): `{.push exportc.}` wrongly affects generic instantiations, causing codegen errors"
|
|
||||||
([#12985](https://github.com/nim-lang/Nim/issues/12985))
|
|
||||||
- Fixed "cdt, crash with --gc:arc, works fine with default gc"
|
|
||||||
([#12978](https://github.com/nim-lang/Nim/issues/12978))
|
|
||||||
- Fixed "ARC: No indexError thrown on out-of-bound seq access, SIGSEGV instead"
|
|
||||||
([#12961](https://github.com/nim-lang/Nim/issues/12961))
|
|
||||||
- Fixed "ARC/async: Returning in a try-block results in wrong codegen"
|
|
||||||
([#12956](https://github.com/nim-lang/Nim/issues/12956))
|
|
||||||
- Fixed "asm keyword is generating wrong output C code when --cc:tcc"
|
|
||||||
([#12988](https://github.com/nim-lang/Nim/issues/12988))
|
|
||||||
- Fixed "Destructor not invoked"
|
|
||||||
([#13026](https://github.com/nim-lang/Nim/issues/13026))
|
|
||||||
- Fixed "ARC/newruntime: Adding inherited var ref object to seq with base type causes segfault"
|
|
||||||
([#12964](https://github.com/nim-lang/Nim/issues/12964))
|
|
||||||
- Fixed "Style check error with JS compiler target"
|
|
||||||
([#13032](https://github.com/nim-lang/Nim/issues/13032))
|
|
||||||
- Fixed "regression(1.0.4): undeclared identifier: 'readLines'; plus another regression and bug"
|
|
||||||
([#13013](https://github.com/nim-lang/Nim/issues/13013))
|
|
||||||
- Fixed "regression(1.04) `invalid pragma: since` with nim js"
|
|
||||||
([#12996](https://github.com/nim-lang/Nim/issues/12996))
|
|
||||||
- Fixed "Sink to MemMove optimization in injectdestructors"
|
|
||||||
([#13002](https://github.com/nim-lang/Nim/issues/13002))
|
|
||||||
- Fixed "--gc:arc: `catch` doesn't work with exception subclassing"
|
|
||||||
([#13072](https://github.com/nim-lang/Nim/issues/13072))
|
|
||||||
- Fixed "nim c --gc:arc --exceptions:{setjmp,goto} incorrectly handles raise; `nim cpp --gc:arc` is ok"
|
|
||||||
([#13070](https://github.com/nim-lang/Nim/issues/13070))
|
|
||||||
- Fixed "typetraits feature request - get subtype of a generic type"
|
|
||||||
([#6454](https://github.com/nim-lang/Nim/issues/6454))
|
|
||||||
- Fixed "CountTable inconsistencies between keys() and len() after setting value to 0"
|
|
||||||
([#12813](https://github.com/nim-lang/Nim/issues/12813))
|
|
||||||
- Fixed "{.align.} pragma is not applied if there is a generic field"
|
|
||||||
([#13122](https://github.com/nim-lang/Nim/issues/13122))
|
|
||||||
- Fixed "ARC, finalizer, allow rebinding the same function multiple times"
|
|
||||||
([#13112](https://github.com/nim-lang/Nim/issues/13112))
|
|
||||||
- Fixed "`nim doc` treats `export localSymbol` incorrectly"
|
|
||||||
([#13100](https://github.com/nim-lang/Nim/issues/13100))
|
|
||||||
- Fixed "--gc:arc SIGSEGV (double free?)"
|
|
||||||
([#13119](https://github.com/nim-lang/Nim/issues/13119))
|
|
||||||
- Fixed "codegen bug with arc"
|
|
||||||
([#13105](https://github.com/nim-lang/Nim/issues/13105))
|
|
||||||
- Fixed "symbols not defined in the grammar"
|
|
||||||
([#10665](https://github.com/nim-lang/Nim/issues/10665))
|
|
||||||
- Fixed "[JS] Move is not defined"
|
|
||||||
([#9674](https://github.com/nim-lang/Nim/issues/9674))
|
|
||||||
- Fixed "[TODO] pathutils.`/` can return invalid AbsoluteFile"
|
|
||||||
([#13121](https://github.com/nim-lang/Nim/issues/13121))
|
|
||||||
- Fixed "regression(1.04) `nim doc main.nim` generates broken html (no css)"
|
|
||||||
([#12998](https://github.com/nim-lang/Nim/issues/12998))
|
|
||||||
- Fixed "Wrong supportsCopyMem on string in type section"
|
|
||||||
([#13095](https://github.com/nim-lang/Nim/issues/13095))
|
|
||||||
- Fixed "Arc, finalizer, out of memory"
|
|
||||||
([#13157](https://github.com/nim-lang/Nim/issues/13157))
|
|
||||||
- Fixed "`--genscript` messes up nimcache and future nim invocations"
|
|
||||||
([#13144](https://github.com/nim-lang/Nim/issues/13144))
|
|
||||||
- Fixed "--gc:arc with --exceptions:goto for "nim c" generate invalid c code"
|
|
||||||
([#13186](https://github.com/nim-lang/Nim/issues/13186))
|
|
||||||
- Fixed "[regression] duplicate member `_i1` codegen bug"
|
|
||||||
([#13195](https://github.com/nim-lang/Nim/issues/13195))
|
|
||||||
- Fixed "RTree investigations with valgrind for --gc:arc"
|
|
||||||
([#13110](https://github.com/nim-lang/Nim/issues/13110))
|
|
||||||
- Fixed "relativePath("foo", ".") returns wrong path"
|
|
||||||
([#13211](https://github.com/nim-lang/Nim/issues/13211))
|
|
||||||
- Fixed "asyncftpclient - problem with welcome.msg"
|
|
||||||
([#4684](https://github.com/nim-lang/Nim/issues/4684))
|
|
||||||
- Fixed "Unclear error message, lowest priority"
|
|
||||||
([#13256](https://github.com/nim-lang/Nim/issues/13256))
|
|
||||||
- Fixed "Channel messages are corrupted"
|
|
||||||
([#13219](https://github.com/nim-lang/Nim/issues/13219))
|
|
||||||
- Fixed "Codegen bug with exportc and case objects"
|
|
||||||
([#13281](https://github.com/nim-lang/Nim/issues/13281))
|
|
||||||
- Fixed "[bugfix] fix #11590: c compiler warnings silently ignored, giving undefined behavior"
|
|
||||||
([#11591](https://github.com/nim-lang/Nim/issues/11591))
|
|
||||||
- Fixed "[CI] tnetdial flaky test"
|
|
||||||
([#13132](https://github.com/nim-lang/Nim/issues/13132))
|
|
||||||
- Fixed "Cross-Compiling with -d:mingw fails to locate compiler under OSX"
|
|
||||||
([#10717](https://github.com/nim-lang/Nim/issues/10717))
|
|
||||||
- Fixed "`nim doc --project` broken with imports below main project file or duplicate names"
|
|
||||||
([#13150](https://github.com/nim-lang/Nim/issues/13150))
|
|
||||||
- Fixed "regression: isNamedTuple(MyGenericTuple[int]) is false, should be true"
|
|
||||||
([#13349](https://github.com/nim-lang/Nim/issues/13349))
|
|
||||||
- Fixed "--gc:arc codegen bug copying objects bound to C structs with missing C struct fields"
|
|
||||||
([#13269](https://github.com/nim-lang/Nim/issues/13269))
|
|
||||||
- Fixed "write requires conversion to string"
|
|
||||||
([#13182](https://github.com/nim-lang/Nim/issues/13182))
|
|
||||||
- Fixed "Some remarks to stdlib documentation"
|
|
||||||
([#13352](https://github.com/nim-lang/Nim/issues/13352))
|
|
||||||
- Fixed "a `check` in unittest generated by template doesn't show actual value"
|
|
||||||
([#6736](https://github.com/nim-lang/Nim/issues/6736))
|
|
||||||
- Fixed "Implicit return with case expression fails with 'var' return."
|
|
||||||
([#3339](https://github.com/nim-lang/Nim/issues/3339))
|
|
||||||
- Fixed "Segfault with closure on arc"
|
|
||||||
([#13314](https://github.com/nim-lang/Nim/issues/13314))
|
|
||||||
- Fixed "[Macro] Crash on malformed case statement with multiple else"
|
|
||||||
([#13255](https://github.com/nim-lang/Nim/issues/13255))
|
|
||||||
- Fixed "regression: `echo 'discard' | nim c -r -` generates a file '-' ; `-` should be treated specially"
|
|
||||||
([#13374](https://github.com/nim-lang/Nim/issues/13374))
|
|
||||||
- Fixed "on OSX, debugging (w gdb or lldb) a nim program crashes at the 1st call to `execCmdEx`"
|
|
||||||
([#9634](https://github.com/nim-lang/Nim/issues/9634))
|
|
||||||
- Fixed "Internal error in getTypeDescAux"
|
|
||||||
([#13378](https://github.com/nim-lang/Nim/issues/13378))
|
|
||||||
- Fixed "gc:arc mode breaks tuple let"
|
|
||||||
([#13368](https://github.com/nim-lang/Nim/issues/13368))
|
|
||||||
- Fixed "Nim compiler hangs for certain C/C++ compiler errors"
|
|
||||||
([#8648](https://github.com/nim-lang/Nim/issues/8648))
|
|
||||||
- Fixed "htmlgen does not support `data-*` attributes"
|
|
||||||
([#13444](https://github.com/nim-lang/Nim/issues/13444))
|
|
||||||
- Fixed "[gc:arc] setLen will cause string not to be null-terminated."
|
|
||||||
([#13457](https://github.com/nim-lang/Nim/issues/13457))
|
|
||||||
- Fixed "joinPath("", "") is "/" ; should be """
|
|
||||||
([#13455](https://github.com/nim-lang/Nim/issues/13455))
|
|
||||||
- Fixed "[CI] flaky test on windows: tests/osproc/texitcode.nim"
|
|
||||||
([#13449](https://github.com/nim-lang/Nim/issues/13449))
|
|
||||||
- Fixed "Casting to float32 on NimVM is broken"
|
|
||||||
([#13479](https://github.com/nim-lang/Nim/issues/13479))
|
|
||||||
- Fixed "`--hints:off` doesn't work (doesn't override ~/.config/nim.cfg)"
|
|
||||||
([#8312](https://github.com/nim-lang/Nim/issues/8312))
|
|
||||||
- Fixed "joinPath("", "") is "/" ; should be """
|
|
||||||
([#13455](https://github.com/nim-lang/Nim/issues/13455))
|
|
||||||
- Fixed "tables.values is broken"
|
|
||||||
([#13496](https://github.com/nim-lang/Nim/issues/13496))
|
|
||||||
- Fixed "global user config can override project specific config"
|
|
||||||
([#9405](https://github.com/nim-lang/Nim/issues/9405))
|
|
||||||
- Fixed "Non deterministic macros and id consistency problem"
|
|
||||||
([#12627](https://github.com/nim-lang/Nim/issues/12627))
|
|
||||||
- Fixed "try expression doesn't work with return on expect branch"
|
|
||||||
([#13490](https://github.com/nim-lang/Nim/issues/13490))
|
|
||||||
- Fixed "CI will break every 4 years on feb 28: times doesn't handle leap years properly"
|
|
||||||
([#13543](https://github.com/nim-lang/Nim/issues/13543))
|
|
||||||
- Fixed "[minor] `nimgrep --word` doesn't work with operators (eg misses `1 +% 2`)"
|
|
||||||
([#13528](https://github.com/nim-lang/Nim/issues/13528))
|
|
||||||
- Fixed "`as` is usable as infix operator but its existence and precedence are not documented"
|
|
||||||
([#13409](https://github.com/nim-lang/Nim/issues/13409))
|
|
||||||
- Fixed "JSON unmarshalling drops seq's items"
|
|
||||||
([#13531](https://github.com/nim-lang/Nim/issues/13531))
|
|
||||||
- Fixed "os.joinPath returns wrong path when head ends '\' or '/' and tail starts '..'."
|
|
||||||
([#13579](https://github.com/nim-lang/Nim/issues/13579))
|
|
||||||
- Fixed "Block-local types with the same name lead to bad codegen (sighashes regression)"
|
|
||||||
([#5170](https://github.com/nim-lang/Nim/issues/5170))
|
|
||||||
- Fixed "tuple codegen error"
|
|
||||||
([#12704](https://github.com/nim-lang/Nim/issues/12704))
|
|
||||||
- Fixed "newHttpHeaders does not accept repeated headers"
|
|
||||||
([#13573](https://github.com/nim-lang/Nim/issues/13573))
|
|
||||||
- Fixed "regression: --incremental:on fails on simplest example"
|
|
||||||
([#13319](https://github.com/nim-lang/Nim/issues/13319))
|
|
||||||
- Fixed "strscan can't get value of last element in format"
|
|
||||||
([#13605](https://github.com/nim-lang/Nim/issues/13605))
|
|
||||||
- Fixed "hashes_examples crashes with "Bus Error" (unaligned access) on sparc64"
|
|
||||||
([#12508](https://github.com/nim-lang/Nim/issues/12508))
|
|
||||||
- Fixed "gc:arc bug with re-used `seq[T]`"
|
|
||||||
([#13596](https://github.com/nim-lang/Nim/issues/13596))
|
|
||||||
- Fixed "`raise CatchableError` is broken with --gc:arc when throwing inside a proc"
|
|
||||||
([#13599](https://github.com/nim-lang/Nim/issues/13599))
|
|
||||||
- Fixed "cpp --gc:arc --exceptions:goto fails to raise with discard"
|
|
||||||
([#13436](https://github.com/nim-lang/Nim/issues/13436))
|
|
||||||
- Fixed "terminal doesn't compile with -d:useWinAnsi"
|
|
||||||
([#13607](https://github.com/nim-lang/Nim/issues/13607))
|
|
||||||
- Fixed "Parsing "sink ptr T" - region needs to be an object type"
|
|
||||||
([#12757](https://github.com/nim-lang/Nim/issues/12757))
|
|
||||||
- Fixed "gc:arc + threads:on + closures compilation error"
|
|
||||||
([#13519](https://github.com/nim-lang/Nim/issues/13519))
|
|
||||||
- Fixed "[ARC] segmentation fault"
|
|
||||||
([#13240](https://github.com/nim-lang/Nim/issues/13240))
|
|
||||||
- Fixed "times.toDateTime buggy on 29th, 30th and 31th of each month"
|
|
||||||
([#13558](https://github.com/nim-lang/Nim/issues/13558))
|
|
||||||
- Fixed "Deque misbehaves on VM"
|
|
||||||
([#13310](https://github.com/nim-lang/Nim/issues/13310))
|
|
||||||
- Fixed "Nimscript listFiles should throw exception when path is not found"
|
|
||||||
([#12676](https://github.com/nim-lang/Nim/issues/12676))
|
|
||||||
- Fixed "koch boot fails if even an empty config.nims is present in ~/.config/nims/ [devel regression]"
|
|
||||||
([#13633](https://github.com/nim-lang/Nim/issues/13633))
|
|
||||||
- Fixed "nim doc generates lots of false positive LockLevel warnings"
|
|
||||||
([#13218](https://github.com/nim-lang/Nim/issues/13218))
|
|
||||||
- Fixed "Arrays are passed by copy to iterators, causing crashes, unnecessary allocations and slowdowns"
|
|
||||||
([#12747](https://github.com/nim-lang/Nim/issues/12747))
|
|
||||||
- Fixed "Range types always uses signed integer as a base type"
|
|
||||||
([#13646](https://github.com/nim-lang/Nim/issues/13646))
|
|
||||||
- Fixed "Generate c code cannot compile with recent devel version"
|
|
||||||
([#13645](https://github.com/nim-lang/Nim/issues/13645))
|
|
||||||
- Fixed "[regression] VM: Error: cannot convert -1 to uint64"
|
|
||||||
([#13661](https://github.com/nim-lang/Nim/issues/13661))
|
|
||||||
- Fixed "Spurious raiseException(Exception) detected"
|
|
||||||
([#13654](https://github.com/nim-lang/Nim/issues/13654))
|
|
||||||
- Fixed "gc:arc memory leak"
|
|
||||||
([#13659](https://github.com/nim-lang/Nim/issues/13659))
|
|
||||||
- Fixed "Error: cannot convert -1 to uint (inside tuples)"
|
|
||||||
([#13671](https://github.com/nim-lang/Nim/issues/13671))
|
|
||||||
- Fixed "strformat issue with --gc:arc"
|
|
||||||
([#13622](https://github.com/nim-lang/Nim/issues/13622))
|
|
||||||
- Fixed "astToStr doesn't work inside generics"
|
|
||||||
([#13524](https://github.com/nim-lang/Nim/issues/13524))
|
|
||||||
- Fixed "oswalkdir.walkDirRec wont return folders"
|
|
||||||
([#11458](https://github.com/nim-lang/Nim/issues/11458))
|
|
||||||
- Fixed "`echo 'echo 1' | nim c -r -` silently gives wrong results (nimBetterRun not updated for stdin)"
|
|
||||||
([#13412](https://github.com/nim-lang/Nim/issues/13412))
|
|
||||||
- Fixed "gc:arc destroys the global variable accidentally."
|
|
||||||
([#13691](https://github.com/nim-lang/Nim/issues/13691))
|
|
||||||
- Fixed "[minor] sigmatch errors should be sorted, for reproducible errors"
|
|
||||||
([#13538](https://github.com/nim-lang/Nim/issues/13538))
|
|
||||||
- Fixed "Exception when converting csize to clong"
|
|
||||||
([#13698](https://github.com/nim-lang/Nim/issues/13698))
|
|
||||||
- Fixed "ARC: variables are no copied on the thread spawn causing crashes"
|
|
||||||
([#13708](https://github.com/nim-lang/Nim/issues/13708))
|
|
||||||
- Fixed "Illegal distinct seq causes compiler crash"
|
|
||||||
([#13720](https://github.com/nim-lang/Nim/issues/13720))
|
|
||||||
- Fixed "cyclic seq definition crashes the compiler"
|
|
||||||
([#13715](https://github.com/nim-lang/Nim/issues/13715))
|
|
||||||
- Fixed "Iterator with openArray parameter make the argument evaluated many times"
|
|
||||||
([#13417](https://github.com/nim-lang/Nim/issues/13417))
|
|
||||||
- Fixed "net/asyncnet: Unable to access peer's certificate chain"
|
|
||||||
([#13299](https://github.com/nim-lang/Nim/issues/13299))
|
|
||||||
- Fixed "Accidentally "SIGSEGV: Illegal storage access" error after arc optimizations (#13325)"
|
|
||||||
([#13709](https://github.com/nim-lang/Nim/issues/13709))
|
|
||||||
- Fixed "Base64 Regression"
|
|
||||||
([#13722](https://github.com/nim-lang/Nim/issues/13722))
|
|
||||||
- Fixed "A regression (?) with --gc:arc and repr"
|
|
||||||
([#13731](https://github.com/nim-lang/Nim/issues/13731))
|
|
||||||
- Fixed "Internal compiler error when using the new variable pragmas"
|
|
||||||
([#13737](https://github.com/nim-lang/Nim/issues/13737))
|
|
||||||
- Fixed "bool conversion produces vcc 2019 warning at cpp compilation stage"
|
|
||||||
([#13744](https://github.com/nim-lang/Nim/issues/13744))
|
|
||||||
- Fixed "Compiler "does not detect" a type recursion error in the wrong code, remaining frozen"
|
|
||||||
([#13763](https://github.com/nim-lang/Nim/issues/13763))
|
|
||||||
- Fixed "[minor] regression: `Foo[0.0] is Foo[-0.0]` is now false"
|
|
||||||
([#13730](https://github.com/nim-lang/Nim/issues/13730))
|
|
||||||
- Fixed "`nim doc` - only whitespace on first line causes segfault"
|
|
||||||
([#13631](https://github.com/nim-lang/Nim/issues/13631))
|
|
||||||
- Fixed "hashset regression"
|
|
||||||
([#13794](https://github.com/nim-lang/Nim/issues/13794))
|
|
||||||
- Fixed "`os.getApplFreebsd` could return incorrect paths in the case of a long path"
|
|
||||||
([#13806](https://github.com/nim-lang/Nim/issues/13806))
|
|
||||||
- Fixed "Destructors are not inherited"
|
|
||||||
([#13810](https://github.com/nim-lang/Nim/issues/13810))
|
|
||||||
- Fixed "io.readLines AssertionError on devel"
|
|
||||||
([#13829](https://github.com/nim-lang/Nim/issues/13829))
|
|
||||||
- Fixed "exceptions:goto accidentally reset the variable during exception handling"
|
|
||||||
([#13782](https://github.com/nim-lang/Nim/issues/13782))
|
|
||||||
|
|
@ -1,874 +0,0 @@
|
||||||
# v1.4.0 - 2020-10-16
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Standard library additions and changes
|
|
||||||
|
|
||||||
- Added some enhancements to `std/jsonutils` module.
|
|
||||||
* Added a possibility to deserialize JSON arrays directly to `HashSet` and
|
|
||||||
`OrderedSet` types and respectively to serialize those types to JSON arrays
|
|
||||||
via `jsonutils.fromJson` and `jsonutils.toJson` procedures.
|
|
||||||
* Added a possibility to deserialize JSON `null` objects to Nim option objects
|
|
||||||
and respectively to serialize Nim option object to JSON object if `isSome`
|
|
||||||
or to JSON null object if `isNone` via `jsonutils.fromJson` and
|
|
||||||
`jsonutils.toJson` procedures.
|
|
||||||
* Added a `Joptions` parameter to `jsonutils.fromJson` currently
|
|
||||||
containing two boolean options `allowExtraKeys` and `allowMissingKeys`.
|
|
||||||
- If `allowExtraKeys` is `true` Nim's object to which the JSON is parsed is
|
|
||||||
not required to have a field for every JSON key.
|
|
||||||
- If `allowMissingKeys` is `true` Nim's object to which JSON is parsed is
|
|
||||||
allowed to have fields without corresponding JSON keys.
|
|
||||||
- Added `bindParams`, `bindParam` to `db_sqlite` for binding parameters into a `SqlPrepared` statement.
|
|
||||||
- Added `tryInsert`,`insert` procs to `db_*` libs which accept primary key column name.
|
|
||||||
- Added `xmltree.newVerbatimText` support create `style`'s,`script`'s text.
|
|
||||||
- `uri` module now implements RFC-2397.
|
|
||||||
- Added [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser)
|
|
||||||
to the `dom` module for the JavaScript target.
|
|
||||||
- The default hash for `Ordinal` has changed to something more bit-scrambling.
|
|
||||||
`import hashes; proc hash(x: myInt): Hash = hashIdentity(x)` recovers the old
|
|
||||||
one in an instantiation context while `-d:nimIntHash1` recovers it globally.
|
|
||||||
- `deques.peekFirst` and `deques.peekLast` now have `var Deque[T] -> var T` overloads.
|
|
||||||
- File handles created from high-level abstractions in the stdlib will no longer
|
|
||||||
be inherited by child processes. In particular, these modules are affected:
|
|
||||||
`asyncdispatch`, `asyncnet`, `system`, `nativesockets`, `net` and `selectors`.
|
|
||||||
|
|
||||||
For `asyncdispatch`, `asyncnet`, `net` and `nativesockets`, an `inheritable`
|
|
||||||
flag has been added to all `proc`s that create sockets, allowing the user to
|
|
||||||
control whether the resulting socket is inheritable. This flag is provided to
|
|
||||||
ease the writing of multi-process servers, where sockets inheritance is
|
|
||||||
desired.
|
|
||||||
|
|
||||||
For a transition period, define `nimInheritHandles` to enable file handle
|
|
||||||
inheritance by default. This flag does **not** affect the `selectors` module
|
|
||||||
due to the differing semantics between operating systems.
|
|
||||||
|
|
||||||
`asyncdispatch.setInheritable`, `system.setInheritable` and
|
|
||||||
`nativesockets.setInheritable` are also introduced for setting file handle or
|
|
||||||
socket inheritance. Not all platforms have these `proc`s defined.
|
|
||||||
|
|
||||||
- The file descriptors created for internal bookkeeping by `ioselector_kqueue`
|
|
||||||
and `ioselector_epoll` will no longer be leaked to child processes.
|
|
||||||
|
|
||||||
- `strutils.formatFloat` with `precision = 0` has been restored to the version
|
|
||||||
1 behaviour that produces a trailing dot, e.g. `formatFloat(3.14159, precision = 0)`
|
|
||||||
is now `3.`, not `3`.
|
|
||||||
- Added `commonPrefixLen` to `critbits`.
|
|
||||||
|
|
||||||
- `relativePath(rel, abs)` and `relativePath(abs, rel)` used to silently give wrong results
|
|
||||||
(see #13222); instead they now use `getCurrentDir` to resolve those cases,
|
|
||||||
and this can now throw in edge cases where `getCurrentDir` throws.
|
|
||||||
`relativePath` also now works for js with `-d:nodejs`.
|
|
||||||
|
|
||||||
- JavaScript and NimScript standard library changes: `streams.StringStream` is
|
|
||||||
now supported in JavaScript, with the limitation that any buffer `pointer`s
|
|
||||||
used must be castable to `ptr string`, any incompatible pointer type will not
|
|
||||||
work. The `lexbase` and `streams` modules used to fail to compile on
|
|
||||||
NimScript due to a bug, but this has been fixed.
|
|
||||||
|
|
||||||
The following modules now compile on both JS and NimScript: `parsecsv`,
|
|
||||||
`parsecfg`, `parsesql`, `xmlparser`, `htmlparser` and `ropes`. Additionally
|
|
||||||
supported for JS is `cstrutils.startsWith` and `cstrutils.endsWith`, for
|
|
||||||
NimScript: `json`, `parsejson`, `strtabs` and `unidecode`.
|
|
||||||
|
|
||||||
- Added `streams.readStr` and `streams.peekStr` overloads to
|
|
||||||
accept an existing string to modify, which avoids memory
|
|
||||||
allocations, similar to `streams.readLine` (#13857).
|
|
||||||
|
|
||||||
- Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom` UDP functionality.
|
|
||||||
|
|
||||||
- `dollars.$` now works for unsigned ints with `nim js`.
|
|
||||||
|
|
||||||
- Improvements to the `bitops` module, including bitslices, non-mutating versions
|
|
||||||
of the original masking functions, `mask`/`masked`, and varargs support for
|
|
||||||
`bitand`, `bitor`, and `bitxor`.
|
|
||||||
|
|
||||||
- `sugar.=>` and `sugar.->` changes: Previously `(x, y: int)` was transformed
|
|
||||||
into `(x: auto, y: int)`, it now becomes `(x: int, y: int)` for consistency
|
|
||||||
with regular proc definitions (although you cannot use semicolons).
|
|
||||||
|
|
||||||
Pragmas and using a name are now allowed on the lefthand side of `=>`. Here
|
|
||||||
is an example of these changes:
|
|
||||||
```nim
|
|
||||||
import sugar
|
|
||||||
|
|
||||||
foo(x, y: int) {.noSideEffect.} => x + y
|
|
||||||
|
|
||||||
# is transformed into
|
|
||||||
|
|
||||||
proc foo(x: int, y: int): auto {.noSideEffect.} = x + y
|
|
||||||
```
|
|
||||||
|
|
||||||
- The fields of `times.DateTime` are now private, and are accessed with getters and deprecated setters.
|
|
||||||
|
|
||||||
- The `times` module now handles the default value for `DateTime` more consistently.
|
|
||||||
Most procs raise an assertion error when given
|
|
||||||
an uninitialized `DateTime`, the exceptions are `==` and `$` (which returns `"Uninitialized DateTime"`).
|
|
||||||
The proc `times.isInitialized` has been added which can be used to check if
|
|
||||||
a `DateTime` has been initialized.
|
|
||||||
|
|
||||||
- Fix a bug where calling `close` on io streams in `osproc.startProcess` was a noop and led to
|
|
||||||
hangs if a process had both reads from stdin and writes (e.g. to stdout).
|
|
||||||
|
|
||||||
- The callback that is passed to `system.onThreadDestruction` must now be `.raises: []`.
|
|
||||||
- The callback that is assigned to `system.onUnhandledException` must now be `.gcsafe`.
|
|
||||||
|
|
||||||
- `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env`
|
|
||||||
parameters.
|
|
||||||
|
|
||||||
- Added a `ssl_config` module containing lists of secure ciphers as recommended by
|
|
||||||
[Mozilla OpSec](https://wiki.mozilla.org/Security/Server_Side_TLS)
|
|
||||||
|
|
||||||
- `net.newContext` now defaults to the list of ciphers targeting
|
|
||||||
["Intermediate compatibility"](https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28recommended.29)
|
|
||||||
per Mozilla's recommendation instead of `ALL`. This change should protect
|
|
||||||
users from the use of weak and insecure ciphers while still provides
|
|
||||||
adequate compatibility with the majority of the Internet.
|
|
||||||
|
|
||||||
- A new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` operations for json
|
|
||||||
serialization/deserialization of custom types was added.
|
|
||||||
|
|
||||||
- A new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``
|
|
||||||
was added.
|
|
||||||
- Added `rstgen.rstToLatex` a convenience proc for `renderRstToOut` and `initRstGenerator`.
|
|
||||||
- Added `os.normalizeExe`.
|
|
||||||
- `macros.newLit` now preserves named vs unnamed tuples.
|
|
||||||
- Added `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution.
|
|
||||||
- Added `typetraits.elementType` to get the element type of an iterable.
|
|
||||||
- `typetraits.$` changes: `$(int,)` is now `"(int,)"` instead of `"(int)"`;
|
|
||||||
`$tuple[]` is now `"tuple[]"` instead of `"tuple"`;
|
|
||||||
`$((int, float), int)` is now `"((int, float), int)"` instead of `"(tuple of (int, float), int)"`
|
|
||||||
- Added `macros.extractDocCommentsAndRunnables` helper.
|
|
||||||
|
|
||||||
- `strformat.fmt` and `strformat.&` support `specifier =`. `fmt"{expr=}"` now
|
|
||||||
expands to `fmt"expr={expr}"`.
|
|
||||||
- Deprecations: instead of `os.existsDir` use `dirExists`, instead of `os.existsFile` use `fileExists`.
|
|
||||||
|
|
||||||
- Added the `jsre` module, [Regular Expressions for the JavaScript target.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions).
|
|
||||||
- Made `maxLines` argument `Positive` in `logging.newRollingFileLogger`,
|
|
||||||
because negative values will result in a new file being created for each logged
|
|
||||||
line which doesn't make sense.
|
|
||||||
- Changed `log` in `logging` to use proper log level for JavaScript,
|
|
||||||
e.g. `debug` uses `console.debug`, `info` uses `console.info`, `warn` uses `console.warn`, etc.
|
|
||||||
- Tables, HashSets, SharedTables and deques don't require anymore that the passed
|
|
||||||
initial size must be a power of two - this is done internally.
|
|
||||||
Proc `rightSize` for Tables and HashSets is deprecated, as it is not needed anymore.
|
|
||||||
`CountTable.inc` takes `val: int` again not `val: Positive`; i.e. it can "count down" again.
|
|
||||||
- Removed deprecated symbols from `macros` module, some of which were deprecated already in `0.15`.
|
|
||||||
- Removed `sugar.distinctBase`, deprecated since `0.19`. Use `typetraits.distinctBase`.
|
|
||||||
- `asyncdispatch.PDispatcher.handles` is exported so that an external low-level libraries can access it.
|
|
||||||
|
|
||||||
- `std/with`, `sugar.dup` now support object field assignment expressions:
|
|
||||||
```nim
|
|
||||||
import std/with
|
|
||||||
|
|
||||||
type Foo = object
|
|
||||||
x, y: int
|
|
||||||
|
|
||||||
var foo = Foo()
|
|
||||||
with foo:
|
|
||||||
x = 10
|
|
||||||
y = 20
|
|
||||||
|
|
||||||
echo foo
|
|
||||||
```
|
|
||||||
|
|
||||||
- Proc `math.round` is no longer deprecated. The advice to use `strformat` instead
|
|
||||||
cannot be applied to every use case. The limitations and the (lack of) reliability
|
|
||||||
of `round` are well documented.
|
|
||||||
|
|
||||||
- Added `getprotobyname` to `winlean`. Added `getProtoByname` to `nativesockets` which returns a protocol code
|
|
||||||
from the database that matches the protocol `name`.
|
|
||||||
|
|
||||||
- Added missing attributes and methods to `dom.Navigator` like `deviceMemory`, `onLine`, `vibrate()`, etc.
|
|
||||||
|
|
||||||
- Added `strutils.indentation` and `strutils.dedent` which enable indented string literals:
|
|
||||||
```nim
|
|
||||||
import strutils
|
|
||||||
echo dedent """
|
|
||||||
This
|
|
||||||
is
|
|
||||||
cool!
|
|
||||||
"""
|
|
||||||
```
|
|
||||||
|
|
||||||
- Added `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname.
|
|
||||||
|
|
||||||
- Added `readLines(p: Process)` to `osproc`.
|
|
||||||
|
|
||||||
- Added the below `toX` procs for collections. The usage is similar to procs such as
|
|
||||||
`sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create the
|
|
||||||
respective empty collection and add items manually.
|
|
||||||
* `critbits.toCritBitTree`, which creates a `CritBitTree` from an `openArray` of
|
|
||||||
items or an `openArray` of pairs.
|
|
||||||
* `deques.toDeque`, which creates a `Deque` from an `openArray`.
|
|
||||||
* `heapqueue.toHeapQueue`, which creates a `HeapQueue` from an `openArray`.
|
|
||||||
* `intsets.toIntSet`, which creates an `IntSet` from an `openArray`.
|
|
||||||
|
|
||||||
- Added `progressInterval` argument to `asyncftpclient.newAsyncFtpClient` to control the interval
|
|
||||||
at which progress callbacks are called.
|
|
||||||
|
|
||||||
- Added `os.copyFileToDir`.
|
|
||||||
|
|
||||||
## Language changes
|
|
||||||
|
|
||||||
- The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts
|
|
||||||
`wasMoved` calls where needed.
|
|
||||||
- The `=` hook is now called `=copy` for clarity. The old name `=` is still available so there
|
|
||||||
is no need to update your code. This change was backported to 1.2 too so you can use the
|
|
||||||
more readable `=copy` without loss of compatibility.
|
|
||||||
|
|
||||||
- In the newruntime it is now allowed to assign to the discriminator field
|
|
||||||
without restrictions as long as the case object doesn't have a custom destructor.
|
|
||||||
The discriminator value doesn't have to be a constant either. If you have a
|
|
||||||
custom destructor for a case object and you do want to freely assign discriminator
|
|
||||||
fields, it is recommended to refactor the object into 2 objects like this:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
type
|
|
||||||
MyObj = object
|
|
||||||
case kind: bool
|
|
||||||
of true: y: ptr UncheckedArray[float]
|
|
||||||
of false: z: seq[int]
|
|
||||||
|
|
||||||
proc `=destroy`(x: MyObj) =
|
|
||||||
if x.kind and x.y != nil:
|
|
||||||
deallocShared(x.y)
|
|
||||||
```
|
|
||||||
Refactor into:
|
|
||||||
```nim
|
|
||||||
type
|
|
||||||
MySubObj = object
|
|
||||||
val: ptr UncheckedArray[float]
|
|
||||||
MyObj = object
|
|
||||||
case kind: bool
|
|
||||||
of true: y: MySubObj
|
|
||||||
of false: z: seq[int]
|
|
||||||
|
|
||||||
proc `=destroy`(x: MySubObj) =
|
|
||||||
if x.val != nil:
|
|
||||||
deallocShared(x.val)
|
|
||||||
```
|
|
||||||
- `getImpl` on enum type symbols now returns field syms instead of idents. This helps
|
|
||||||
with writing typed macros. The old behavior for backwards compatibility can be restored
|
|
||||||
with `--useVersion:1.0`.
|
|
||||||
- The typed AST for proc headers will now have the arguments be syms instead of idents.
|
|
||||||
This helps with writing typed macros. The old behaviour for backwards compatibility can
|
|
||||||
be restored with `--useVersion:1.0`.
|
|
||||||
- ``let`` statements can now be used without a value if declared with
|
|
||||||
``importc``/``importcpp``/``importjs``/``importobjc``.
|
|
||||||
- The keyword `from` is now usable as an operator.
|
|
||||||
- Exceptions inheriting from `system.Defect` are no longer tracked with
|
|
||||||
the `.raises: []` exception tracking mechanism. This is more consistent with the
|
|
||||||
built-in operations. The following always used to compile (and still does):
|
|
||||||
```nim
|
|
||||||
proc mydiv(a, b): int {.raises: [].} =
|
|
||||||
a div b # can raise an DivByZeroDefect
|
|
||||||
```
|
|
||||||
|
|
||||||
Now also this compiles:
|
|
||||||
```nim
|
|
||||||
proc mydiv(a, b): int {.raises: [].} =
|
|
||||||
if b == 0: raise newException(DivByZeroDefect, "division by zero")
|
|
||||||
else: result = a div b
|
|
||||||
```
|
|
||||||
|
|
||||||
The reason for this is that `DivByZeroDefect` inherits from `Defect` and
|
|
||||||
with `--panics:on` `Defects` become unrecoverable errors.
|
|
||||||
|
|
||||||
- Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purposes.
|
|
||||||
- Deprecated the `{.unroll.}` pragma, because it was always ignored by the compiler anyway.
|
|
||||||
- Removed the deprecated `strutils.isNilOrWhitespace`.
|
|
||||||
- Removed the deprecated `sharedtables.initSharedTable`.
|
|
||||||
- Removed the deprecated `asyncdispatch.newAsyncNativeSocket`.
|
|
||||||
- Removed the deprecated `dom.releaseEvents` and `dom.captureEvents`.
|
|
||||||
|
|
||||||
- Removed `sharedlist.initSharedList`, was deprecated and produces undefined behaviour.
|
|
||||||
|
|
||||||
- There is a new experimental feature called "strictFuncs" which makes the definition of
|
|
||||||
`.noSideEffect` stricter. [See here](manual_experimental.html#stricts-funcs)
|
|
||||||
for more information.
|
|
||||||
|
|
||||||
- "for-loop macros" (see [the manual](manual.html#macros-for-loop-macros)) are no longer
|
|
||||||
an experimental feature. In other words, you don't have to write pragma
|
|
||||||
`{.experimental: "forLoopMacros".}` if you want to use them.
|
|
||||||
|
|
||||||
- Added the ``.noalias`` pragma. It is mapped to C's ``restrict`` keyword for the increased
|
|
||||||
performance this keyword can enable.
|
|
||||||
|
|
||||||
- `items` no longer compiles with enums with holes as its behavior was error prone, see #14004.
|
|
||||||
- `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via
|
|
||||||
`--deepcopy:on`.
|
|
||||||
|
|
||||||
- Added the `std/effecttraits` module for introspection of the inferred effects.
|
|
||||||
We hope this enables `async` macros that are precise about the possible exceptions that
|
|
||||||
can be raised.
|
|
||||||
- The pragma blocks `{.gcsafe.}: ...` and `{.noSideEffect.}: ...` can now also be
|
|
||||||
written as `{.cast(gcsafe).}: ...` and `{.cast(noSideEffect).}: ...`. This is the new
|
|
||||||
preferred way of writing these, emphasizing their unsafe nature.
|
|
||||||
|
|
||||||
|
|
||||||
## Compiler changes
|
|
||||||
|
|
||||||
- Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`.
|
|
||||||
- The `define` and `undef` pragmas have been de-deprecated.
|
|
||||||
- New command: `nim r main.nim [args...]` which compiles and runs main.nim, and implies `--usenimcache`
|
|
||||||
so that the output is saved to $nimcache/main$exeExt, using the same logic as `nim c -r` to
|
|
||||||
avoid recompilations when sources don't change.
|
|
||||||
Example:
|
|
||||||
```bash
|
|
||||||
nim r compiler/nim.nim --help # only compiled the first time
|
|
||||||
echo 'import os; echo getCurrentCompilerExe()' | nim r - # this works too
|
|
||||||
nim r compiler/nim.nim --fullhelp # no recompilation
|
|
||||||
nim r --nimcache:/tmp main # binary saved to /tmp/main
|
|
||||||
```
|
|
||||||
- `--hint:processing` is now supported and means `--hint:processing:on`
|
|
||||||
(likewise with other hints and warnings), which is consistent with all other bool flags.
|
|
||||||
(since 1.3.3).
|
|
||||||
- `nim doc -r main` and `nim rst2html -r main` now call `openDefaultBrowser`.
|
|
||||||
- Added the new hint `--hint:msgOrigin` will show where a compiler msg (hint|warning|error)
|
|
||||||
was generated; this helps in particular when it's non obvious where it came from
|
|
||||||
either because multiple locations generate the same message, or because the
|
|
||||||
message involves runtime formatting.
|
|
||||||
- Added the new flag `--backend:js|c|cpp|objc` (or -b:js etc), to change the backend; can be
|
|
||||||
used with any command (e.g. nim r, doc, check etc); safe to re-assign.
|
|
||||||
- Added the new flag `--doccmd:cmd` to pass additional flags for runnableExamples,
|
|
||||||
e.g.: `--doccmd:-d:foo --threads`
|
|
||||||
use `--doccmd:skip` to skip runnableExamples and rst test snippets.
|
|
||||||
- Added the new flag `--usenimcache` to output binary files to nimcache.
|
|
||||||
- `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override
|
|
||||||
how an example is compiled and run, for example to change the backend.
|
|
||||||
- `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified
|
|
||||||
(with or without `--project`); passing `--project` now automatically generates
|
|
||||||
an index and enables search.
|
|
||||||
See [docgen](docgen.html#introduction-quick-start) for details.
|
|
||||||
- Removed the `--oldNewlines` switch.
|
|
||||||
- Removed the `--laxStrings` switch for mutating the internal zero terminator on strings.
|
|
||||||
- Removed the `--oldast` switch.
|
|
||||||
- Removed the `--oldgensym` switch.
|
|
||||||
- `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is
|
|
||||||
now "typed" instead of "stmt".
|
|
||||||
- Sink inference is now disabled per default and has to enabled explicitly via
|
|
||||||
`--sinkInference:on`. *Note*: For the standard library sink inference remains
|
|
||||||
enabled. This change is most relevant for the `--gc:arc`, `--gc:orc` memory
|
|
||||||
management modes.
|
|
||||||
|
|
||||||
|
|
||||||
## Tool changes
|
|
||||||
|
|
||||||
- `nimsuggest` now returns both the forward declaration and the
|
|
||||||
implementation location upon a `def` query. Previously the behavior was
|
|
||||||
to return the forward declaration only.
|
|
||||||
|
|
||||||
|
|
||||||
## Bugfixes
|
|
||||||
|
|
||||||
- Fixed "repr() not available for uint{,8,16,32,64} under --gc:arc"
|
|
||||||
([#13872](https://github.com/nim-lang/Nim/issues/13872))
|
|
||||||
- Fixed "Critical: 1 completed Future, multiple await: Only 1 await will be awakened (the last one)"
|
|
||||||
([#13889](https://github.com/nim-lang/Nim/issues/13889))
|
|
||||||
- Fixed "crash on openarray interator with argument in stmtListExpr"
|
|
||||||
([#13739](https://github.com/nim-lang/Nim/issues/13739))
|
|
||||||
- Fixed "Some compilers on Windows don't work"
|
|
||||||
([#13910](https://github.com/nim-lang/Nim/issues/13910))
|
|
||||||
- Fixed "httpclient hangs if it recieves an HTTP 204 (No Content)"
|
|
||||||
([#13894](https://github.com/nim-lang/Nim/issues/13894))
|
|
||||||
- Fixed ""distinct uint64" type corruption on 32-bit, when using {.borrow.} operators"
|
|
||||||
([#13902](https://github.com/nim-lang/Nim/issues/13902))
|
|
||||||
- Fixed "Regression: impossible to use typed pragmas with proc types"
|
|
||||||
([#13909](https://github.com/nim-lang/Nim/issues/13909))
|
|
||||||
- Fixed "openssl wrapper corrupts stack on OpenSSL 1.1.1f + Android"
|
|
||||||
([#13903](https://github.com/nim-lang/Nim/issues/13903))
|
|
||||||
- Fixed "C compile error with --gc:arc on version 1.2.0 "unknown type name 'TGenericSeq'"
|
|
||||||
([#13863](https://github.com/nim-lang/Nim/issues/13863))
|
|
||||||
- Fixed "var return type for proc doesn't work at c++ backend"
|
|
||||||
([#13848](https://github.com/nim-lang/Nim/issues/13848))
|
|
||||||
- Fixed "TimeFormat() should raise an error but craches at compilation time"
|
|
||||||
([#12864](https://github.com/nim-lang/Nim/issues/12864))
|
|
||||||
- Fixed "gc:arc cannot fully support threadpool with FlowVar"
|
|
||||||
([#13781](https://github.com/nim-lang/Nim/issues/13781))
|
|
||||||
- Fixed "simple 'var openarray[char]' assignment crash when the openarray source is a local string and using gc:arc"
|
|
||||||
([#14003](https://github.com/nim-lang/Nim/issues/14003))
|
|
||||||
- Fixed "Cant use expressions with `when` in `type` sections."
|
|
||||||
([#14007](https://github.com/nim-lang/Nim/issues/14007))
|
|
||||||
- Fixed "`for a in MyEnum` gives incorrect results with enum with holes"
|
|
||||||
([#14001](https://github.com/nim-lang/Nim/issues/14001))
|
|
||||||
- Fixed "Trivial crash"
|
|
||||||
([#12741](https://github.com/nim-lang/Nim/issues/12741))
|
|
||||||
- Fixed "Enum with holes cannot be used as Table index"
|
|
||||||
([#12834](https://github.com/nim-lang/Nim/issues/12834))
|
|
||||||
- Fixed "spawn proc that uses typedesc crashes the compiler"
|
|
||||||
([#14014](https://github.com/nim-lang/Nim/issues/14014))
|
|
||||||
- Fixed "Docs Search `Results` box styling is not Dark Mode Friendly"
|
|
||||||
([#13972](https://github.com/nim-lang/Nim/issues/13972))
|
|
||||||
- Fixed "--gc:arc -d:useSysAssert undeclared identifier `cstderr` with newSeq"
|
|
||||||
([#14038](https://github.com/nim-lang/Nim/issues/14038))
|
|
||||||
- Fixed "issues in the manual"
|
|
||||||
([#12486](https://github.com/nim-lang/Nim/issues/12486))
|
|
||||||
- Fixed "Annoying warning: inherit from a more precise exception type like ValueError, IOError or OSError [InheritFromException]"
|
|
||||||
([#14052](https://github.com/nim-lang/Nim/issues/14052))
|
|
||||||
- Fixed "relativePath("foo", "/") and relativePath("/", "foo") is wrong"
|
|
||||||
([#13222](https://github.com/nim-lang/Nim/issues/13222))
|
|
||||||
- Fixed "[regression] `parseEnum` does not work anymore for enums with holes"
|
|
||||||
([#14030](https://github.com/nim-lang/Nim/issues/14030))
|
|
||||||
- Fixed "Exception types in the stdlib should inherit from `CatchableError` or `Defect`, not `Exception`"
|
|
||||||
([#10288](https://github.com/nim-lang/Nim/issues/10288))
|
|
||||||
- Fixed "Make debugSend and debugRecv procs public in smtp.nim"
|
|
||||||
([#12189](https://github.com/nim-lang/Nim/issues/12189))
|
|
||||||
- Fixed "xmltree need add raw text, when add style element"
|
|
||||||
([#14064](https://github.com/nim-lang/Nim/issues/14064))
|
|
||||||
- Fixed "raises requirement does not propagate to derived methods"
|
|
||||||
([#8481](https://github.com/nim-lang/Nim/issues/8481))
|
|
||||||
- Fixed "tests/stdlib/tgetaddrinfo.nim fails on NetBSD"
|
|
||||||
([#14091](https://github.com/nim-lang/Nim/issues/14091))
|
|
||||||
- Fixed "tests/niminaction/Chapter8/sdl/sdl_test.nim fails on NetBSD"
|
|
||||||
([#14088](https://github.com/nim-lang/Nim/issues/14088))
|
|
||||||
- Fixed "Incorrect escape sequence for example in jsffi library documentation"
|
|
||||||
([#14110](https://github.com/nim-lang/Nim/issues/14110))
|
|
||||||
- Fixed "HCR: Can not link exported const, in external library"
|
|
||||||
([#13915](https://github.com/nim-lang/Nim/issues/13915))
|
|
||||||
- Fixed "Cannot import std/unidecode"
|
|
||||||
([#14112](https://github.com/nim-lang/Nim/issues/14112))
|
|
||||||
- Fixed "macOS: dsymutil should not be called on static libraries"
|
|
||||||
([#14132](https://github.com/nim-lang/Nim/issues/14132))
|
|
||||||
- Fixed "nim jsondoc -o:doc.json filename.nim fails when sequences without a type are used"
|
|
||||||
([#14066](https://github.com/nim-lang/Nim/issues/14066))
|
|
||||||
- Fixed "algorithm.sortedByIt template corrupts tuple input under --gc:arc"
|
|
||||||
([#14079](https://github.com/nim-lang/Nim/issues/14079))
|
|
||||||
- Fixed "Invalid C code with lvalue conversion"
|
|
||||||
([#14160](https://github.com/nim-lang/Nim/issues/14160))
|
|
||||||
- Fixed "strformat: doc example fails"
|
|
||||||
([#14054](https://github.com/nim-lang/Nim/issues/14054))
|
|
||||||
- Fixed "Nim doc fail to run for nim 1.2.0 (nim 1.0.4 is ok)"
|
|
||||||
([#13986](https://github.com/nim-lang/Nim/issues/13986))
|
|
||||||
- Fixed "Exception when converting csize to clong"
|
|
||||||
([#13698](https://github.com/nim-lang/Nim/issues/13698))
|
|
||||||
- Fixed "[Documentation] overloading using named arguments works but is not documented"
|
|
||||||
([#11932](https://github.com/nim-lang/Nim/issues/11932))
|
|
||||||
- Fixed "import os + use of existsDir/dirExists/existsFile/fileExists/findExe in config.nims causes "ambiguous call' error"
|
|
||||||
([#14142](https://github.com/nim-lang/Nim/issues/14142))
|
|
||||||
- Fixed "import os + use of existsDir/dirExists/existsFile/fileExists/findExe in config.nims causes "ambiguous call' error"
|
|
||||||
([#14142](https://github.com/nim-lang/Nim/issues/14142))
|
|
||||||
- Fixed "runnableExamples doc gen crashes compiler with `except Exception as e` syntax"
|
|
||||||
([#14177](https://github.com/nim-lang/Nim/issues/14177))
|
|
||||||
- Fixed "[ARC] Segfault with cyclic references (?)"
|
|
||||||
([#14159](https://github.com/nim-lang/Nim/issues/14159))
|
|
||||||
- Fixed "Semcheck regression when accessing a static parameter in proc"
|
|
||||||
([#14136](https://github.com/nim-lang/Nim/issues/14136))
|
|
||||||
- Fixed "iterator walkDir doesn't work with -d:useWinAnsi"
|
|
||||||
([#14201](https://github.com/nim-lang/Nim/issues/14201))
|
|
||||||
- Fixed "cas is wrong for tcc"
|
|
||||||
([#14151](https://github.com/nim-lang/Nim/issues/14151))
|
|
||||||
- Fixed "proc execCmdEx doesn't work with -d:useWinAnsi"
|
|
||||||
([#14203](https://github.com/nim-lang/Nim/issues/14203))
|
|
||||||
- Fixed "Use -d:nimEmulateOverflowChecks by default?"
|
|
||||||
([#14209](https://github.com/nim-lang/Nim/issues/14209))
|
|
||||||
- Fixed "Old sequences with destructor objects bug"
|
|
||||||
([#14217](https://github.com/nim-lang/Nim/issues/14217))
|
|
||||||
- Fixed "[ARC] ICE when changing the discriminant of a return value"
|
|
||||||
([#14244](https://github.com/nim-lang/Nim/issues/14244))
|
|
||||||
- Fixed "[ARC] ICE with static objects"
|
|
||||||
([#14236](https://github.com/nim-lang/Nim/issues/14236))
|
|
||||||
- Fixed "[ARC] "internal error: environment misses: a" in a finalizer"
|
|
||||||
([#14243](https://github.com/nim-lang/Nim/issues/14243))
|
|
||||||
- Fixed "[ARC] compile failure using repr with object containing `ref seq[string]`"
|
|
||||||
([#14270](https://github.com/nim-lang/Nim/issues/14270))
|
|
||||||
- Fixed "[ARC] implicit move on last use happening on non-last use"
|
|
||||||
([#14269](https://github.com/nim-lang/Nim/issues/14269))
|
|
||||||
- Fixed "[ARC] Compiler crash with a recursive non-ref object variant"
|
|
||||||
([#14294](https://github.com/nim-lang/Nim/issues/14294))
|
|
||||||
- Fixed "htmlparser.parseHtml behaves differently using --gc:arc or --gc:orc"
|
|
||||||
([#13946](https://github.com/nim-lang/Nim/issues/13946))
|
|
||||||
- Fixed "Invalid return value of openProcess is NULL rather than INVALID_HANDLE_VALUE(-1) in windows"
|
|
||||||
([#14289](https://github.com/nim-lang/Nim/issues/14289))
|
|
||||||
- Fixed "ARC codegen bug with inline iterators"
|
|
||||||
([#14219](https://github.com/nim-lang/Nim/issues/14219))
|
|
||||||
- Fixed "Building koch on OpenBSD fails unless the Nim directory is in `$PATH`"
|
|
||||||
([#13758](https://github.com/nim-lang/Nim/issues/13758))
|
|
||||||
- Fixed "[gc:arc] case object assignment SIGSEGV: destroy not called for primitive type "
|
|
||||||
([#14312](https://github.com/nim-lang/Nim/issues/14312))
|
|
||||||
- Fixed "Crash when using thread and --gc:arc "
|
|
||||||
([#13881](https://github.com/nim-lang/Nim/issues/13881))
|
|
||||||
- Fixed "Getting "Warning: Cannot prove that 'result' is initialized" for an importcpp'd proc with `var T` return type"
|
|
||||||
([#14314](https://github.com/nim-lang/Nim/issues/14314))
|
|
||||||
- Fixed "`nim cpp -r --gc:arc` segfaults on caught AssertionError"
|
|
||||||
([#13071](https://github.com/nim-lang/Nim/issues/13071))
|
|
||||||
- Fixed "tests/async/tasyncawait.nim is recently very flaky"
|
|
||||||
([#14320](https://github.com/nim-lang/Nim/issues/14320))
|
|
||||||
- Fixed "Documentation nonexistent quitprocs module"
|
|
||||||
([#14331](https://github.com/nim-lang/Nim/issues/14331))
|
|
||||||
- Fixed "SIGSEV encountered when creating threads in a loop w/ --gc:arc"
|
|
||||||
([#13935](https://github.com/nim-lang/Nim/issues/13935))
|
|
||||||
- Fixed "nim-gdb is missing from all released packages"
|
|
||||||
([#13104](https://github.com/nim-lang/Nim/issues/13104))
|
|
||||||
- Fixed "sysAssert error with gc:arc on 3 line program"
|
|
||||||
([#13862](https://github.com/nim-lang/Nim/issues/13862))
|
|
||||||
- Fixed "compiler error with inline async proc and pragma"
|
|
||||||
([#13998](https://github.com/nim-lang/Nim/issues/13998))
|
|
||||||
- Fixed "[ARC] Compiler crash when adding to a seq[ref Object]"
|
|
||||||
([#14333](https://github.com/nim-lang/Nim/issues/14333))
|
|
||||||
- Fixed "nimvm: sysFatal: unhandled exception: 'sons' is not accessible using discriminant 'kind' of type 'TNode' [FieldError]"
|
|
||||||
([#14340](https://github.com/nim-lang/Nim/issues/14340))
|
|
||||||
- Fixed "[Regression] karax events are not firing "
|
|
||||||
([#14350](https://github.com/nim-lang/Nim/issues/14350))
|
|
||||||
- Fixed "odbcsql module has some wrong integer types"
|
|
||||||
([#9771](https://github.com/nim-lang/Nim/issues/9771))
|
|
||||||
- Fixed "db_sqlite needs sqlPrepared"
|
|
||||||
([#13559](https://github.com/nim-lang/Nim/issues/13559))
|
|
||||||
- Fixed "[Regression] `createThread` is not GC-safe"
|
|
||||||
([#14370](https://github.com/nim-lang/Nim/issues/14370))
|
|
||||||
- Fixed "Broken example on hot code reloading"
|
|
||||||
([#14380](https://github.com/nim-lang/Nim/issues/14380))
|
|
||||||
- Fixed "runnableExamples block with `except` on specified error fails with `nim doc`"
|
|
||||||
([#12746](https://github.com/nim-lang/Nim/issues/12746))
|
|
||||||
- Fixed "compiler as a library: findNimStdLibCompileTime fails to find system.nim"
|
|
||||||
([#12293](https://github.com/nim-lang/Nim/issues/12293))
|
|
||||||
- Fixed "5 bugs with importcpp exceptions"
|
|
||||||
([#14369](https://github.com/nim-lang/Nim/issues/14369))
|
|
||||||
- Fixed "Docs shouldn't collapse pragmas inside runnableExamples/code blocks"
|
|
||||||
([#14174](https://github.com/nim-lang/Nim/issues/14174))
|
|
||||||
- Fixed "Bad codegen/emit for hashes.hiXorLo in some contexts."
|
|
||||||
([#14394](https://github.com/nim-lang/Nim/issues/14394))
|
|
||||||
- Fixed "Boehm GC does not scan thread-local storage"
|
|
||||||
([#14364](https://github.com/nim-lang/Nim/issues/14364))
|
|
||||||
- Fixed "RVO not exception safe"
|
|
||||||
([#14126](https://github.com/nim-lang/Nim/issues/14126))
|
|
||||||
- Fixed "runnableExamples that are only compiled"
|
|
||||||
([#10731](https://github.com/nim-lang/Nim/issues/10731))
|
|
||||||
- Fixed "`foldr` raises IndexError when called on sequence"
|
|
||||||
([#14404](https://github.com/nim-lang/Nim/issues/14404))
|
|
||||||
- Fixed "moveFile does not overwrite destination file"
|
|
||||||
([#14057](https://github.com/nim-lang/Nim/issues/14057))
|
|
||||||
- Fixed "doc2 outputs in current work dir"
|
|
||||||
([#6583](https://github.com/nim-lang/Nim/issues/6583))
|
|
||||||
- Fixed "[docgen] proc doc comments silently omitted after 1st runnableExamples"
|
|
||||||
([#9227](https://github.com/nim-lang/Nim/issues/9227))
|
|
||||||
- Fixed "`nim doc --project` shows '@@/' instead of '../' for relative paths to submodules"
|
|
||||||
([#14448](https://github.com/nim-lang/Nim/issues/14448))
|
|
||||||
- Fixed "re, nre have wrong `start` semantics"
|
|
||||||
([#14284](https://github.com/nim-lang/Nim/issues/14284))
|
|
||||||
- Fixed "runnableExamples should preserve source code doc comments, strings, and (maybe) formatting"
|
|
||||||
([#8871](https://github.com/nim-lang/Nim/issues/8871))
|
|
||||||
- Fixed "`nim doc ..` fails when runnableExamples uses `$` [devel] [regression]"
|
|
||||||
([#14485](https://github.com/nim-lang/Nim/issues/14485))
|
|
||||||
- Fixed "`items` is 20%~30% slower than iteration via an index"
|
|
||||||
([#14421](https://github.com/nim-lang/Nim/issues/14421))
|
|
||||||
- Fixed "ARC: unreliable setLen "
|
|
||||||
([#14495](https://github.com/nim-lang/Nim/issues/14495))
|
|
||||||
- Fixed "lent is unsafe: after #14447 you can modify variables with "items" loop for sequences"
|
|
||||||
([#14498](https://github.com/nim-lang/Nim/issues/14498))
|
|
||||||
- Fixed "`var op = fn()` wrongly gives warning `ObservableStores` with `object of RootObj` type"
|
|
||||||
([#14514](https://github.com/nim-lang/Nim/issues/14514))
|
|
||||||
- Fixed "Compiler assertion"
|
|
||||||
([#14562](https://github.com/nim-lang/Nim/issues/14562))
|
|
||||||
- Fixed "Can't get `ord` of a value of a Range type in the JS backend "
|
|
||||||
([#14570](https://github.com/nim-lang/Nim/issues/14570))
|
|
||||||
- Fixed "js: can't take addr of param (including implicitly via `lent`)"
|
|
||||||
([#14576](https://github.com/nim-lang/Nim/issues/14576))
|
|
||||||
- Fixed "{.noinit.} ignored in for loop -> bad codegen for non-movable types"
|
|
||||||
([#14118](https://github.com/nim-lang/Nim/issues/14118))
|
|
||||||
- Fixed "generic destructor gives: `Error: unresolved generic parameter`"
|
|
||||||
([#14315](https://github.com/nim-lang/Nim/issues/14315))
|
|
||||||
- Fixed "Memory leak with arc gc"
|
|
||||||
([#14568](https://github.com/nim-lang/Nim/issues/14568))
|
|
||||||
- Fixed "escape analysis broken with `lent`"
|
|
||||||
([#14557](https://github.com/nim-lang/Nim/issues/14557))
|
|
||||||
- Fixed "`wrapWords` seems to ignore linebreaks when wrapping, leaving breaks in the wrong place"
|
|
||||||
([#14579](https://github.com/nim-lang/Nim/issues/14579))
|
|
||||||
- Fixed "`lent` gives wrong results with -d:release"
|
|
||||||
([#14578](https://github.com/nim-lang/Nim/issues/14578))
|
|
||||||
- Fixed "Nested await expressions regression: `await a(await expandValue())` doesnt compile"
|
|
||||||
([#14279](https://github.com/nim-lang/Nim/issues/14279))
|
|
||||||
- Fixed "windows CI docs fails with strange errors"
|
|
||||||
([#14545](https://github.com/nim-lang/Nim/issues/14545))
|
|
||||||
- Fixed "[CI] tests/async/tioselectors.nim flaky test for freebsd + OSX CI"
|
|
||||||
([#13166](https://github.com/nim-lang/Nim/issues/13166))
|
|
||||||
- Fixed "seq.setLen sometimes doesn't zero memory"
|
|
||||||
([#14655](https://github.com/nim-lang/Nim/issues/14655))
|
|
||||||
- Fixed "`nim dump` is roughly 100x slower in 1.3 versus 1.2"
|
|
||||||
([#14179](https://github.com/nim-lang/Nim/issues/14179))
|
|
||||||
- Fixed "Regression: devel docgen cannot generate document for method"
|
|
||||||
([#14691](https://github.com/nim-lang/Nim/issues/14691))
|
|
||||||
- Fixed "recently flaky tests/async/t7758.nim"
|
|
||||||
([#14685](https://github.com/nim-lang/Nim/issues/14685))
|
|
||||||
- Fixed "Bind no longer working in generic procs."
|
|
||||||
([#11811](https://github.com/nim-lang/Nim/issues/11811))
|
|
||||||
- Fixed "The pegs module doesn't work with generics!"
|
|
||||||
([#14718](https://github.com/nim-lang/Nim/issues/14718))
|
|
||||||
- Fixed "Defer is not properly working for asynchronous procedures."
|
|
||||||
([#13899](https://github.com/nim-lang/Nim/issues/13899))
|
|
||||||
- Fixed "Add an ARC test with threads in a loop"
|
|
||||||
([#14690](https://github.com/nim-lang/Nim/issues/14690))
|
|
||||||
- Fixed "[goto exceptions] {.noReturn.} pragma is not detected in a case expression"
|
|
||||||
([#14458](https://github.com/nim-lang/Nim/issues/14458))
|
|
||||||
- Fixed "[exceptions:goto] C compiler error with dynlib pragma calling a proc"
|
|
||||||
([#14240](https://github.com/nim-lang/Nim/issues/14240))
|
|
||||||
- Fixed "Cannot borrow var float64 in infix assignment"
|
|
||||||
([#14440](https://github.com/nim-lang/Nim/issues/14440))
|
|
||||||
- Fixed "lib/pure/memfiles.nim: compilation error with --taintMode:on"
|
|
||||||
([#14760](https://github.com/nim-lang/Nim/issues/14760))
|
|
||||||
- Fixed "newWideCString allocates a multiple of the memory needed"
|
|
||||||
([#14750](https://github.com/nim-lang/Nim/issues/14750))
|
|
||||||
- Fixed "Nim source archive install: 'install.sh' fails with error: cp: cannot stat 'bin/nim-gdb': No such file or directory"
|
|
||||||
([#14748](https://github.com/nim-lang/Nim/issues/14748))
|
|
||||||
- Fixed "`nim cpp -r tests/exception/t9657` hangs"
|
|
||||||
([#10343](https://github.com/nim-lang/Nim/issues/10343))
|
|
||||||
- Fixed "Detect tool fails on FreeBSD"
|
|
||||||
([#14715](https://github.com/nim-lang/Nim/issues/14715))
|
|
||||||
- Fixed "compiler crash: `findUnresolvedStatic` "
|
|
||||||
([#14802](https://github.com/nim-lang/Nim/issues/14802))
|
|
||||||
- Fixed "seq namespace (?) regression"
|
|
||||||
([#4796](https://github.com/nim-lang/Nim/issues/4796))
|
|
||||||
- Fixed "Possible out of bounds string access in std/colors parseColor and isColor"
|
|
||||||
([#14839](https://github.com/nim-lang/Nim/issues/14839))
|
|
||||||
- Fixed "compile error on latest devel with orc and ssl"
|
|
||||||
([#14647](https://github.com/nim-lang/Nim/issues/14647))
|
|
||||||
- Fixed "[minor] `$` wrong for type tuple"
|
|
||||||
([#13432](https://github.com/nim-lang/Nim/issues/13432))
|
|
||||||
- Fixed "Documentation missing on devel asyncftpclient"
|
|
||||||
([#14846](https://github.com/nim-lang/Nim/issues/14846))
|
|
||||||
- Fixed "nimpretty is confused with a trailing comma in enum definition"
|
|
||||||
([#14401](https://github.com/nim-lang/Nim/issues/14401))
|
|
||||||
- Fixed "Output arguments get ignored when compiling with --app:staticlib"
|
|
||||||
([#12745](https://github.com/nim-lang/Nim/issues/12745))
|
|
||||||
- Fixed "[ARC] destructive move destroys the object too early"
|
|
||||||
([#14396](https://github.com/nim-lang/Nim/issues/14396))
|
|
||||||
- Fixed "highlite.getNextToken() crashes if the buffer string is "echo "\"""
|
|
||||||
([#14830](https://github.com/nim-lang/Nim/issues/14830))
|
|
||||||
- Fixed "Memory corruption with --gc:arc with a seq of objects with an empty body."
|
|
||||||
([#14472](https://github.com/nim-lang/Nim/issues/14472))
|
|
||||||
- Fixed "Stropped identifiers don't work as field names in tuple literals"
|
|
||||||
([#14911](https://github.com/nim-lang/Nim/issues/14911))
|
|
||||||
- Fixed "Please revert my commit"
|
|
||||||
([#14930](https://github.com/nim-lang/Nim/issues/14930))
|
|
||||||
- Fixed "[ARC] C compiler error with inline iterators and imports"
|
|
||||||
([#14864](https://github.com/nim-lang/Nim/issues/14864))
|
|
||||||
- Fixed "AsyncHttpClient segfaults with gc:orc, possibly memory corruption"
|
|
||||||
([#14402](https://github.com/nim-lang/Nim/issues/14402))
|
|
||||||
- Fixed "[ARC] Template with a block evaluating to a GC'd value results in a compiler crash"
|
|
||||||
([#14899](https://github.com/nim-lang/Nim/issues/14899))
|
|
||||||
- Fixed "[ARC] Weird issue with if expressions and templates"
|
|
||||||
([#14900](https://github.com/nim-lang/Nim/issues/14900))
|
|
||||||
- Fixed "xmlparser does not compile on devel"
|
|
||||||
([#14805](https://github.com/nim-lang/Nim/issues/14805))
|
|
||||||
- Fixed "returning lent T from a var T param gives codegen errors or SIGSEGV"
|
|
||||||
([#14878](https://github.com/nim-lang/Nim/issues/14878))
|
|
||||||
- Fixed "[ARC] Weird issue with if expressions and templates"
|
|
||||||
([#14900](https://github.com/nim-lang/Nim/issues/14900))
|
|
||||||
- Fixed "threads:on + gc:orc + unittest = C compiler errors"
|
|
||||||
([#14865](https://github.com/nim-lang/Nim/issues/14865))
|
|
||||||
- Fixed "mitems, mpairs doesn't work at compile time anymore"
|
|
||||||
([#12129](https://github.com/nim-lang/Nim/issues/12129))
|
|
||||||
- Fixed "strange result from executing code in const expression"
|
|
||||||
([#10465](https://github.com/nim-lang/Nim/issues/10465))
|
|
||||||
- Fixed "Same warning printed 3 times"
|
|
||||||
([#11009](https://github.com/nim-lang/Nim/issues/11009))
|
|
||||||
- Fixed "type alias for generic typeclass doesn't work"
|
|
||||||
([#4668](https://github.com/nim-lang/Nim/issues/4668))
|
|
||||||
- Fixed "exceptions:goto Bug devel codegen lvalue NIM_FALSE=NIM_FALSE"
|
|
||||||
([#14925](https://github.com/nim-lang/Nim/issues/14925))
|
|
||||||
- Fixed "the --useVersion:1.0 no longer works in devel"
|
|
||||||
([#14912](https://github.com/nim-lang/Nim/issues/14912))
|
|
||||||
- Fixed "template declaration of iterator doesn't compile"
|
|
||||||
([#4722](https://github.com/nim-lang/Nim/issues/4722))
|
|
||||||
- Fixed "Compiler crash on type inheritance with static generic parameter and equality check"
|
|
||||||
([#12571](https://github.com/nim-lang/Nim/issues/12571))
|
|
||||||
- Fixed "Nim crashes while handling a cast in async circumstances."
|
|
||||||
([#13815](https://github.com/nim-lang/Nim/issues/13815))
|
|
||||||
- Fixed "[ARC] Internal compiler error when calling an iterator from an inline proc "
|
|
||||||
([#14383](https://github.com/nim-lang/Nim/issues/14383))
|
|
||||||
- Fixed ""Cannot instantiate" error when template uses generic type"
|
|
||||||
([#5926](https://github.com/nim-lang/Nim/issues/5926))
|
|
||||||
- Fixed "Different raises behaviour for newTerminal between Linux and Windows"
|
|
||||||
([#12759](https://github.com/nim-lang/Nim/issues/12759))
|
|
||||||
- Fixed "Expand on a type (that defines a proc type) in error message "
|
|
||||||
([#6608](https://github.com/nim-lang/Nim/issues/6608))
|
|
||||||
- Fixed "unittest require quits program with an exit code of 0"
|
|
||||||
([#14475](https://github.com/nim-lang/Nim/issues/14475))
|
|
||||||
- Fixed "Range type: Generics vs concrete type, semcheck difference."
|
|
||||||
([#8426](https://github.com/nim-lang/Nim/issues/8426))
|
|
||||||
- Fixed "[Macro] Type mismatch when parameter name is the same as a field"
|
|
||||||
([#13253](https://github.com/nim-lang/Nim/issues/13253))
|
|
||||||
- Fixed "Generic instantiation failure when converting a sequence of circular generic types to strings"
|
|
||||||
([#10396](https://github.com/nim-lang/Nim/issues/10396))
|
|
||||||
- Fixed "initOptParser ignores argument after value option with empty value."
|
|
||||||
([#13086](https://github.com/nim-lang/Nim/issues/13086))
|
|
||||||
- Fixed "[ARC] proc with both explicit and implicit return results in a C compiler error"
|
|
||||||
([#14985](https://github.com/nim-lang/Nim/issues/14985))
|
|
||||||
- Fixed "Alias type forgets implicit generic params depending on order"
|
|
||||||
([#14990](https://github.com/nim-lang/Nim/issues/14990))
|
|
||||||
- Fixed "[ARC] sequtils.insert has different behaviour between ARC/refc"
|
|
||||||
([#14994](https://github.com/nim-lang/Nim/issues/14994))
|
|
||||||
- Fixed "The documentation for "hot code reloading" references a non-existent npm package"
|
|
||||||
([#13621](https://github.com/nim-lang/Nim/issues/13621))
|
|
||||||
- Fixed "existsDir deprecated but breaking `dir` undeclared"
|
|
||||||
([#15006](https://github.com/nim-lang/Nim/issues/15006))
|
|
||||||
- Fixed "uri.decodeUrl crashes on incorrectly formatted input"
|
|
||||||
([#14082](https://github.com/nim-lang/Nim/issues/14082))
|
|
||||||
- Fixed "testament incorrectly reports time for tests, leading to wrong conclusions"
|
|
||||||
([#14822](https://github.com/nim-lang/Nim/issues/14822))
|
|
||||||
- Fixed "Calling peekChar with Stream returned from osproc.outputStream generate runtime error"
|
|
||||||
([#14906](https://github.com/nim-lang/Nim/issues/14906))
|
|
||||||
- Fixed "localPassC pragma should come *after* other flags"
|
|
||||||
([#14194](https://github.com/nim-lang/Nim/issues/14194))
|
|
||||||
- Fixed ""Could not load" dynamic library at runtime because of hidden dependency"
|
|
||||||
([#2408](https://github.com/nim-lang/Nim/issues/2408))
|
|
||||||
- Fixed "--gc:arc generate invalid code for {.global.} (`«nimErr_» in NIM_UNLIKELY`)"
|
|
||||||
([#14480](https://github.com/nim-lang/Nim/issues/14480))
|
|
||||||
- Fixed "Using `^` from stdlib/math along with converters gives a match for types that aren't SomeNumber"
|
|
||||||
([#15033](https://github.com/nim-lang/Nim/issues/15033))
|
|
||||||
- Fixed "[ARC] Weird exception behaviour from doAssertRaises"
|
|
||||||
([#15026](https://github.com/nim-lang/Nim/issues/15026))
|
|
||||||
- Fixed "[ARC] Compiler crash declaring a finalizer proc directly in 'new'"
|
|
||||||
([#15044](https://github.com/nim-lang/Nim/issues/15044))
|
|
||||||
- Fixed "[ARC] C compiler error when creating a var of a const seq"
|
|
||||||
([#15036](https://github.com/nim-lang/Nim/issues/15036))
|
|
||||||
- Fixed "code with named arguments in proc of winim/com can not been compiled"
|
|
||||||
([#15056](https://github.com/nim-lang/Nim/issues/15056))
|
|
||||||
- Fixed "javascript backend produces javascript code with syntax error in object syntax"
|
|
||||||
([#14534](https://github.com/nim-lang/Nim/issues/14534))
|
|
||||||
- Fixed "--gc:arc should be ignored in JS mode."
|
|
||||||
([#14684](https://github.com/nim-lang/Nim/issues/14684))
|
|
||||||
- Fixed "arc: C compilation error with imported global code using a closure iterator"
|
|
||||||
([#12990](https://github.com/nim-lang/Nim/issues/12990))
|
|
||||||
- Fixed "[ARC] Crash when modifying a string with mitems iterator"
|
|
||||||
([#15052](https://github.com/nim-lang/Nim/issues/15052))
|
|
||||||
- Fixed "[ARC] SIGSEGV when calling a closure as a tuple field in a seq"
|
|
||||||
([#15038](https://github.com/nim-lang/Nim/issues/15038))
|
|
||||||
- Fixed "pass varargs[seq[T]] to iterator give empty seq "
|
|
||||||
([#12576](https://github.com/nim-lang/Nim/issues/12576))
|
|
||||||
- Fixed "Compiler crashes when using string as object variant selector with else branch"
|
|
||||||
([#14189](https://github.com/nim-lang/Nim/issues/14189))
|
|
||||||
- Fixed "JS compiler error related to implicit return and return var type"
|
|
||||||
([#11354](https://github.com/nim-lang/Nim/issues/11354))
|
|
||||||
- Fixed "`nkRecWhen` causes internalAssert in semConstructFields"
|
|
||||||
([#14698](https://github.com/nim-lang/Nim/issues/14698))
|
|
||||||
- Fixed "Memory leaks with async (closure iterators?) under ORC"
|
|
||||||
([#15076](https://github.com/nim-lang/Nim/issues/15076))
|
|
||||||
- Fixed "strutil.insertSep() fails on negative numbers"
|
|
||||||
([#11352](https://github.com/nim-lang/Nim/issues/11352))
|
|
||||||
- Fixed "Constructing a uint64 range on a 32-bit machine leads to incorrect codegen"
|
|
||||||
([#14616](https://github.com/nim-lang/Nim/issues/14616))
|
|
||||||
- Fixed "heapqueue pushpop() proc doesn't compile"
|
|
||||||
([#14139](https://github.com/nim-lang/Nim/issues/14139))
|
|
||||||
- Fixed "[ARC] SIGSEGV when trying to swap in a literal/const string"
|
|
||||||
([#15112](https://github.com/nim-lang/Nim/issues/15112))
|
|
||||||
- Fixed "Defer and --gc:arc"
|
|
||||||
([#15071](https://github.com/nim-lang/Nim/issues/15071))
|
|
||||||
- Fixed "internal error: compiler/semobjconstr.nim(324, 20) example"
|
|
||||||
([#15111](https://github.com/nim-lang/Nim/issues/15111))
|
|
||||||
- Fixed "[ARC] Sequence "disappears" with a table inside of a table with an object variant"
|
|
||||||
([#15122](https://github.com/nim-lang/Nim/issues/15122))
|
|
||||||
- Fixed "[ARC] SIGSEGV with tuple assignment caused by cursor inference"
|
|
||||||
([#15130](https://github.com/nim-lang/Nim/issues/15130))
|
|
||||||
- Fixed "Issue with --gc:arc at compile time"
|
|
||||||
([#15129](https://github.com/nim-lang/Nim/issues/15129))
|
|
||||||
- Fixed "Writing an empty string to an AsyncFile raises an IndexDefect"
|
|
||||||
([#15148](https://github.com/nim-lang/Nim/issues/15148))
|
|
||||||
- Fixed "Compiler is confused about call convention of function with nested closure"
|
|
||||||
([#5688](https://github.com/nim-lang/Nim/issues/5688))
|
|
||||||
- Fixed "Nil check on each field fails in generic function"
|
|
||||||
([#15101](https://github.com/nim-lang/Nim/issues/15101))
|
|
||||||
- Fixed "{.nimcall.} convention won't avoid the creation of closures"
|
|
||||||
([#8473](https://github.com/nim-lang/Nim/issues/8473))
|
|
||||||
- Fixed "smtp.nim(161, 40) Error: type mismatch: got <typeof(nil)> but expected 'SslContext = void'"
|
|
||||||
([#15177](https://github.com/nim-lang/Nim/issues/15177))
|
|
||||||
- Fixed "[strscans] scanf doesn't match a single character with $+ if it's the end of the string"
|
|
||||||
([#15064](https://github.com/nim-lang/Nim/issues/15064))
|
|
||||||
- Fixed "Crash and incorrect return values when using readPasswordFromStdin on Windows."
|
|
||||||
([#15207](https://github.com/nim-lang/Nim/issues/15207))
|
|
||||||
- Fixed "Possible capture error with fieldPairs and genericParams"
|
|
||||||
([#15221](https://github.com/nim-lang/Nim/issues/15221))
|
|
||||||
- Fixed "The StmtList processing of template parameters can lead to unexpected errors"
|
|
||||||
([#5691](https://github.com/nim-lang/Nim/issues/5691))
|
|
||||||
- Fixed "[ARC] C compiler error when passing a var openArray to a sink openArray"
|
|
||||||
([#15035](https://github.com/nim-lang/Nim/issues/15035))
|
|
||||||
- Fixed "Inconsistent unsigned -> signed RangeDefect usage across integer sizes"
|
|
||||||
([#15210](https://github.com/nim-lang/Nim/issues/15210))
|
|
||||||
- Fixed "toHex results in RangeDefect exception when used with large uint64"
|
|
||||||
([#15257](https://github.com/nim-lang/Nim/issues/15257))
|
|
||||||
- Fixed "Arc sink arg crash"
|
|
||||||
([#15238](https://github.com/nim-lang/Nim/issues/15238))
|
|
||||||
- Fixed "SQL escape in db_mysql is not enough"
|
|
||||||
([#15219](https://github.com/nim-lang/Nim/issues/15219))
|
|
||||||
- Fixed "Mixing 'return' with expressions is allowed in 1.2"
|
|
||||||
([#15280](https://github.com/nim-lang/Nim/issues/15280))
|
|
||||||
- Fixed "os.getFileInfo() causes ICE with --gc:arc on Windows"
|
|
||||||
([#15286](https://github.com/nim-lang/Nim/issues/15286))
|
|
||||||
- Fixed "[ARC] Sequence "disappears" with a table inside of a table with an object variant"
|
|
||||||
([#15122](https://github.com/nim-lang/Nim/issues/15122))
|
|
||||||
- Fixed "Documentation regression jsre module missing"
|
|
||||||
([#15183](https://github.com/nim-lang/Nim/issues/15183))
|
|
||||||
- Fixed "CountTable.smallest/largest() on empty table either asserts or gives bogus answer"
|
|
||||||
([#15021](https://github.com/nim-lang/Nim/issues/15021))
|
|
||||||
- Fixed "[Regression] Parser regression"
|
|
||||||
([#15305](https://github.com/nim-lang/Nim/issues/15305))
|
|
||||||
- Fixed "[ARC] SIGSEGV with tuple unpacking caused by cursor inference"
|
|
||||||
([#15147](https://github.com/nim-lang/Nim/issues/15147))
|
|
||||||
- Fixed "LwIP/FreeRTOS compile error - missing SIGPIPE and more "
|
|
||||||
([#15302](https://github.com/nim-lang/Nim/issues/15302))
|
|
||||||
- Fixed "Memory leaks with async (closure iterators?) under ORC"
|
|
||||||
([#15076](https://github.com/nim-lang/Nim/issues/15076))
|
|
||||||
- Fixed "Bug compiling with --gc:arg or --gc:orc"
|
|
||||||
([#15325](https://github.com/nim-lang/Nim/issues/15325))
|
|
||||||
- Fixed "memory corruption in tmarshall.nim"
|
|
||||||
([#9754](https://github.com/nim-lang/Nim/issues/9754))
|
|
||||||
- Fixed "typed macros break generic proc definitions"
|
|
||||||
([#15326](https://github.com/nim-lang/Nim/issues/15326))
|
|
||||||
- Fixed "nim doc2 ignores --docSeeSrcUrl parameter"
|
|
||||||
([#6071](https://github.com/nim-lang/Nim/issues/6071))
|
|
||||||
- Fixed "The decodeData Iterator from cgi module crash"
|
|
||||||
([#15369](https://github.com/nim-lang/Nim/issues/15369))
|
|
||||||
- Fixed "|| iterator generates invalid code when compiling with --debugger:native"
|
|
||||||
([#9710](https://github.com/nim-lang/Nim/issues/9710))
|
|
||||||
- Fixed "Wrong number of variables"
|
|
||||||
([#15360](https://github.com/nim-lang/Nim/issues/15360))
|
|
||||||
- Fixed "Coercions with distinct types should traverse pointer modifiers transparently."
|
|
||||||
([#7165](https://github.com/nim-lang/Nim/issues/7165))
|
|
||||||
- Fixed "Error with distinct generic TableRef"
|
|
||||||
([#6060](https://github.com/nim-lang/Nim/issues/6060))
|
|
||||||
- Fixed "Support images in nim docgen"
|
|
||||||
([#6430](https://github.com/nim-lang/Nim/issues/6430))
|
|
||||||
- Fixed "Regression. Double sem check for procs."
|
|
||||||
([#15389](https://github.com/nim-lang/Nim/issues/15389))
|
|
||||||
- Fixed "uri.nim url with literal ipv6 address is printed wrong, and cannot parsed again"
|
|
||||||
([#15333](https://github.com/nim-lang/Nim/issues/15333))
|
|
||||||
- Fixed "[ARC] Object variant gets corrupted with cursor inference"
|
|
||||||
([#15361](https://github.com/nim-lang/Nim/issues/15361))
|
|
||||||
- Fixed "`nim doc ..` compiler crash (regression 0.19.6 => 1.0)"
|
|
||||||
([#14474](https://github.com/nim-lang/Nim/issues/14474))
|
|
||||||
- Fixed "cannot borrow result; what it borrows from is potentially mutated"
|
|
||||||
([#15403](https://github.com/nim-lang/Nim/issues/15403))
|
|
||||||
- Fixed "memory corruption for seq.add(seq) with gc:arc and d:useMalloc "
|
|
||||||
([#14983](https://github.com/nim-lang/Nim/issues/14983))
|
|
||||||
- Fixed "DocGen HTML output appears improperly when encountering text immediately after/before inline monospace; in some cases won't compile"
|
|
||||||
([#11537](https://github.com/nim-lang/Nim/issues/11537))
|
|
||||||
- Fixed "Deepcopy in arc crashes"
|
|
||||||
([#15405](https://github.com/nim-lang/Nim/issues/15405))
|
|
||||||
- Fixed "pop pragma takes invalid input"
|
|
||||||
([#15430](https://github.com/nim-lang/Nim/issues/15430))
|
|
||||||
- Fixed "tests/stdlib/tgetprotobyname fails on NetBSD"
|
|
||||||
([#15452](https://github.com/nim-lang/Nim/issues/15452))
|
|
||||||
- Fixed "defer doesnt work with block, break and await"
|
|
||||||
([#15243](https://github.com/nim-lang/Nim/issues/15243))
|
|
||||||
- Fixed "tests/stdlib/tssl failing on NetBSD"
|
|
||||||
([#15493](https://github.com/nim-lang/Nim/issues/15493))
|
|
||||||
- Fixed "strictFuncs doesn't seem to catch simple ref mutation"
|
|
||||||
([#15508](https://github.com/nim-lang/Nim/issues/15508))
|
|
||||||
- Fixed "Sizeof of case object is incorrect. Showstopper"
|
|
||||||
([#15516](https://github.com/nim-lang/Nim/issues/15516))
|
|
||||||
- Fixed "[ARC] Internal error when trying to use a parallel for loop"
|
|
||||||
([#15512](https://github.com/nim-lang/Nim/issues/15512))
|
|
||||||
- Fixed "[ARC] Type-bound assign op is not being generated"
|
|
||||||
([#15510](https://github.com/nim-lang/Nim/issues/15510))
|
|
||||||
- Fixed "[ARC] Crash when adding openArray proc argument to a local seq"
|
|
||||||
([#15511](https://github.com/nim-lang/Nim/issues/15511))
|
|
||||||
- Fixed "VM: const case object gets some fields zeroed out at runtime"
|
|
||||||
([#13081](https://github.com/nim-lang/Nim/issues/13081))
|
|
||||||
- Fixed "regression(1.2.6 => devel): VM: const case object field access gives: 'sons' is not accessible"
|
|
||||||
([#15532](https://github.com/nim-lang/Nim/issues/15532))
|
|
||||||
- Fixed "Csources: huge size increase (x2.3) in 0.20"
|
|
||||||
([#12027](https://github.com/nim-lang/Nim/issues/12027))
|
|
||||||
- Fixed "Out of date error message for GC options"
|
|
||||||
([#15547](https://github.com/nim-lang/Nim/issues/15547))
|
|
||||||
- Fixed "dbQuote additional escape regression"
|
|
||||||
([#15560](https://github.com/nim-lang/Nim/issues/15560))
|
|
||||||
|
|
@ -1,957 +0,0 @@
|
||||||
# v1.6.0 - 2021-10-14
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Major new features
|
|
||||||
|
|
||||||
With so many new features, pinpointing the most salient ones is a subjective exercise,
|
|
||||||
but here are a select few:
|
|
||||||
|
|
||||||
|
|
||||||
## `iterable[T]`
|
|
||||||
|
|
||||||
The `iterable[T]` type class was added to match called iterators,
|
|
||||||
which solves a number of long-standing issues related to iterators.
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
iterator iota(n: int): int =
|
|
||||||
for i in 0..<n: yield i
|
|
||||||
|
|
||||||
# previously, you'd need `untyped`, which caused other problems such as lack
|
|
||||||
# of type inference, overloading issues, and MCS.
|
|
||||||
template sumOld(a: untyped): untyped = # no type inference possible
|
|
||||||
var result: typeof(block:(for ai in a: ai))
|
|
||||||
for ai in a: result += ai
|
|
||||||
result
|
|
||||||
|
|
||||||
assert sumOld(iota(3)) == 0 + 1 + 2
|
|
||||||
|
|
||||||
# now, you can write:
|
|
||||||
template sum[T](a: iterable[T]): T =
|
|
||||||
# `template sum(a: iterable): auto =` would also be possible
|
|
||||||
var result: T
|
|
||||||
for ai in a: result += ai
|
|
||||||
result
|
|
||||||
|
|
||||||
assert sum(iota(3)) == 0 + 1 + 2 # or `iota(3).sum`
|
|
||||||
```
|
|
||||||
|
|
||||||
In particular iterable arguments can now be used with the method call syntax. For example:
|
|
||||||
```nim
|
|
||||||
import std/[sequtils, os]
|
|
||||||
echo walkFiles("*").toSeq # now works
|
|
||||||
```
|
|
||||||
See PR [#17196](https://github.com/nim-lang/Nim/pull/17196) for additional details.
|
|
||||||
|
|
||||||
|
|
||||||
## Strict effects
|
|
||||||
|
|
||||||
The effect system was refined and there is a new `.effectsOf` annotation that does
|
|
||||||
explicitly what was previously done implicitly. See the
|
|
||||||
[manual](https://nim-lang.github.io/Nim/manual.html#effect-system-effectsof-annotation)
|
|
||||||
for more details.
|
|
||||||
To write code that is portable with older Nim versions, use this idiom:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
when defined(nimHasEffectsOf):
|
|
||||||
{.experimental: "strictEffects".}
|
|
||||||
else:
|
|
||||||
{.pragma: effectsOf.}
|
|
||||||
|
|
||||||
proc mysort(s: seq; cmp: proc(a, b: T): int) {.effectsOf: cmp.}
|
|
||||||
```
|
|
||||||
|
|
||||||
To enable the new effect system, compile with `--experimental:strictEffects`.
|
|
||||||
See also [#18777](https://github.com/nim-lang/Nim/pull/18777) and RFC
|
|
||||||
[#408](https://github.com/nim-lang/RFCs/issues/408).
|
|
||||||
|
|
||||||
|
|
||||||
## Private imports and private field access
|
|
||||||
|
|
||||||
A new import syntax `import foo {.all.}` now allows importing all symbols
|
|
||||||
(public or private) from `foo`.
|
|
||||||
This can be useful for testing purposes or for more flexibility in project organization.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```nim
|
|
||||||
from system {.all.} as system2 import nil
|
|
||||||
echo system2.ThisIsSystem # ThisIsSystem is private in `system`
|
|
||||||
import os {.all.} # weirdTarget is private in `os`
|
|
||||||
echo weirdTarget # or `os.weirdTarget`
|
|
||||||
```
|
|
||||||
|
|
||||||
Added a new module `std/importutils`, and an API `privateAccess`, which allows access
|
|
||||||
to private fields for an object type in the current scope.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```nim
|
|
||||||
import times
|
|
||||||
from std/importutils import privateAccess
|
|
||||||
block:
|
|
||||||
let t = now()
|
|
||||||
# echo t.monthdayZero # Error: undeclared field: 'monthdayZero' for type times.DateTime
|
|
||||||
privateAccess(typeof(t)) # enables private access in this scope
|
|
||||||
echo t.monthdayZero # ok
|
|
||||||
```
|
|
||||||
|
|
||||||
See PR [#17706](https://github.com/nim-lang/Nim/pull/17706) for additional details.
|
|
||||||
|
|
||||||
|
|
||||||
## `nim --eval:cmd`
|
|
||||||
|
|
||||||
Added `nim --eval:cmd` to evaluate a command directly, e.g.: `nim --eval:"echo 1"`.
|
|
||||||
It defaults to `e` (nimscript) but can also work with other commands, e.g.:
|
|
||||||
```bash
|
|
||||||
find . | nim r --eval:'import strutils; for a in stdin.lines: echo a.toUpper'
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# use as a calculator:
|
|
||||||
nim --eval:'echo 3.1 / (1.2+7)'
|
|
||||||
# explore a module's APIs, including private symbols:
|
|
||||||
nim --eval:'import os {.all.}; echo weirdTarget'
|
|
||||||
# use a custom backend:
|
|
||||||
nim r -b:js --eval:"import std/jsbigints; echo 2'big ** 64'big"
|
|
||||||
```
|
|
||||||
|
|
||||||
See PR [#15687](https://github.com/nim-lang/Nim/pull/15687) for more details.
|
|
||||||
|
|
||||||
|
|
||||||
## Round-trip float to string
|
|
||||||
|
|
||||||
`system.addFloat` and `system.$` now can produce string representations of
|
|
||||||
floating point numbers that are minimal in size and possess round-trip and correct
|
|
||||||
rounding guarantees (via the
|
|
||||||
[Dragonbox](https://raw.githubusercontent.com/jk-jeon/dragonbox/master/other_files/Dragonbox.pdf) algorithm).
|
|
||||||
This currently has to be enabled via `-d:nimPreviewFloatRoundtrip`.
|
|
||||||
It is expected that this behavior becomes the new default in upcoming versions,
|
|
||||||
as with other `nimPreviewX` define flags.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```nim
|
|
||||||
from math import round
|
|
||||||
let a = round(9.779999999999999, 2)
|
|
||||||
assert a == 9.78
|
|
||||||
echo a # with `-d:nimPreviewFloatRoundtrip`: 9.78, like in python3 (instead of 9.779999999999999)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## New `std/jsbigints` module
|
|
||||||
|
|
||||||
Provides arbitrary precision integers for the JS target. See PR
|
|
||||||
[#16409](https://github.com/nim-lang/Nim/pull/16409).
|
|
||||||
Example:
|
|
||||||
```nim
|
|
||||||
import std/jsbigints
|
|
||||||
assert 2'big ** 65'big == 36893488147419103232'big
|
|
||||||
echo 0xdeadbeef'big shl 4'big # 59774856944n
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## New `std/sysrand` module
|
|
||||||
Cryptographically secure pseudorandom number generator,
|
|
||||||
allows generating random numbers from a secure source provided by the operating system.
|
|
||||||
Example:
|
|
||||||
```nim
|
|
||||||
import std/sysrand
|
|
||||||
assert urandom(1234) != urandom(1234) # unlikely to fail in practice
|
|
||||||
```
|
|
||||||
See PR [#16459](https://github.com/nim-lang/Nim/pull/16459).
|
|
||||||
|
|
||||||
|
|
||||||
## New module: `std/tempfiles`
|
|
||||||
|
|
||||||
Allows creating temporary files and directories, see PR
|
|
||||||
[#17361](https://github.com/nim-lang/Nim/pull/17361) and followups.
|
|
||||||
```nim
|
|
||||||
import std/tempfiles
|
|
||||||
let tmpPath = genTempPath("prefix", "suffix.log", "/tmp/")
|
|
||||||
# tmpPath looks like: /tmp/prefixpmW1P2KLsuffix.log
|
|
||||||
|
|
||||||
let dir = createTempDir("tmpprefix_", "_end")
|
|
||||||
# created dir looks like: getTempDir() / "tmpprefix_YEl9VuVj_end"
|
|
||||||
|
|
||||||
let (cfile, path) = createTempFile("tmpprefix_", "_end.tmp")
|
|
||||||
# path looks like: getTempDir() / "tmpprefix_FDCIRZA0_end.tmp"
|
|
||||||
cfile.write "foo"
|
|
||||||
cfile.setFilePos 0
|
|
||||||
assert readAll(cfile) == "foo"
|
|
||||||
close cfile
|
|
||||||
assert readFile(path) == "foo"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## User-defined literals
|
|
||||||
|
|
||||||
Custom numeric literals (e.g. `-128'bignum`) are now supported.
|
|
||||||
Additionally, the unary minus in `-1` is now part of the integer literal, i.e.
|
|
||||||
it is now parsed as a single token.
|
|
||||||
This implies that edge cases like `-128'i8` finally work correctly.
|
|
||||||
Example:
|
|
||||||
```nim
|
|
||||||
func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".}
|
|
||||||
assert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Dot-like operators
|
|
||||||
|
|
||||||
With `-d:nimPreviewDotLikeOps`, dot-like operators (operators starting with `.`,
|
|
||||||
but not with `..`) now have the same precedence as `.`, so that `a.?b.c` is now
|
|
||||||
parsed as `(a.?b).c` instead of `a.?(b.c)`.
|
|
||||||
A warning is generated when a dot-like operator is used without `-d:nimPreviewDotLikeOps`.
|
|
||||||
|
|
||||||
An important use case is to enable dynamic fields without affecting the
|
|
||||||
built-in `.` operator, e.g. for `std/jsffi`, `std/json`, `pkg/nimpy`. Example:
|
|
||||||
```nim
|
|
||||||
import std/json
|
|
||||||
template `.?`(a: JsonNode, b: untyped{ident}): JsonNode =
|
|
||||||
a[astToStr(b)]
|
|
||||||
let j = %*{"a1": {"a2": 10}}
|
|
||||||
assert j.?a1.?a2.getInt == 10
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Block arguments now support optional parameters
|
|
||||||
|
|
||||||
This solves a major pain point for routines accepting block parameters,
|
|
||||||
see PR [#18631](https://github.com/nim-lang/Nim/pull/18631) for details:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
template fn(a = 1, b = 2, body) = discard
|
|
||||||
fn(1, 2): # already works
|
|
||||||
bar
|
|
||||||
fn(a = 1): # now works
|
|
||||||
bar
|
|
||||||
```
|
|
||||||
|
|
||||||
Likewise with multiple block arguments via `do`:
|
|
||||||
```nim
|
|
||||||
template fn(a = 1, b = 2, body1, body2) = discard
|
|
||||||
fn(a = 1): # now works
|
|
||||||
bar1
|
|
||||||
do:
|
|
||||||
bar2
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
# Other new features
|
|
||||||
|
|
||||||
## New and deprecated modules
|
|
||||||
|
|
||||||
The following modules were added (they are discussed in the rest of the text):
|
|
||||||
- `std/enumutils`
|
|
||||||
- `std/genasts`
|
|
||||||
- `std/importutils`
|
|
||||||
- `std/jsbigints`
|
|
||||||
- `std/jsfetch`
|
|
||||||
- `std/jsformdata`
|
|
||||||
- `std/jsheaders`
|
|
||||||
- `std/packedsets`
|
|
||||||
- `std/setutils`
|
|
||||||
- `std/socketstreams`
|
|
||||||
- `std/strbasics`
|
|
||||||
- `std/sysrand`
|
|
||||||
- `std/tasks`
|
|
||||||
- `std/tempfiles`
|
|
||||||
- `std/vmutils`
|
|
||||||
|
|
||||||
- Deprecated `std/mersenne`.
|
|
||||||
- Removed deprecated `std/iup` module from stdlib; it has already moved to
|
|
||||||
[nimble](https://github.com/nim-lang/iup).
|
|
||||||
|
|
||||||
|
|
||||||
## New `std/jsfetch` module
|
|
||||||
|
|
||||||
Provides a wrapper for JS Fetch API.
|
|
||||||
Example:
|
|
||||||
```nim
|
|
||||||
# requires -d:nimExperimentalAsyncjsThen
|
|
||||||
import std/[jsfetch, asyncjs, jsconsole, jsffi, sugar]
|
|
||||||
proc fn {.async.} =
|
|
||||||
await fetch("https://api.github.com/users/torvalds".cstring)
|
|
||||||
.then((response: Response) => response.json())
|
|
||||||
.then((json: JsObject) => console.log(json))
|
|
||||||
.catch((err: Error) => console.log("Request Failed", err))
|
|
||||||
discard fn()
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## New `std/tasks` module
|
|
||||||
|
|
||||||
Provides basic primitives for creating parallel programs.
|
|
||||||
Example:
|
|
||||||
```nim
|
|
||||||
import std/tasks
|
|
||||||
var num = 0
|
|
||||||
proc hello(a: int) = num+=a
|
|
||||||
|
|
||||||
let b = toTask hello(13) # arguments must be isolated, see `std/isolation`
|
|
||||||
b.invoke()
|
|
||||||
assert num == 13
|
|
||||||
b.invoke() # can be called again
|
|
||||||
assert num == 26
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## New module: `std/genasts`
|
|
||||||
|
|
||||||
Provides an API `genAst` that avoids the problems inherent with `quote do` and can
|
|
||||||
be used as a replacement.
|
|
||||||
Example showing how this could be used for writing a simplified version of `unittest.check`:
|
|
||||||
```nim
|
|
||||||
import std/[genasts, macros, strutils]
|
|
||||||
macro check2(cond: bool): untyped =
|
|
||||||
assert cond.kind == nnkInfix, "$# not implemented" % $cond.kind
|
|
||||||
result = genAst(cond, s = repr(cond), lhs = cond[1], rhs = cond[2]):
|
|
||||||
# each local symbol we access must be explicitly captured
|
|
||||||
if not cond:
|
|
||||||
doAssert false, "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs]
|
|
||||||
let a = 3
|
|
||||||
check2 a*2 == a+3
|
|
||||||
if false: check2 a*2 < a+1 # would error with: 'a * 2 < a + 1'' failed: lhs: '6', rhs: '4'
|
|
||||||
```
|
|
||||||
|
|
||||||
See PR [#17426](https://github.com/nim-lang/Nim/pull/17426) for details.
|
|
||||||
|
|
||||||
|
|
||||||
## New module: `std/setutils`
|
|
||||||
|
|
||||||
- Added `setutils.toSet` that can take any iterable and convert it to a built-in `set`,
|
|
||||||
if the iterable yields a built-in settable type.
|
|
||||||
- Added `setutils.fullSet` which returns a full built-in `set` for a valid type.
|
|
||||||
- Added `setutils.complement` which returns the complement of a built-in `set`.
|
|
||||||
- Added `setutils.[]=`.
|
|
||||||
|
|
||||||
|
|
||||||
## New module: `std/enumutils`
|
|
||||||
|
|
||||||
- Added `genEnumCaseStmt` macro that generates
|
|
||||||
case statement to parse string to enum.
|
|
||||||
- Added `items` for enums with holes.
|
|
||||||
- Added `symbolName` to return the `enum` symbol name ignoring the human-readable name.
|
|
||||||
- Added `symbolRank` to return the index in which an `enum` member is listed in an enum.
|
|
||||||
|
|
||||||
|
|
||||||
## `system`
|
|
||||||
|
|
||||||
- Added `system.prepareMutation` for better support of low
|
|
||||||
level `moveMem`, `copyMem` operations for `gc:orc`'s copy-on-write string
|
|
||||||
implementation.
|
|
||||||
- `system.addEscapedChar` now renders `\r` as `\r` instead of `\c`, to be compatible
|
|
||||||
with most other languages.
|
|
||||||
- Added `cmpMem` to `system`.
|
|
||||||
- `doAssertRaises` now correctly handles foreign exceptions.
|
|
||||||
- `addInt` now supports unsigned integers.
|
|
||||||
|
|
||||||
Compatibility notes:
|
|
||||||
- `system.delete` had surprising behavior when the index passed to it was out of
|
|
||||||
bounds (it would delete the last entry then). Compile with `-d:nimStrictDelete` so
|
|
||||||
that an index error is produced instead. Be aware however that your code might depend on
|
|
||||||
this quirky behavior so a review process is required on your part before you can
|
|
||||||
use `-d:nimStrictDelete`. To make this review easier, use the `-d:nimAuditDelete`
|
|
||||||
switch, which pretends that `system.delete` is deprecated so that it is easier
|
|
||||||
to see where it was used in your code.
|
|
||||||
`-d:nimStrictDelete` will become the default in upcoming versions.
|
|
||||||
- `cuchar` is now deprecated as it aliased `char` where arguably it should have aliased `uint8`.
|
|
||||||
Please use `char` or `uint8` instead.
|
|
||||||
- `repr` now doesn't insert trailing newlines; the previous behavior was very inconsistent,
|
|
||||||
see [#16034](https://github.com/nim-lang/Nim/pull/16034).
|
|
||||||
Use `-d:nimLegacyReprWithNewline` for the previous behavior.
|
|
||||||
`repr` now also renders ASTs correctly for user defined literals, setters, `do`, etc.
|
|
||||||
- Deprecated `any`. See RFC [#281](https://github.com/nim-lang/RFCs/issues/281).
|
|
||||||
- The unary slice `..b` was deprecated, use `0..b` instead.
|
|
||||||
|
|
||||||
|
|
||||||
## `std/math`
|
|
||||||
|
|
||||||
- Added `almostEqual` for comparing two float values using a machine epsilon.
|
|
||||||
- Added `clamp` which allows using a `Slice` to clamp to a value.
|
|
||||||
- Added `ceilDiv` for integer division that rounds up.
|
|
||||||
- Added `isNaN`.
|
|
||||||
- Added `copySign`.
|
|
||||||
- Added `euclDiv` and `euclMod`.
|
|
||||||
- Added `signbit`.
|
|
||||||
- Added `frexp` overload procs. Deprecated `c_frexp`, use `frexp` instead.
|
|
||||||
|
|
||||||
Compatibility notes:
|
|
||||||
- `math.round` now rounds "away from zero" in the JS backend, which is consistent
|
|
||||||
with other backends. See [#9125](https://github.com/nim-lang/Nim/pull/9125).
|
|
||||||
Use `-d:nimLegacyJsRound` for the previous behavior.
|
|
||||||
|
|
||||||
|
|
||||||
## Random number generators: `std/random`, `std/sysrand`, `std/oids`
|
|
||||||
|
|
||||||
- Added `std/sysrand` module (see details above).
|
|
||||||
- Added `randState` template that exposes the default random number generator.
|
|
||||||
Useful for library authors.
|
|
||||||
- Added `initRand()` overload with no argument which uses the current time as a seed.
|
|
||||||
- `initRand(seed)` now allows `seed == 0`.
|
|
||||||
- Fixed overflow bugs.
|
|
||||||
- Fix `initRand` to avoid random number sequences overlapping, refs
|
|
||||||
[#18744](https://github.com/nim-lang/Nim/pull/18744).
|
|
||||||
- `std/oids` now uses `std/random`.
|
|
||||||
|
|
||||||
Compatibility notes:
|
|
||||||
- Deprecated `std/mersenne`.
|
|
||||||
- `random.initRand(seed)` now produces non-skewed values for the first call to
|
|
||||||
`rand()` after initialization with a small (< 30000) seed.
|
|
||||||
Use `-d:nimLegacyRandomInitRand` to restore previous behavior for a transition
|
|
||||||
time, see PR [#17467](https://github.com/nim-lang/Nim/pull/17467).
|
|
||||||
|
|
||||||
|
|
||||||
## `std/json`, `std/jsonutils`
|
|
||||||
|
|
||||||
- With `-d:nimPreviewJsonutilsHoleyEnum`, `jsonutils` now can serialize/deserialize
|
|
||||||
holey enums as regular enums (via `ord`) instead of as strings.
|
|
||||||
It is expected that this behavior becomes the new default in upcoming versions.
|
|
||||||
`toJson` now serializes `JsonNode` as is via reference (without a deep copy)
|
|
||||||
instead of treating `JsonNode` as a regular ref object,
|
|
||||||
this can be customized via `jsonNodeMode`.
|
|
||||||
- `std/json` and `std/jsonutils` now serialize `NaN`, `Inf`, `-Inf` as strings,
|
|
||||||
so that `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]`
|
|
||||||
which was invalid JSON.
|
|
||||||
- `std/json` can now handle integer literals and floating point literals of
|
|
||||||
arbitrary length and precision.
|
|
||||||
Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are
|
|
||||||
kept as string literals and one can use external BigNum libraries to handle these.
|
|
||||||
The `parseFloat` family of functions also has now optional `rawIntegers` and
|
|
||||||
`rawFloats` parameters that can be used to enforce that all integer or float
|
|
||||||
literals remain in the "raw" string form so that client code can easily treat
|
|
||||||
small and large numbers uniformly.
|
|
||||||
- Added `BackwardsIndex` overload for `JsonNode`.
|
|
||||||
- `json.%`,`json.to`, `jsonutils.fromJson`,`jsonutils.toJson` now work with `uint|uint64`
|
|
||||||
instead of raising (as in 1.4) or giving wrong results (as in 1.2).
|
|
||||||
- `std/jsonutils` now handles `cstring` (including as Table key), and `set`.
|
|
||||||
- Added `jsonutils.jsonTo` overload with `opt = Joptions()` param.
|
|
||||||
- `jsonutils.toJson` now supports customization via `ToJsonOptions`.
|
|
||||||
- `std/json`, `std/jsonutils` now support round-trip serialization when
|
|
||||||
`-d:nimPreviewFloatRoundtrip` is used.
|
|
||||||
|
|
||||||
|
|
||||||
## `std/typetraits`, `std/compilesettings`
|
|
||||||
|
|
||||||
- `distinctBase` now is identity instead of error for non distinct types.
|
|
||||||
- `distinctBase` now allows controlling whether to be recursive or not.
|
|
||||||
- Added `enumLen` to return the number of elements in an enum.
|
|
||||||
- Added `HoleyEnum` for enums with holes, `OrdinalEnum` for enums without holes.
|
|
||||||
- Added `hasClosure`.
|
|
||||||
- Added `pointerBase` to return `T` for `ref T | ptr T`.
|
|
||||||
- Added `compilesettings.SingleValueSetting.libPath`.
|
|
||||||
|
|
||||||
|
|
||||||
## networking: `std/net`, `std/asyncnet`, `std/htmlgen`, `std/httpclient`, `std/asyncdispatch`, `std/asynchttpserver`, `std/httpcore`
|
|
||||||
|
|
||||||
- Fixed buffer overflow bugs in `std/net`.
|
|
||||||
- Exported `sslHandle` from `std/net` and `std/asyncnet`.
|
|
||||||
- Added `hasDataBuffered` to `std/asyncnet`.
|
|
||||||
- Various functions in `std/httpclient` now accept `url` of type `Uri`.
|
|
||||||
Moreover, the `request` function's `httpMethod` argument of type `string` was
|
|
||||||
deprecated in favor of `HttpMethod` `enum` type; see
|
|
||||||
[#15919](https://github.com/nim-lang/Nim/pull/15919).
|
|
||||||
- Added `asyncdispatch.activeDescriptors` that returns the number of currently
|
|
||||||
active async event handles/file descriptors.
|
|
||||||
- Added `getPort` to `std/asynchttpserver` to resolve OS-assigned `Port(0)`;
|
|
||||||
this is usually recommended instead of hardcoding ports which can lead to
|
|
||||||
"Address already in use" errors.
|
|
||||||
- Fixed premature garbage collection in `std/asyncdispatch`, when a stacktrace
|
|
||||||
override is in place.
|
|
||||||
- Added `httpcore.is1xx` and missing HTTP codes.
|
|
||||||
- Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals).
|
|
||||||
|
|
||||||
Compatibility notes:
|
|
||||||
- On Windows, the SSL library now checks for valid certificates.
|
|
||||||
For this purpose it uses the `cacert.pem` file, which was extracted
|
|
||||||
from `https://curl.se/ca/cacert.pem`. Besides
|
|
||||||
the OpenSSL DLLs (e.g. `libssl-1_1-x64.dll`, `libcrypto-1_1-x64.dll`) you
|
|
||||||
now also need to ship `cacert.pem` with your `.exe` file.
|
|
||||||
|
|
||||||
|
|
||||||
## `std/hashes`
|
|
||||||
|
|
||||||
- `hashes.hash` can now support `object` and `ref` (can be overloaded in user code),
|
|
||||||
if `-d:nimPreviewHashRef` is used. It is expected that this behavior
|
|
||||||
becomes the new default in upcoming versions.
|
|
||||||
- `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`.
|
|
||||||
`hashes.hash(closure)` has also been improved.
|
|
||||||
|
|
||||||
|
|
||||||
## OS: `std/os`, `std/io`, `std/socketstream`, `std/linenoise`, `std/tempfiles`
|
|
||||||
|
|
||||||
- `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`,
|
|
||||||
determining preferred I/O block size for this file object.
|
|
||||||
- Added `os.getCacheDir()` to return platform specific cache directory.
|
|
||||||
- Improved `os.getTempDir()`, see PR [#16914](https://github.com/nim-lang/Nim/pull/16914).
|
|
||||||
- Added `os.isAdmin` to tell whether the caller's process is a member of the
|
|
||||||
Administrators local group (on Windows) or a root (on POSIX).
|
|
||||||
- Added optional `options` argument to `copyFile`, `copyFileToDir`, and
|
|
||||||
`copyFileWithPermissions`. By default, on non-Windows OSes, symlinks are
|
|
||||||
followed (copy files symlinks point to); on Windows, `options` argument is
|
|
||||||
ignored and symlinks are skipped.
|
|
||||||
- On non-Windows OSes, `copyDir` and `copyDirWithPermissions` copy symlinks as
|
|
||||||
symlinks (instead of skipping them as it was before); on Windows symlinks are
|
|
||||||
skipped.
|
|
||||||
- On non-Windows OSes, `moveFile` and `moveDir` move symlinks as symlinks
|
|
||||||
(instead of skipping them sometimes as it was before).
|
|
||||||
- Added optional `followSymlinks` argument to `setFilePermissions`.
|
|
||||||
- Added a simpler to use `io.readChars` overload.
|
|
||||||
- Added `socketstream` module that wraps sockets in the stream interface.
|
|
||||||
- Added experimental `linenoise.readLineStatus` to get line and status (e.g. ctrl-D or ctrl-C).
|
|
||||||
|
|
||||||
|
|
||||||
## Environment variable handling
|
|
||||||
|
|
||||||
- Empty environment variable values are now supported across OS's and backends.
|
|
||||||
- Environment variable APIs now work in multithreaded scenarios, by delegating to
|
|
||||||
direct OS calls instead of trying to keep track of the environment.
|
|
||||||
- `putEnv`, `delEnv` now work at compile time.
|
|
||||||
- NodeJS backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`.
|
|
||||||
|
|
||||||
Compatibility notes:
|
|
||||||
- `std/os`: `putEnv` now raises if the first argument contains a `=`.
|
|
||||||
|
|
||||||
|
|
||||||
## POSIX
|
|
||||||
|
|
||||||
- On POSIX systems, the default signal handlers used for Nim programs (it's
|
|
||||||
used for printing the stacktrace on fatal signals) will now re-raise the
|
|
||||||
signal for the OS default handlers to handle.
|
|
||||||
This lets the OS perform its default actions, which might include core
|
|
||||||
dumping (on select signals) and notifying the parent process about the cause
|
|
||||||
of termination.
|
|
||||||
- On POSIX systems, we now ignore `SIGPIPE` signals, use `-d:nimLegacySigpipeHandler`
|
|
||||||
for previous behavior.
|
|
||||||
- Added `posix_utils.osReleaseFile` to get system identification from `os-release`
|
|
||||||
file on Linux and the BSDs.
|
|
||||||
([link](https://www.freedesktop.org/software/systemd/man/os-release.html))
|
|
||||||
- Removed undefined behavior for `posix.open`.
|
|
||||||
|
|
||||||
|
|
||||||
## `std/prelude`
|
|
||||||
|
|
||||||
- `std/strformat` is now part of `include std/prelude`.
|
|
||||||
- Added `std/sequtils` import to `std/prelude`.
|
|
||||||
- `std/prelude` now works with the JS target.
|
|
||||||
- `std/prelude` can now be used via `include std/prelude`, but `include prelude` still works.
|
|
||||||
|
|
||||||
|
|
||||||
## String manipulation: `std/strformat`, `std/strbasics`
|
|
||||||
|
|
||||||
- Added support for parenthesized expressions.
|
|
||||||
- Added support for const strings instead of just string literals.
|
|
||||||
- Added `std/strbasics` for high-performance string operations.
|
|
||||||
- Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`.
|
|
||||||
|
|
||||||
|
|
||||||
## `std/wrapnils`
|
|
||||||
|
|
||||||
- `std/wrapnils` doesn't use `experimental:dotOperators` anymore, avoiding
|
|
||||||
issues like bug [#13063](https://github.com/nim-lang/Nim/issues/13063)
|
|
||||||
(which affected error messages) for modules importing `std/wrapnils`.
|
|
||||||
- Added `??.` macro which returns an `Option`.
|
|
||||||
- `std/wrapnils` can now be used to protect against `FieldDefect` errors in
|
|
||||||
case objects, generates optimal code (no overhead compared to manual
|
|
||||||
if-else branches), and preserves lvalue semantics which allows modifying
|
|
||||||
an expression.
|
|
||||||
|
|
||||||
|
|
||||||
## Containers: `std/algorithm`, `std/lists`, `std/sequtils`, `std/options`, `std/packedsets`
|
|
||||||
|
|
||||||
- Removed the optional `longestMatch` parameter of the `critbits._WithPrefix`
|
|
||||||
iterators (it never worked reliably).
|
|
||||||
- Added `algorithm.merge`.
|
|
||||||
- In `std/lists`: renamed `append` to `add` and retained `append` as an alias;
|
|
||||||
added `prepend` and `prependMoved` analogously to `add` and `addMoved`;
|
|
||||||
added `remove` for `SinglyLinkedList`s.
|
|
||||||
- Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList`
|
|
||||||
and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements
|
|
||||||
shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes
|
|
||||||
its argument, `addMoved`, is also supplied.
|
|
||||||
See PRs [#16362](https://github.com/nim-lang/Nim/pull/16362),
|
|
||||||
[#16536](https://github.com/nim-lang/Nim/pull/16536).
|
|
||||||
- New module: `std/packedsets`.
|
|
||||||
Generalizes `std/intsets`, see PR [#15564](https://github.com/nim-lang/Nim/pull/15564).
|
|
||||||
|
|
||||||
Compatibility notes:
|
|
||||||
- Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises
|
|
||||||
a defect if the slice is out of bounds, likewise with `strutils.delete`.
|
|
||||||
- Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]`
|
|
||||||
in `std/algorithm`.
|
|
||||||
- `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"`
|
|
||||||
and `$none(int)` to `"none(int)"` instead of `"None[int]"`.
|
|
||||||
|
|
||||||
|
|
||||||
## `std/times`
|
|
||||||
|
|
||||||
- Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time
|
|
||||||
zone offsets without colons, e.g. `UTC+7 -> +0700`.
|
|
||||||
- Added `dateTime` and deprecated `initDateTime`.
|
|
||||||
|
|
||||||
|
|
||||||
## `std/macros` and AST
|
|
||||||
|
|
||||||
- New module `std/genasts`, see description above.
|
|
||||||
- The required name of case statement macros for the experimental
|
|
||||||
`caseStmtMacros` feature has changed from `match` to `` `case` ``.
|
|
||||||
- Tuple expressions are now parsed consistently as
|
|
||||||
`nnkTupleConstr` node. Will affect macros expecting nodes to be of `nnkPar`.
|
|
||||||
- In `std/macros`, `treeRepr,lispRepr,astGenRepr` now represent SymChoice nodes
|
|
||||||
in a collapsed way.
|
|
||||||
Use `-d:nimLegacyMacrosCollapseSymChoice` to get the previous behavior.
|
|
||||||
- Made custom op in `macros.quote` work for all statements.
|
|
||||||
|
|
||||||
|
|
||||||
## `std/sugar`
|
|
||||||
|
|
||||||
- Added `sugar.dumpToString` which improves on `sugar.dump`.
|
|
||||||
- Added an overload for the `collect` macro that infers the container type based
|
|
||||||
on the syntax of the last expression. Works with std seqs, tables and sets.
|
|
||||||
|
|
||||||
Compatibility notes:
|
|
||||||
- Removed support for named procs in `sugar.=>`.
|
|
||||||
|
|
||||||
|
|
||||||
## Parsing: `std/parsecfg`, `std/strscans`, `std/uri`
|
|
||||||
|
|
||||||
- Added `sections` iterator in `parsecfg`.
|
|
||||||
- `strscans.scanf` now supports parsing single characters.
|
|
||||||
- Added `strscans.scanTuple` which uses `strscans.scanf` internally,
|
|
||||||
returning a tuple which can be unpacked for easier usage of `scanf`.
|
|
||||||
- Added `decodeQuery` to `std/uri`.
|
|
||||||
- `parseopt.initOptParser` has been made available and `parseopt` has been
|
|
||||||
added back to `std/prelude` for all backends. Previously `initOptParser` was
|
|
||||||
unavailable if the `std/os` module did not have `paramCount` or `paramStr`,
|
|
||||||
but the use of these in `initOptParser` were conditionally to the runtime
|
|
||||||
arguments passed to it, so `initOptParser` has been changed to raise
|
|
||||||
`ValueError` when the real command line is not available. `parseopt` was
|
|
||||||
previously excluded from `std/prelude` for JS, as it could not be imported.
|
|
||||||
|
|
||||||
Compatibility notes:
|
|
||||||
- Changed the behavior of `uri.decodeQuery` when there are unencoded `=`
|
|
||||||
characters in the decoded values. Prior versions would raise an error. This is
|
|
||||||
no longer the case to comply with the HTML spec and other languages'
|
|
||||||
implementations. Old behavior can be obtained with
|
|
||||||
`-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same
|
|
||||||
underlying code is also updated the same way.
|
|
||||||
|
|
||||||
|
|
||||||
## JS stdlib changes
|
|
||||||
|
|
||||||
- Added `std/jsbigints` module, which provides arbitrary precision integers for the JS target.
|
|
||||||
- Added `setCurrentException` for the JS backend.
|
|
||||||
- `writeStackTrace` is available in the JS backend now.
|
|
||||||
- Added `then`, `catch` to `std/asyncjs` for promise pipelining, for now hidden
|
|
||||||
behind `-d:nimExperimentalAsyncjsThen`.
|
|
||||||
- Added `std/jsfetch` module [Fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API)
|
|
||||||
wrapper for the JS target.
|
|
||||||
- Added `std/jsheaders` module [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
|
|
||||||
wrapper for the JS target.
|
|
||||||
- Added `std/jsformdata` module [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
|
|
||||||
wrapper for the JS target.
|
|
||||||
- Added `jscore.debugger` to [call any available debugging functionality, such as breakpoints](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger).
|
|
||||||
- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`.
|
|
||||||
- Added dollar `$` and `len` for `jsre.RegExp`.
|
|
||||||
- Added `jsconsole.jsAssert` for the JS target.
|
|
||||||
- Added `**` to `std/jsffi`.
|
|
||||||
- Added `copyWithin` [for `seq` and `array` for JS targets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin).
|
|
||||||
- In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`.
|
|
||||||
Definitions of `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` were updated.
|
|
||||||
- Added `dom.scrollIntoView` proc with options.
|
|
||||||
- Added `dom.setInterval`, `dom.clearInterval` overloads.
|
|
||||||
- Merged `std/dom_extensions` into the `std/dom` module,
|
|
||||||
as it was a module with a single line, see RFC [#413](https://github.com/nim-lang/RFCs/issues/413).
|
|
||||||
- `$` now gives more correct results on the JS backend.
|
|
||||||
|
|
||||||
|
|
||||||
## JS compiler changes
|
|
||||||
|
|
||||||
- `cstring` doesn't support the `[]=` operator anymore in the JS backend.
|
|
||||||
- Array literals now use JS typed arrays when the corresponding JS typed array exists,
|
|
||||||
for example `[byte(1), 2, 3]` generates `new Uint8Array([1, 2, 3])`.
|
|
||||||
|
|
||||||
|
|
||||||
## VM and nimscript backend
|
|
||||||
|
|
||||||
- VM now supports `addr(mystring[ind])` (index + index assignment).
|
|
||||||
- `nimscript` now handles `except Exception as e`.
|
|
||||||
- `nil` dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time.
|
|
||||||
- `static[T]` now works better, refs [#17590](https://github.com/nim-lang/Nim/pull/17590),
|
|
||||||
[#15853](https://github.com/nim-lang/Nim/pull/15853).
|
|
||||||
- `distinct T` conversions now work in VM.
|
|
||||||
- `items(cstring)` now works in VM.
|
|
||||||
- Fix `addr`, `len`, `high` in VM ([#16002](https://github.com/nim-lang/Nim/pull/16002),
|
|
||||||
[#16610](https://github.com/nim-lang/Nim/pull/16610)).
|
|
||||||
- `std/cstrutils` now works in VM.
|
|
||||||
|
|
||||||
|
|
||||||
## OS/platform-specific notes
|
|
||||||
|
|
||||||
- Support for Apple silicon/M1.
|
|
||||||
- Support for 32-bit RISC-V, refs [#16231](https://github.com/nim-lang/Nim/pull/16231).
|
|
||||||
- Support for armv8l, refs [#18901](https://github.com/nim-lang/Nim/pull/18901).
|
|
||||||
- Support for CROSSOS, refs [#18889](https://github.com/nim-lang/Nim/pull/18889).
|
|
||||||
- The allocator for Nintendo Switch, which was nonfunctional because
|
|
||||||
of breaking changes in libnx, was removed, in favor of the new `-d:nimAllocPagesViaMalloc` option.
|
|
||||||
- Allow reading parameters when compiling for Nintendo Switch.
|
|
||||||
- `--nimcache` now correctly works in a cross-compilation setting.
|
|
||||||
- Cross compilation targeting Windows was improved.
|
|
||||||
- This now works from macOS/Linux: `nim r -d:mingw main`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Performance / memory optimizations
|
|
||||||
|
|
||||||
- The comment field in PNode AST was moved to a side channel, reducing overall
|
|
||||||
memory usage during compilation by a factor 1.25x
|
|
||||||
- `std/jsonutils` deserialization is now up to 20x faster.
|
|
||||||
- `os.copyFile` is now 2.5x faster on macOS, by using `copyfile` from `copyfile.h`;
|
|
||||||
use `-d:nimLegacyCopyFile` for macOS < 10.5.
|
|
||||||
- Float to string conversion is now 10x faster thanks to the Dragonbox algorithm,
|
|
||||||
with `-d:nimPreviewFloatRoundtrip`.
|
|
||||||
- `newSeqWith` is 3x faster.
|
|
||||||
- CI now supports batching (making Windows CI 2.3X faster).
|
|
||||||
- Sets now uses the optimized `countSetBits` proc, see PR
|
|
||||||
[#17334](https://github.com/nim-lang/Nim/pull/17334).
|
|
||||||
|
|
||||||
|
|
||||||
## Debugging
|
|
||||||
|
|
||||||
- You can now enable/disable VM tracing in user code via `vmutils.vmTrace`.
|
|
||||||
- `koch tools` now builds `bin/nim_dbg` which allows easy access to a debug version
|
|
||||||
of Nim without recompiling.
|
|
||||||
- Added new module `compiler/debugutils` to help with debugging Nim compiler.
|
|
||||||
- Renamed `-d:nimCompilerStackraceHints` to `-d:nimCompilerStacktraceHints` and
|
|
||||||
used it in more contexts; this flag which works in tandem with `--stackTraceMsgs`
|
|
||||||
to show user code context in compiler stacktraces.
|
|
||||||
|
|
||||||
|
|
||||||
## Type system
|
|
||||||
|
|
||||||
- `typeof(voidStmt)` now works and returns `void`.
|
|
||||||
- `enum` values can now be overloaded. This needs to be enabled
|
|
||||||
via `{.experimental: "overloadableEnums".}`. We hope that this feature allows
|
|
||||||
for the development of more fluent (less ugly) APIs.
|
|
||||||
See RFC [#373](https://github.com/nim-lang/RFCs/issues/373) for more details.
|
|
||||||
- A type conversion from one `enum` type to another now produces an `[EnumConv]` warning.
|
|
||||||
You should use `ord` (or `cast`, but the compiler won't help, if you misuse it) instead.
|
|
||||||
```nim
|
|
||||||
type A = enum a1, a2
|
|
||||||
type B = enum b1, b2
|
|
||||||
echo a1.B # produces a warning
|
|
||||||
echo a1.ord.B # produces no warning
|
|
||||||
```
|
|
||||||
- A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning.
|
|
||||||
This warning will become an error in future versions! Use an explicit conversion
|
|
||||||
like `cstring(x)` in order to silence the warning.
|
|
||||||
- There is a new warning for *any* type conversion to `enum` that can be enabled via
|
|
||||||
`.warning[AnyEnumConv]:on` or `--warning:AnyEnumConv:on`.
|
|
||||||
- Reusing a type name in a different scope now works, refs [#17710](https://github.com/nim-lang/Nim/pull/17710).
|
|
||||||
- Fixed implicit and explicit generics in procedures, refs [#18808](https://github.com/nim-lang/Nim/pull/18808).
|
|
||||||
|
|
||||||
|
|
||||||
## New-style concepts
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```nim
|
|
||||||
type
|
|
||||||
Comparable = concept # no T, an atom
|
|
||||||
proc cmp(a, b: Self): int
|
|
||||||
```
|
|
||||||
The new design does not rely on `system.compiles` and may compile faster.
|
|
||||||
See PR [#15251](https://github.com/nim-lang/Nim/pull/15251)
|
|
||||||
and RFC [#168](https://github.com/nim-lang/RFCs/issues/168) for details.
|
|
||||||
|
|
||||||
|
|
||||||
## Lexical / syntactic
|
|
||||||
|
|
||||||
- Nim now supports a small subset of Unicode operators as operator symbols.
|
|
||||||
The supported symbols are: "∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔".
|
|
||||||
To enable this feature, use `--experimental:unicodeOperators`. Note that due
|
|
||||||
to parser limitations you **cannot** enable this feature via a
|
|
||||||
pragma `{.experimental: "unicodeOperators".}` reliably, you need to enable
|
|
||||||
it via the command line or in a configuration file.
|
|
||||||
- `var a {.foo.} = expr` now works inside templates (except when `foo` is overloaded).
|
|
||||||
|
|
||||||
|
|
||||||
## Compiler messages, error messages, hints, warnings
|
|
||||||
|
|
||||||
- Significant improvement to error messages involving effect mismatches,
|
|
||||||
see PRs [#18384](https://github.com/nim-lang/Nim/pull/18384),
|
|
||||||
[#18418](https://github.com/nim-lang/Nim/pull/18418).
|
|
||||||
- Added `--declaredLocs` to show symbol declaration location in error messages.
|
|
||||||
- Added `--spellSuggest` to show spelling suggestions on typos.
|
|
||||||
- Added `--processing:dots|filenames|off` which customizes `hintProcessing`;
|
|
||||||
`--processing:filenames` shows which include/import modules are being compiled as an import stack.
|
|
||||||
- `FieldDefect` messages now shows discriminant value + lineinfo, in all backends (C, JS, VM)
|
|
||||||
- Added `--hintAsError` with similar semantics as `--warningAsError`.
|
|
||||||
- Added `--unitsep:on|off` to control whether to add ASCII unit separator `\31`
|
|
||||||
before a newline for every generated message (potentially multiline),
|
|
||||||
so tooling can tell when messages start and end.
|
|
||||||
- Added `--filenames:abs|canonical|legacyRelProj` which replaces `--listFullPaths:on|off`
|
|
||||||
- `--hint:all:on|off` is now supported to select or deselect all hints; it
|
|
||||||
differs from `--hints:on|off` which acts as a (reversible) gate.
|
|
||||||
Likewise with `--warning:all:on|off`.
|
|
||||||
- The style checking of the compiler now supports a `--styleCheck:usages` switch.
|
|
||||||
This switch enforces that every symbol is written as it was declared, not enforcing
|
|
||||||
the official Nim style guide. To be enabled, this has to be combined either
|
|
||||||
with `--styleCheck:error` or `--styleCheck:hint`.
|
|
||||||
- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for
|
|
||||||
previous behavior.
|
|
||||||
- `typedesc[Foo]` now renders as such instead of `type Foo` in compiler messages.
|
|
||||||
- `runnableExamples` now show originating location in stacktraces on failure.
|
|
||||||
- `SuccessX` message now shows more useful information.
|
|
||||||
- New `DuplicateModuleImport` warning; improved `UnusedImport` and
|
|
||||||
`XDeclaredButNotUsed` accuracy.
|
|
||||||
|
|
||||||
Compatibility notes:
|
|
||||||
- `--hint:CC` now prints to stderr (like all other hints) instead of stdout.
|
|
||||||
|
|
||||||
|
|
||||||
## Building and running Nim programs, configuration system
|
|
||||||
|
|
||||||
- JSON build instructions are now generated in `$nimcache/outFileBasename.json`
|
|
||||||
instead of `$nimcache/projectName.json`. This allows avoiding recompiling a
|
|
||||||
given project compiled with different options if the output file differs.
|
|
||||||
- `--usenimcache` (implied by `nim r main`) now generates an output file that includes
|
|
||||||
a hash of some of the compilation options, which allows caching generated binaries:
|
|
||||||
```bash
|
|
||||||
nim r main # recompiles
|
|
||||||
nim r -d:foo main # recompiles
|
|
||||||
nim r main # uses cached binary
|
|
||||||
nim r main arg1 arg2 # likewise (runtime arguments are irrelevant)
|
|
||||||
```
|
|
||||||
- `nim r` now supports cross compilation from unix to windows when specifying
|
|
||||||
`-d:mingw` by using Wine, e.g.:
|
|
||||||
`nim r --eval:'import os; echo "a" / "b"'` prints `a\b`.
|
|
||||||
- `nim` can compile version 1.4.0 as follows:
|
|
||||||
`nim c --lib:lib --stylecheck:off -d:nimVersion140 compiler/nim`.
|
|
||||||
`-d:nimVersion140` is not needed for bootstrapping, only for building 1.4.0 from devel.
|
|
||||||
- `nim e` now accepts arbitrary file extensions for the nimscript file,
|
|
||||||
although `.nims` is still the preferred extension in general.
|
|
||||||
- The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected.
|
|
||||||
The downside is that these defines now have custom logic that doesn't apply for
|
|
||||||
other defines.
|
|
||||||
|
|
||||||
|
|
||||||
## Multithreading
|
|
||||||
|
|
||||||
- TLS: macOS now uses native TLS (`--tlsEmulation:off`). TLS now works with
|
|
||||||
`importcpp` non-POD types; such types must use `.cppNonPod` and
|
|
||||||
`--tlsEmulation:off`should be used.
|
|
||||||
- Added `unsafeIsolate` and `extract` to `std/isolation`.
|
|
||||||
- Added `std/tasks`, see description above.
|
|
||||||
|
|
||||||
|
|
||||||
## Memory management
|
|
||||||
|
|
||||||
- `--gc:arc` now bootstraps (PR [#17342](https://github.com/nim-lang/Nim/pull/17342)).
|
|
||||||
- Lots of improvements to `gc:arc`, `gc:orc`, see PR
|
|
||||||
[#15697](https://github.com/nim-lang/Nim/pull/15697),
|
|
||||||
[#16849](https://github.com/nim-lang/Nim/pull/16849),
|
|
||||||
[#17993](https://github.com/nim-lang/Nim/pull/17993).
|
|
||||||
- `--gc:orc` is now 10% faster than previously for common workloads.
|
|
||||||
If you have trouble with its changed behavior, compile with `-d:nimOldOrc`.
|
|
||||||
- The `--gc:orc` algorithm was refined so that custom container types can participate in the
|
|
||||||
cycle collection process. See the documentation of `=trace` for more details.
|
|
||||||
- On embedded devices `malloc` can now be used instead of `mmap` via `-d:nimAllocPagesViaMalloc`.
|
|
||||||
This is only supported for `--gc:orc` or `--gc:arc`.
|
|
||||||
|
|
||||||
Compatibility notes:
|
|
||||||
- `--newruntime` and `--refchecks` are deprecated,
|
|
||||||
use `--gc:arc`, `--gc:orc`, or `--gc:none` as appropriate instead.
|
|
||||||
|
|
||||||
|
|
||||||
## Docgen
|
|
||||||
|
|
||||||
- docgen: RST files can now use single backticks instead of double backticks and
|
|
||||||
correctly render in both `nim rst2html` (as before) as well as common tools rendering
|
|
||||||
RST directly (e.g. GitHub).
|
|
||||||
This is done by adding the `default-role:: code` directive inside the RST file
|
|
||||||
(which is now handled by `nim rst2html`).
|
|
||||||
- Source+Edit links now appear on top of every docgen'd page when
|
|
||||||
`nim doc --git.url:url ...` is given.
|
|
||||||
- Latex doc generation is revised: output `.tex` files should be compiled
|
|
||||||
by `xelatex` (not by `pdflatex` as before). Now default Latex settings
|
|
||||||
provide support for Unicode and better avoid margin overflows.
|
|
||||||
The minimum required version is TeXLive 2018 (or an equivalent MikTeX version).
|
|
||||||
- The RST parser now supports footnotes, citations, admonitions, and short style
|
|
||||||
references with symbols.
|
|
||||||
- The RST parser now supports Markdown table syntax.
|
|
||||||
Known limitations:
|
|
||||||
- cell alignment is not supported, i.e. alignment annotations in a delimiter
|
|
||||||
row (`:---`, `:--:`, `---:`) are ignored
|
|
||||||
- every table row must start with `|`, e.g. `| cell 1 | cell 2 |`.
|
|
||||||
- Implemented `doc2tex` compiler command which converts documentation in
|
|
||||||
`.nim` files to Latex.
|
|
||||||
- docgen now supports syntax highlighting for inline code.
|
|
||||||
- docgen now supports same-line doc comments:
|
|
||||||
```nim
|
|
||||||
func fn*(a: int): int = 42 ## Doc comment
|
|
||||||
```
|
|
||||||
- docgen now renders `deprecated` and other pragmas.
|
|
||||||
- `runnableExamples` now works with templates and nested templates.
|
|
||||||
- `runnableExamples: "-r:off"` now works for examples that should compile but not run.
|
|
||||||
- `runnableExamples` now renders code verbatim, and produces correct code in all cases.
|
|
||||||
- docgen now shows correct, canonical import paths in docs.
|
|
||||||
- docgen now shows all routines in sidebar, and the proc signature is now shown in sidebar.
|
|
||||||
|
|
||||||
|
|
||||||
## Effects and checks
|
|
||||||
|
|
||||||
- Significant improvement to error messages involving effect mismatches
|
|
||||||
- There is a new `cast` section `{.cast(uncheckedAssign).}: body` that disables some
|
|
||||||
compiler checks regarding `case objects`. This allows serialization libraries
|
|
||||||
to avoid ugly, non-portable solutions. See RFC
|
|
||||||
[#407](https://github.com/nim-lang/RFCs/issues/407) for more details.
|
|
||||||
|
|
||||||
Compatibility notes:
|
|
||||||
- Fixed effect tracking for borrowed procs (see [#18882](https://github.com/nim-lang/Nim/pull/18882)).
|
|
||||||
One consequence is that, under some circumstances, Nim could previously permit a procedure with side effects to be written with `func` - you may need to change some occurrences of `func` to `proc`.
|
|
||||||
To illustrate, Nim versions before 1.6.0 compile the below without error
|
|
||||||
```nim
|
|
||||||
proc print(s: string) =
|
|
||||||
echo s
|
|
||||||
|
|
||||||
type
|
|
||||||
MyString = distinct string
|
|
||||||
|
|
||||||
proc print(s: MyString) {.borrow.}
|
|
||||||
|
|
||||||
func foo(s: MyString) =
|
|
||||||
print(s)
|
|
||||||
```
|
|
||||||
but Nim 1.6.0 produces the error
|
|
||||||
```
|
|
||||||
Error: 'foo' can have side effects
|
|
||||||
```
|
|
||||||
similar to how we expect that
|
|
||||||
```nim
|
|
||||||
func print(s: string) =
|
|
||||||
echo s
|
|
||||||
```
|
|
||||||
produces
|
|
||||||
```
|
|
||||||
Error: 'print' can have side effects
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Tools
|
|
||||||
|
|
||||||
- Major improvements to `nimgrep`, see PR [#15612
|
|
||||||
](https://github.com/nim-lang/Nim/pull/15612).
|
|
||||||
- `fusion` is now un-bundled from Nim, `./koch fusion` will
|
|
||||||
install it via Nimble at a fixed hash.
|
|
||||||
- `testament`: added `nimoutFull: bool` spec to compare full output of compiler
|
|
||||||
instead of a subset; many bugfixes to testament.
|
|
||||||
|
|
||||||
|
|
||||||
## Misc/cleanups
|
|
||||||
|
|
||||||
- Deprecated `TaintedString` and `--taintmode`.
|
|
||||||
- Deprecated `--nilseqs` which is now a noop.
|
|
||||||
- Added `-d:nimStrictMode` in CI in several places to ensure code doesn't have
|
|
||||||
certain hints/warnings.
|
|
||||||
- Removed `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled`.
|
|
||||||
- `[skip ci]` now works in azure and CI pipelines, see detail in PR
|
|
||||||
[#17561](https://github.com/nim-lang/Nim/pull/17561).
|
|
||||||
|
|
@ -1,367 +0,0 @@
|
||||||
# v2.0.0 - yyyy-mm-dd
|
|
||||||
|
|
||||||
|
|
||||||
## 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`.
|
|
||||||
|
|
||||||
- `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 `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.
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
|
|
||||||
- The `mainmodule` and `m` options are removed.
|
|
||||||
|
|
||||||
- The `threads:on` option is now the default.
|
|
||||||
|
|
||||||
- Optional parameters in combination with `: body` syntax (RFC #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`.
|
|
||||||
|
|
||||||
- `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.
|
|
||||||
|
|
||||||
- Removed the `nimIncrSeqV3` define.
|
|
||||||
|
|
||||||
- `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.
|
|
||||||
|
|
||||||
- ORC is now the default memory management strategy. Use
|
|
||||||
`--mm:refc` for a transition period.
|
|
||||||
|
|
||||||
- `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` 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`
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
|
|
||||||
- If no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered.
|
|
||||||
|
|
||||||
- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection.
|
|
||||||
|
|
||||||
- Underscores (`_`) as routine parameters are now ignored and cannot be used in the routine body.
|
|
||||||
The following code now does not compile:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
proc foo(_: int): int = _ + 1
|
|
||||||
echo foo(1)
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, the following code now compiles:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
proc foo(_, _: int): int = 123
|
|
||||||
echo foo(1, 2)
|
|
||||||
```
|
|
||||||
- Underscores (`_`) as generic parameters are not supported and cannot be used.
|
|
||||||
Generics that use `_` as parameters will no longer compile requires you to replace `_` with something else:
|
|
||||||
|
|
||||||
```nim
|
|
||||||
proc foo[_](t: typedesc[_]): string = "BAR" # Can not compile
|
|
||||||
proc foo[T](t: typedesc[T]): string = "BAR" # Can compile
|
|
||||||
```
|
|
||||||
|
|
||||||
- - Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
|
|
||||||
|
|
||||||
## 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`.
|
|
||||||
|
|
||||||
[//]: # "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.
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
|
|
||||||
## 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`).
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
||||||
# v1.xx.x - yyyy-mm-dd
|
|
||||||
|
|
||||||
This is an example file.
|
|
||||||
The changes should go to changelog.md!
|
|
||||||
|
|
||||||
## Changes affecting backward compatibility
|
|
||||||
|
|
||||||
- `foo` now behaves differently, use `-d:nimLegacyFoo` for previous behavior.
|
|
||||||
|
|
||||||
## Standard library additions and changes
|
|
||||||
|
|
||||||
- Added `example.exampleProc`.
|
|
||||||
|
|
||||||
- Changed `example.foo` to take additional `bar` parameter.
|
|
||||||
|
|
||||||
## Language changes
|
|
||||||
|
|
||||||
|
|
||||||
## Compiler changes
|
|
||||||
|
|
||||||
|
|
||||||
## Tool changes
|
|
||||||
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
# Update changelog
|
|
||||||
After each release, call:
|
|
||||||
```
|
|
||||||
git mv changelog.md changelogs/changelog_0_19_0.md # use correct version
|
|
||||||
cp changelogs/changelog_X_XX_X.md changelog.md
|
|
||||||
git add changelog.md
|
|
||||||
```
|
|
||||||
|
|
||||||
## Recent changelogs are saved here (with their git history)
|
|
||||||
|
|
||||||
## Older changelogs are stored in https://github.com/nim-lang/website
|
|
||||||
|
|
||||||
## source files:
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2009-12-21-version-086-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2010-03-14-version-088-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2010-10-20-version-0810-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2011-07-10-version-0812-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2012-02-09-version-0814-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2012-09-23-version-090-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2013-05-20-version-092-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-04-21-version-094-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-10-19-version-096-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2014-12-29-version-0102-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-04-30-version-0110-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-05-04-version-0112-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2015-10-27-version-0120-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-01-18-version-0130-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-06-07-version-0140-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-06-09-version-0142-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-09-30-version-0150-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2016-10-23-version-0152-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-01-08-version-0160-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-05-17-version-0170-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2017-09-07-version-0172-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2018-03-01-version-0180-released.md
|
|
||||||
* https://github.com/nim-lang/website/blob/master/jekyll/_posts/2018-09-26-version-0190-released.md
|
|
||||||
|
|
||||||
## urls:
|
|
||||||
* https://nim-lang.org/blog/2009/12/21/version-086-released.html
|
|
||||||
* https://nim-lang.org/blog/2010/03/14/version-088-released.html
|
|
||||||
* https://nim-lang.org/blog/2010/10/20/version-0810-released.html
|
|
||||||
* https://nim-lang.org/blog/2011/07/10/version-0812-released.html
|
|
||||||
* https://nim-lang.org/blog/2012/02/09/version-0814-released.html
|
|
||||||
* https://nim-lang.org/blog/2012/09/23/version-090-released.html
|
|
||||||
* https://nim-lang.org/blog/2013/05/20/version-092-released.html
|
|
||||||
* https://nim-lang.org/blog/2014/04/21/version-094-released.html
|
|
||||||
* https://nim-lang.org/blog/2014/10/19/version-096-released.html
|
|
||||||
* https://nim-lang.org/blog/2014/12/29/version-0102-released.html
|
|
||||||
* https://nim-lang.org/blog/2015/04/30/version-0110-released.html
|
|
||||||
* https://nim-lang.org/blog/2015/05/04/version-0112-released.html
|
|
||||||
* https://nim-lang.org/blog/2015/10/27/version-0120-released.html
|
|
||||||
* https://nim-lang.org/blog/2016/01/18/version-0130-released.html
|
|
||||||
* https://nim-lang.org/blog/2016/06/07/version-0140-released.html
|
|
||||||
* https://nim-lang.org/blog/2016/06/09/version-0142-released.html
|
|
||||||
* https://nim-lang.org/blog/2016/09/30/version-0150-released.html
|
|
||||||
* https://nim-lang.org/blog/2016/10/23/version-0152-released.html
|
|
||||||
* https://nim-lang.org/blog/2017/01/08/version-0160-released.html
|
|
||||||
* https://nim-lang.org/blog/2017/05/17/version-0170-released.html
|
|
||||||
* https://nim-lang.org/blog/2017/09/07/version-0172-released.html
|
|
||||||
* https://nim-lang.org/blog/2018/03/01/version-0180-released.html
|
|
||||||
* https://nim-lang.org/blog/2018/09/26/version-0190-released.html
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import std/[strutils, os, osproc, parseutils, strformat]
|
|
||||||
|
|
||||||
|
|
||||||
proc main() =
|
|
||||||
var msg = ""
|
|
||||||
const cmd = "./koch boot --gc:orc -d:release"
|
|
||||||
|
|
||||||
let (output, exitCode) = execCmdEx(cmd)
|
|
||||||
|
|
||||||
doAssert exitCode == 0, output
|
|
||||||
|
|
||||||
let start = rfind(output, "Hint: mm")
|
|
||||||
doAssert parseUntil(output, msg, "; proj", start) > 0, output
|
|
||||||
|
|
||||||
let (commitHash, _) = execCmdEx("""git log --format="%H" -n 1""")
|
|
||||||
|
|
||||||
let welcomeMessage = fmt"""Thanks for your hard work on this PR!
|
|
||||||
The lines below are statistics of the Nim compiler built from {commitHash}
|
|
||||||
|
|
||||||
{msg}
|
|
||||||
"""
|
|
||||||
createDir "ci/nimcache"
|
|
||||||
writeFile "ci/nimcache/results.txt", welcomeMessage
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
main()
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
@echo off
|
|
||||||
rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
|
||||||
rem Build development version of the compiler; can be rerun safely
|
|
||||||
rem bare bones version of ci/funs.sh adapted for windows.
|
|
||||||
|
|
||||||
rem Read in some common shared variables (shared with other tools),
|
|
||||||
rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file
|
|
||||||
for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H
|
|
||||||
SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe
|
|
||||||
echo "building from csources: %nim_csources%"
|
|
||||||
|
|
||||||
if not exist %nim_csourcesDir% (
|
|
||||||
git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir%
|
|
||||||
)
|
|
||||||
|
|
||||||
if not exist %nim_csources% (
|
|
||||||
cd %nim_csourcesDir%
|
|
||||||
git checkout %nim_csourcesHash%
|
|
||||||
echo "%PROCESSOR_ARCHITECTURE%"
|
|
||||||
if "%PROCESSOR_ARCHITECTURE%"=="AMD64" (
|
|
||||||
SET ARCH=64
|
|
||||||
)
|
|
||||||
CALL build.bat
|
|
||||||
cd ..
|
|
||||||
copy /y bin\nim.exe %nim_csources%
|
|
||||||
)
|
|
||||||
143
ci/funs.sh
143
ci/funs.sh
|
|
@ -1,143 +0,0 @@
|
||||||
# Utilities used in CI pipelines and tooling to avoid duplication.
|
|
||||||
# Avoid top-level statements.
|
|
||||||
# Prefer nim scripts whenever possible.
|
|
||||||
# functions starting with `_` are considered internal, less stable.
|
|
||||||
|
|
||||||
echo_run () {
|
|
||||||
# echo's a command before running it, which helps understanding logs
|
|
||||||
echo ""
|
|
||||||
echo "cmd: $@" # in azure we could also use this: echo '##[section]"$@"'
|
|
||||||
"$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
nimGetLastCommit() {
|
|
||||||
git log --no-merges -1 --pretty=format:"%s"
|
|
||||||
}
|
|
||||||
|
|
||||||
nimIsCiSkip(){
|
|
||||||
# D20210329T004830:here refs https://github.com/microsoft/azure-pipelines-agent/issues/2944
|
|
||||||
# `--no-merges` is needed to avoid merge commits which occur for PR's.
|
|
||||||
# $(Build.SourceVersionMessage) is not helpful
|
|
||||||
# nor is `github.event.head_commit.message` for github actions.
|
|
||||||
# Note: `[skip ci]` is now handled automatically for github actions, see https://github.blog/changelog/2021-02-08-github-actions-skip-pull-request-and-push-workflows-with-skip-ci/
|
|
||||||
commitMsg=$(nimGetLastCommit)
|
|
||||||
echo commitMsg: "$commitMsg"
|
|
||||||
if [[ $commitMsg == *"[skip ci]"* ]]; then
|
|
||||||
echo "skipci: true"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo "skipci: false"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
nimInternalInstallDepsWindows(){
|
|
||||||
echo_run mkdir dist
|
|
||||||
echo_run curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z
|
|
||||||
echo_run curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip
|
|
||||||
echo_run 7z x dist/mingw64.7z -odist
|
|
||||||
echo_run 7z x dist/dlls.zip -obin
|
|
||||||
}
|
|
||||||
|
|
||||||
nimInternalBuildKochAndRunCI(){
|
|
||||||
echo_run nim c koch
|
|
||||||
if ! echo_run ./koch runCI; then
|
|
||||||
echo_run echo "runCI failed"
|
|
||||||
echo_run nim r tools/ci_testresults.nim
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
nimDefineVars(){
|
|
||||||
. config/build_config.txt
|
|
||||||
nim_csources=bin/nim_csources_$nim_csourcesHash
|
|
||||||
}
|
|
||||||
|
|
||||||
_nimNumCpu(){
|
|
||||||
# linux: $(nproc)
|
|
||||||
# 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
_nimBuildCsourcesIfNeeded(){
|
|
||||||
# if some systems cannot use make or gmake, we could add support for calling `build.sh`
|
|
||||||
# but this is slower (not parallel jobs) and would require making build.sh
|
|
||||||
# understand the arguments passed to the makefile (e.g. `CC=gcc ucpu=amd64 uos=darwin`),
|
|
||||||
# instead of `--cpu amd64 --os darwin`.
|
|
||||||
unamestr=$(uname)
|
|
||||||
# uname values: https://en.wikipedia.org/wiki/Uname
|
|
||||||
if [ "$unamestr" = 'FreeBSD' ]; then
|
|
||||||
makeX=gmake
|
|
||||||
elif [ "$unamestr" = 'OpenBSD' ]; then
|
|
||||||
makeX=gmake
|
|
||||||
elif [ "$unamestr" = 'NetBSD' ]; then
|
|
||||||
makeX=gmake
|
|
||||||
elif [ "$unamestr" = 'CROSSOS' ]; then
|
|
||||||
makeX=gmake
|
|
||||||
elif [ "$unamestr" = 'SunOS' ]; then
|
|
||||||
makeX=gmake
|
|
||||||
else
|
|
||||||
makeX=make
|
|
||||||
fi
|
|
||||||
nCPU=$(_nimNumCpu)
|
|
||||||
echo_run which $makeX
|
|
||||||
# parallel jobs (5X faster on 16 cores: 10s instead of 50s)
|
|
||||||
echo_run $makeX -C $nim_csourcesDir -j $((nCPU + 2)) -l $nCPU "$@"
|
|
||||||
# keep $nim_csources in case needed to investigate bootstrap issues
|
|
||||||
# without having to rebuild
|
|
||||||
echo_run cp bin/nim $nim_csources
|
|
||||||
}
|
|
||||||
|
|
||||||
nimCiSystemInfo(){
|
|
||||||
nimDefineVars
|
|
||||||
echo_run eval echo '$'nim_csources
|
|
||||||
echo_run pwd
|
|
||||||
echo_run date
|
|
||||||
echo_run uname -a
|
|
||||||
echo_run git log --no-merges -1 --pretty=oneline
|
|
||||||
echo_run eval echo '$'PATH
|
|
||||||
echo_run gcc -v
|
|
||||||
echo_run node -v
|
|
||||||
echo_run make -v
|
|
||||||
}
|
|
||||||
|
|
||||||
nimCsourcesHash(){
|
|
||||||
nimDefineVars
|
|
||||||
echo $nim_csourcesHash
|
|
||||||
}
|
|
||||||
|
|
||||||
nimBuildCsourcesIfNeeded(){
|
|
||||||
# goal: allow cachine each tagged version independently
|
|
||||||
# to avoid rebuilding, so that tools like `git bisect`
|
|
||||||
# can grab a cached past version without rebuilding.
|
|
||||||
nimDefineVars
|
|
||||||
(
|
|
||||||
set -e
|
|
||||||
# avoid polluting caller scope with internal variable definitions.
|
|
||||||
if test -f "$nim_csources"; then
|
|
||||||
echo "$nim_csources exists."
|
|
||||||
else
|
|
||||||
if test -d "$nim_csourcesDir"; then
|
|
||||||
echo "$nim_csourcesDir exists."
|
|
||||||
else
|
|
||||||
# Note: using git tags would allow fetching just what's needed, unlike git hashes, e.g.
|
|
||||||
# via `git clone -q --depth 1 --branch $tag $nim_csourcesUrl`.
|
|
||||||
echo_run git clone -q --depth 1 -b $nim_csourcesBranch \
|
|
||||||
$nim_csourcesUrl "$nim_csourcesDir"
|
|
||||||
# old `git` versions don't support -C option, using `cd` explicitly:
|
|
||||||
echo_run cd "$nim_csourcesDir"
|
|
||||||
echo_run git checkout $nim_csourcesHash
|
|
||||||
echo_run cd "$OLDPWD"
|
|
||||||
# if needed we could also add: `git reset --hard $nim_csourcesHash`
|
|
||||||
fi
|
|
||||||
_nimBuildCsourcesIfNeeded "$@"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo_run rm -f bin/nim
|
|
||||||
# fixes bug #17913, but it's unclear why it's needed, maybe specific to MacOS Big Sur 11.3 on M1 arch?
|
|
||||||
echo_run cp $nim_csources bin/nim
|
|
||||||
echo_run $nim_csources -v
|
|
||||||
)
|
|
||||||
}
|
|
||||||
11
compiler.nimble
Normal file
11
compiler.nimble
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[Package]
|
||||||
|
name = "compiler"
|
||||||
|
version = "0.10.3"
|
||||||
|
author = "Andreas Rumpf"
|
||||||
|
description = "Compiler package providing the compiler sources as a library."
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
InstallDirs = "doc, compiler"
|
||||||
|
|
||||||
|
[Deps]
|
||||||
|
Requires: "nim >= 0.10.3"
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
|
|
||||||
import ast
|
|
||||||
|
|
||||||
import std / assertions
|
|
||||||
|
|
||||||
const
|
|
||||||
PathKinds0* = {nkDotExpr, nkCheckedFieldExpr,
|
|
||||||
nkBracketExpr, nkDerefExpr, nkHiddenDeref,
|
|
||||||
nkAddr, nkHiddenAddr,
|
|
||||||
nkObjDownConv, nkObjUpConv}
|
|
||||||
PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv}
|
|
||||||
|
|
||||||
proc skipConvDfa*(n: PNode): PNode =
|
|
||||||
result = n
|
|
||||||
while true:
|
|
||||||
case result.kind
|
|
||||||
of nkObjDownConv, nkObjUpConv:
|
|
||||||
result = result[0]
|
|
||||||
of PathKinds1:
|
|
||||||
result = result[1]
|
|
||||||
else: break
|
|
||||||
|
|
||||||
proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
|
|
||||||
var n = orig
|
|
||||||
while true:
|
|
||||||
case n.kind
|
|
||||||
of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
|
|
||||||
n = n[0]
|
|
||||||
of PathKinds1:
|
|
||||||
n = n[1]
|
|
||||||
of nkHiddenDeref, nkDerefExpr:
|
|
||||||
# We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
|
|
||||||
# pointer indirection.
|
|
||||||
# bug #14159, we cannot reason about sinkParam[].location as it can
|
|
||||||
# still be shared for tyRef.
|
|
||||||
n = n[0]
|
|
||||||
return n.kind == nkSym and n.sym.owner == owner and
|
|
||||||
(n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
|
|
||||||
else: break
|
|
||||||
# XXX Allow closure deref operations here if we know
|
|
||||||
# the owner controlled the closure allocation?
|
|
||||||
result = n.kind == nkSym and n.sym.owner == owner and
|
|
||||||
{sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
|
|
||||||
(n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
|
|
||||||
# Note: There is a different move analyzer possible that checks for
|
|
||||||
# consume(param.key); param.key = newValue for all paths. Then code like
|
|
||||||
#
|
|
||||||
# let splited = split(move self.root, x)
|
|
||||||
# self.root = merge(splited.lower, splited.greater)
|
|
||||||
#
|
|
||||||
# could be written without the ``move self.root``. However, this would be
|
|
||||||
# wrong! Then the write barrier for the ``self.root`` assignment would
|
|
||||||
# free the old data and all is lost! Lesson: Don't be too smart, trust the
|
|
||||||
# lower level C++ optimizer to specialize this code.
|
|
||||||
|
|
||||||
type AliasKind* = enum
|
|
||||||
yes, no, maybe
|
|
||||||
|
|
||||||
proc aliases*(obj, field: PNode): AliasKind =
|
|
||||||
# obj -> field:
|
|
||||||
# x -> x: true
|
|
||||||
# x -> x.f: true
|
|
||||||
# x.f -> x: false
|
|
||||||
# x.f -> x.f: true
|
|
||||||
# x.f -> x.v: false
|
|
||||||
# x -> x[]: true
|
|
||||||
# x[] -> x: false
|
|
||||||
# x -> x[0]: true
|
|
||||||
# x[0] -> x: false
|
|
||||||
# x[0] -> x[0]: true
|
|
||||||
# x[0] -> x[1]: false
|
|
||||||
# x -> x[i]: true
|
|
||||||
# x[i] -> x: false
|
|
||||||
# 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 n = n
|
|
||||||
while true:
|
|
||||||
case n.kind
|
|
||||||
of PathKinds0 - {nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref}:
|
|
||||||
n = n[0]
|
|
||||||
of PathKinds1:
|
|
||||||
n = n[1]
|
|
||||||
of nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref:
|
|
||||||
result.add n
|
|
||||||
n = n[0]
|
|
||||||
of nkSym:
|
|
||||||
result.add n
|
|
||||||
break
|
|
||||||
else: return no
|
|
||||||
|
|
||||||
collectImportantNodes(objImportantNodes, obj)
|
|
||||||
collectImportantNodes(fieldImportantNodes, field)
|
|
||||||
|
|
||||||
# If field is less nested than obj, then it cannot be part of/aliased by obj
|
|
||||||
if fieldImportantNodes.len < objImportantNodes.len: return no
|
|
||||||
|
|
||||||
result = yes
|
|
||||||
for i in 1..objImportantNodes.len:
|
|
||||||
# We compare the nodes leading to the location of obj and field
|
|
||||||
# with each other.
|
|
||||||
# We continue until they diverge, in which case we return no, or
|
|
||||||
# until we reach the location of obj, in which case we do not need
|
|
||||||
# to look further, since field must be part of/aliased by obj now.
|
|
||||||
# If we encounter an element access using an index which is a runtime value,
|
|
||||||
# we simply return maybe instead of yes; should further nodes not diverge.
|
|
||||||
let currFieldPath = fieldImportantNodes[^i]
|
|
||||||
let currObjPath = objImportantNodes[^i]
|
|
||||||
|
|
||||||
if currFieldPath.kind != currObjPath.kind:
|
|
||||||
return no
|
|
||||||
|
|
||||||
case currFieldPath.kind
|
|
||||||
of nkSym:
|
|
||||||
if currFieldPath.sym != currObjPath.sym: return no
|
|
||||||
of nkDotExpr:
|
|
||||||
if currFieldPath[1].sym != currObjPath[1].sym: return no
|
|
||||||
of nkDerefExpr, nkHiddenDeref:
|
|
||||||
discard
|
|
||||||
of nkBracketExpr:
|
|
||||||
if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
|
|
||||||
if currFieldPath[1].intVal != currObjPath[1].intVal:
|
|
||||||
return no
|
|
||||||
else:
|
|
||||||
result = maybe
|
|
||||||
else: assert false # unreachable
|
|
||||||
|
|
||||||
|
|
@ -10,11 +10,8 @@
|
||||||
## Simple alias analysis for the HLO and the code generators.
|
## Simple alias analysis for the HLO and the code generators.
|
||||||
|
|
||||||
import
|
import
|
||||||
ast, astalgo, types, trees, intsets
|
ast, astalgo, types, trees, intsets, msgs
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
TAnalysisResult* = enum
|
TAnalysisResult* = enum
|
||||||
arNo, arMaybe, arYes
|
arNo, arMaybe, arYes
|
||||||
|
|
@ -24,43 +21,42 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult
|
||||||
proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
|
proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
|
||||||
result = arNo
|
result = arNo
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for i in 0..<n.len:
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
result = isPartOfAux(n[i], b, marker)
|
result = isPartOfAux(n.sons[i], b, marker)
|
||||||
if result == arYes: return
|
if result == arYes: return
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
assert(n[0].kind == nkSym)
|
assert(n.sons[0].kind == nkSym)
|
||||||
result = isPartOfAux(n[0], b, marker)
|
result = isPartOfAux(n.sons[0], b, marker)
|
||||||
if result == arYes: return
|
if result == arYes: return
|
||||||
for i in 1..<n.len:
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
case n[i].kind
|
case n.sons[i].kind
|
||||||
of nkOfBranch, nkElse:
|
of nkOfBranch, nkElse:
|
||||||
result = isPartOfAux(lastSon(n[i]), b, marker)
|
result = isPartOfAux(lastSon(n.sons[i]), b, marker)
|
||||||
if result == arYes: return
|
if result == arYes: return
|
||||||
else: discard "isPartOfAux(record case branch)"
|
else: internalError("isPartOfAux(record case branch)")
|
||||||
of nkSym:
|
of nkSym:
|
||||||
result = isPartOfAux(n.sym.typ, b, marker)
|
result = isPartOfAux(n.sym.typ, b, marker)
|
||||||
else: discard
|
else: internalError(n.info, "isPartOfAux()")
|
||||||
|
|
||||||
proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
|
proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
|
||||||
result = arNo
|
result = arNo
|
||||||
if a == nil or b == nil: return
|
if a == nil or b == nil: return
|
||||||
if containsOrIncl(marker, a.id): return
|
if containsOrIncl(marker, a.id): return
|
||||||
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
|
if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
|
||||||
case a.kind
|
case a.kind
|
||||||
of tyObject:
|
of tyObject:
|
||||||
if a[0] != nil:
|
result = isPartOfAux(a.sons[0], b, marker)
|
||||||
result = isPartOfAux(a[0].skipTypes(skipPtrs), b, marker)
|
|
||||||
if result == arNo: result = isPartOfAux(a.n, b, marker)
|
if result == arNo: result = isPartOfAux(a.n, b, marker)
|
||||||
of tyGenericInst, tyDistinct, tyAlias, tySink:
|
of tyGenericInst, tyDistinct:
|
||||||
result = isPartOfAux(lastSon(a), b, marker)
|
result = isPartOfAux(lastSon(a), b, marker)
|
||||||
of tyArray, tySet, tyTuple:
|
of tyArray, tyArrayConstr, tySet, tyTuple:
|
||||||
for i in 0..<a.len:
|
for i in countup(0, sonsLen(a) - 1):
|
||||||
result = isPartOfAux(a[i], b, marker)
|
result = isPartOfAux(a.sons[i], b, marker)
|
||||||
if result == arYes: return
|
if result == arYes: return
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc isPartOf(a, b: PType): TAnalysisResult =
|
proc isPartOf(a, b: PType): TAnalysisResult =
|
||||||
## checks iff 'a' can be part of 'b'. Iterates over VALUE types!
|
## checks iff 'a' can be part of 'b'. Iterates over VALUE types!
|
||||||
var marker = initIntSet()
|
var marker = initIntSet()
|
||||||
# watch out: parameters reversed because I'm too lazy to change the code...
|
# watch out: parameters reversed because I'm too lazy to change the code...
|
||||||
|
|
@ -74,39 +70,34 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
## type. Since however type analysis is more expensive, we perform it only
|
## type. Since however type analysis is more expensive, we perform it only
|
||||||
## if necessary.
|
## if necessary.
|
||||||
##
|
##
|
||||||
## cases:
|
## cases:
|
||||||
##
|
##
|
||||||
## YES-cases:
|
## YES-cases:
|
||||||
## ```
|
|
||||||
## x <| x # for general trees
|
## x <| x # for general trees
|
||||||
## x[] <| x
|
## x[] <| x
|
||||||
## x[i] <| x
|
## x[i] <| x
|
||||||
## x.f <| x
|
## x.f <| x
|
||||||
## ```
|
##
|
||||||
##
|
|
||||||
## NO-cases:
|
## NO-cases:
|
||||||
## ```
|
|
||||||
## x !<| y # depending on type and symbol kind
|
## x !<| y # depending on type and symbol kind
|
||||||
## x[constA] !<| x[constB]
|
## x[constA] !<| x[constB]
|
||||||
## x.f !<| x.g
|
## x.f !<| x.g
|
||||||
## x.f !<| y.f iff x !<= y
|
## x.f !<| y.f iff x !<= y
|
||||||
## ```
|
|
||||||
##
|
##
|
||||||
## MAYBE-cases:
|
## MAYBE-cases:
|
||||||
##
|
##
|
||||||
## ```
|
|
||||||
## x[] ?<| y[] iff compatible type
|
## x[] ?<| y[] iff compatible type
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
## x[] ?<| y depending on type
|
## x[] ?<| y depending on type
|
||||||
## ```
|
##
|
||||||
if a.kind == b.kind:
|
if a.kind == b.kind:
|
||||||
case a.kind
|
case a.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
const varKinds = {skVar, skTemp, skProc, skFunc}
|
const varKinds = {skVar, skTemp, skProc}
|
||||||
# same symbol: aliasing:
|
# same symbol: aliasing:
|
||||||
if a.sym.id == b.sym.id: result = arYes
|
if a.sym.id == b.sym.id: result = arYes
|
||||||
elif a.sym.kind in varKinds or b.sym.kind in varKinds:
|
elif a.sym.kind in varKinds or b.sym.kind in varKinds:
|
||||||
# actually, a param could alias a var but we know that cannot happen
|
# actually, a param could alias a var but we know that cannot happen
|
||||||
# here. XXX make this more generic
|
# here. XXX make this more generic
|
||||||
result = arNo
|
result = arNo
|
||||||
|
|
@ -116,14 +107,14 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
result = arMaybe
|
result = arMaybe
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
result = isPartOf(a[0], b[0])
|
result = isPartOf(a[0], b[0])
|
||||||
if a.len >= 2 and b.len >= 2:
|
if len(a) >= 2 and len(b) >= 2:
|
||||||
# array accesses:
|
# array accesses:
|
||||||
if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
|
if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]):
|
||||||
# we know it's the same array and we have 2 constant indexes;
|
# we know it's the same array and we have 2 constant indexes;
|
||||||
# if they are
|
# if they are
|
||||||
var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1]
|
var x = if a[1].kind == nkHiddenStdConv: a[1][1] else: a[1]
|
||||||
var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1]
|
var y = if b[1].kind == nkHiddenStdConv: b[1][1] else: b[1]
|
||||||
|
|
||||||
if sameValue(x, y): result = arYes
|
if sameValue(x, y): result = arYes
|
||||||
else: result = arNo
|
else: result = arNo
|
||||||
# else: maybe and no are accurate
|
# else: maybe and no are accurate
|
||||||
|
|
@ -131,7 +122,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
# pointer derefs:
|
# pointer derefs:
|
||||||
if result != arYes:
|
if result != arYes:
|
||||||
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
|
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
|
||||||
|
|
||||||
of nkDotExpr:
|
of nkDotExpr:
|
||||||
result = isPartOf(a[0], b[0])
|
result = isPartOf(a[0], b[0])
|
||||||
if result != arNo:
|
if result != arNo:
|
||||||
|
|
@ -144,7 +135,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
# weaken because of indirection:
|
# weaken because of indirection:
|
||||||
if result != arYes:
|
if result != arYes:
|
||||||
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
|
if isPartOf(a.typ, b.typ) != arNo: result = arMaybe
|
||||||
|
|
||||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||||
result = isPartOf(a[1], b[1])
|
result = isPartOf(a[1], b[1])
|
||||||
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||||
|
|
@ -153,26 +144,26 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
# Calls return a new location, so a default of ``arNo`` is fine.
|
# Calls return a new location, so a default of ``arNo`` is fine.
|
||||||
else:
|
else:
|
||||||
# go down recursively; this is quite demanding:
|
# go down recursively; this is quite demanding:
|
||||||
const
|
const
|
||||||
Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
|
Ix0Kinds = {nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
|
||||||
nkCheckedFieldExpr, nkHiddenAddr}
|
nkCheckedFieldExpr}
|
||||||
Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv}
|
Ix1Kinds = {nkHiddenStdConv, nkHiddenSubConv, nkConv}
|
||||||
DerefKinds = {nkHiddenDeref, nkDerefExpr}
|
DerefKinds = {nkHiddenDeref, nkDerefExpr}
|
||||||
case b.kind
|
case b.kind
|
||||||
of Ix0Kinds:
|
of Ix0Kinds:
|
||||||
# a* !<| b.f iff a* !<| b
|
# a* !<| b.f iff a* !<| b
|
||||||
result = isPartOf(a, b[0])
|
result = isPartOf(a, b[0])
|
||||||
|
|
||||||
of DerefKinds:
|
of DerefKinds:
|
||||||
# a* !<| b[] iff
|
# a* !<| b[] iff
|
||||||
if isPartOf(a.typ, b.typ) != arNo:
|
if isPartOf(a.typ, b.typ) != arNo:
|
||||||
result = isPartOf(a, b[0])
|
result = isPartOf(a, b[0])
|
||||||
if result == arNo: result = arMaybe
|
if result == arNo: result = arMaybe
|
||||||
|
|
||||||
of Ix1Kinds:
|
of Ix1Kinds:
|
||||||
# a* !<| T(b) iff a* !<| b
|
# a* !<| T(b) iff a* !<| b
|
||||||
result = isPartOf(a, b[1])
|
result = isPartOf(a, b[1])
|
||||||
|
|
||||||
of nkSym:
|
of nkSym:
|
||||||
# b is an atom, so we have to check a:
|
# b is an atom, so we have to check a:
|
||||||
case a.kind
|
case a.kind
|
||||||
|
|
@ -181,27 +172,11 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
result = isPartOf(a[0], b)
|
result = isPartOf(a[0], b)
|
||||||
of Ix1Kinds:
|
of Ix1Kinds:
|
||||||
result = isPartOf(a[1], b)
|
result = isPartOf(a[1], b)
|
||||||
|
|
||||||
of DerefKinds:
|
of DerefKinds:
|
||||||
if isPartOf(a.typ, b.typ) != arNo:
|
if isPartOf(a.typ, b.typ) != arNo:
|
||||||
result = isPartOf(a[0], b)
|
result = isPartOf(a[0], b)
|
||||||
if result == arNo: result = arMaybe
|
if result == arNo: result = arMaybe
|
||||||
else: discard
|
else: discard
|
||||||
of nkObjConstr:
|
|
||||||
result = arNo
|
|
||||||
for i in 1..<b.len:
|
|
||||||
let res = isPartOf(a, b[i][1])
|
|
||||||
if res != arNo:
|
|
||||||
result = res
|
|
||||||
if res == arYes: break
|
|
||||||
of nkCallKinds:
|
|
||||||
result = arNo
|
|
||||||
for i in 1..<b.len:
|
|
||||||
let res = isPartOf(a, b[i])
|
|
||||||
if res != arNo:
|
|
||||||
result = res
|
|
||||||
if res == arYes: break
|
|
||||||
of nkBracket:
|
|
||||||
if b.len > 0:
|
|
||||||
result = isPartOf(a, b[0])
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
|
|
|
||||||
1655
compiler/ast.nim
1655
compiler/ast.nim
File diff suppressed because it is too large
Load diff
1037
compiler/astalgo.nim
1037
compiler/astalgo.nim
File diff suppressed because it is too large
Load diff
|
|
@ -1,39 +0,0 @@
|
||||||
# this module avoids ast depending on msgs or vice versa
|
|
||||||
import std/strutils
|
|
||||||
import options, ast, msgs
|
|
||||||
|
|
||||||
proc typSym*(t: PType): PSym =
|
|
||||||
result = t.sym
|
|
||||||
if result == nil and t.kind == tyGenericInst: # this might need to be refined
|
|
||||||
result = t[0].sym
|
|
||||||
|
|
||||||
proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) =
|
|
||||||
result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)]
|
|
||||||
|
|
||||||
proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; sym: PSym) =
|
|
||||||
if optDeclaredLocs in conf.globalOptions and sym != nil:
|
|
||||||
addDeclaredLoc(result, conf, sym)
|
|
||||||
|
|
||||||
proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
|
||||||
# xxx figure out how to resolve `tyGenericParam`, e.g. for
|
|
||||||
# proc fn[T](a: T, b: T) = discard
|
|
||||||
# fn(1.1, "a")
|
|
||||||
let typ = typ.skipTypes(abstractInst + {tyStatic, tySequence, tyArray, tySet, tyUserTypeClassInst, tyVar, tyRef, tyPtr} - {tyRange})
|
|
||||||
result.add " [$1" % typ.kind.toHumanStr
|
|
||||||
if typ.sym != nil:
|
|
||||||
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
|
|
||||||
result.add "]"
|
|
||||||
|
|
||||||
proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) =
|
|
||||||
if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ)
|
|
||||||
|
|
||||||
template quoteExpr*(a: string): untyped =
|
|
||||||
## can be used for quoting expressions in error msgs.
|
|
||||||
"'" & a & "'"
|
|
||||||
|
|
||||||
proc genFieldDefect*(conf: ConfigRef, field: string, disc: PSym): string =
|
|
||||||
let obj = disc.owner.name.s # `types.typeToString` might be better, eg for generics
|
|
||||||
result = "field '$#' is not accessible for type '$#'" % [field, obj]
|
|
||||||
if optDeclaredLocs in conf.globalOptions:
|
|
||||||
result.add " [discriminant declared in $#]" % toFileLineCol(conf, disc.info)
|
|
||||||
result.add " using '$# = " % disc.name.s
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
import pragmas, options, ast, trees
|
|
||||||
|
|
||||||
proc pushBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
|
|
||||||
optionsStack.add options
|
|
||||||
|
|
||||||
proc popBackendOption(optionsStack: var seq[TOptions], options: var TOptions) =
|
|
||||||
options = optionsStack[^1]
|
|
||||||
optionsStack.setLen(optionsStack.len-1)
|
|
||||||
|
|
||||||
proc processPushBackendOption*(optionsStack: var seq[TOptions], options: var TOptions,
|
|
||||||
n: PNode, start: int) =
|
|
||||||
pushBackendOption(optionsStack, options)
|
|
||||||
for i in start..<n.len:
|
|
||||||
let it = n[i]
|
|
||||||
if it.kind in nkPragmaCallKinds and it.len == 2 and it[1].kind == nkIntLit:
|
|
||||||
let sw = whichPragma(it[0])
|
|
||||||
let opts = pragmaToOptions(sw)
|
|
||||||
if opts != {}:
|
|
||||||
if it[1].intVal != 0:
|
|
||||||
options.incl opts
|
|
||||||
else:
|
|
||||||
options.excl opts
|
|
||||||
|
|
||||||
template processPopBackendOption*(optionsStack: var seq[TOptions], options: var TOptions) =
|
|
||||||
popBackendOption(optionsStack, options)
|
|
||||||
|
|
@ -10,82 +10,62 @@
|
||||||
# this unit handles Nim sets; it implements bit sets
|
# this unit handles Nim sets; it implements bit sets
|
||||||
# the code here should be reused in the Nim standard library
|
# the code here should be reused in the Nim standard library
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
type
|
||||||
import std/assertions
|
TBitSet* = seq[int8] # we use byte here to avoid issues with
|
||||||
|
|
||||||
type
|
|
||||||
ElemType = byte
|
|
||||||
TBitSet* = seq[ElemType] # we use byte here to avoid issues with
|
|
||||||
# cross-compiling; uint would be more efficient
|
# cross-compiling; uint would be more efficient
|
||||||
# however
|
# however
|
||||||
const
|
|
||||||
ElemSize* = 8
|
|
||||||
One = ElemType(1)
|
|
||||||
Zero = ElemType(0)
|
|
||||||
|
|
||||||
template modElemSize(arg: untyped): untyped = arg and 7
|
const
|
||||||
template divElemSize(arg: untyped): untyped = arg shr 3
|
ElemSize* = sizeof(int8) * 8
|
||||||
|
|
||||||
proc bitSetIn*(x: TBitSet, e: BiggestInt): bool =
|
proc bitSetInit*(b: var TBitSet, length: int)
|
||||||
result = (x[int(e.divElemSize)] and (One shl e.modElemSize)) != Zero
|
proc bitSetUnion*(x: var TBitSet, y: TBitSet)
|
||||||
|
proc bitSetDiff*(x: var TBitSet, y: TBitSet)
|
||||||
|
proc bitSetSymDiff*(x: var TBitSet, y: TBitSet)
|
||||||
|
proc bitSetIntersect*(x: var TBitSet, y: TBitSet)
|
||||||
|
proc bitSetIncl*(x: var TBitSet, elem: BiggestInt)
|
||||||
|
proc bitSetExcl*(x: var TBitSet, elem: BiggestInt)
|
||||||
|
proc bitSetIn*(x: TBitSet, e: BiggestInt): bool
|
||||||
|
proc bitSetEquals*(x, y: TBitSet): bool
|
||||||
|
proc bitSetContains*(x, y: TBitSet): bool
|
||||||
|
# implementation
|
||||||
|
|
||||||
proc bitSetIncl*(x: var TBitSet, elem: BiggestInt) =
|
proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
|
||||||
|
result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) !=
|
||||||
|
toU8(0)
|
||||||
|
|
||||||
|
proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
|
||||||
assert(elem >= 0)
|
assert(elem >= 0)
|
||||||
x[int(elem.divElemSize)] = x[int(elem.divElemSize)] or
|
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or
|
||||||
(One shl elem.modElemSize)
|
toU8(int(1 shl (elem mod ElemSize)))
|
||||||
|
|
||||||
proc bitSetExcl*(x: var TBitSet, elem: BiggestInt) =
|
proc bitSetExcl(x: var TBitSet, elem: BiggestInt) =
|
||||||
x[int(elem.divElemSize)] = x[int(elem.divElemSize)] and
|
x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and
|
||||||
not(One shl elem.modElemSize)
|
not toU8(int(1 shl (elem mod ElemSize)))
|
||||||
|
|
||||||
proc bitSetInit*(b: var TBitSet, length: int) =
|
proc bitSetInit(b: var TBitSet, length: int) =
|
||||||
newSeq(b, length)
|
newSeq(b, length)
|
||||||
|
|
||||||
proc bitSetUnion*(x: var TBitSet, y: TBitSet) =
|
proc bitSetUnion(x: var TBitSet, y: TBitSet) =
|
||||||
for i in 0..high(x): x[i] = x[i] or y[i]
|
for i in countup(0, high(x)): x[i] = x[i] or y[i]
|
||||||
|
|
||||||
proc bitSetDiff*(x: var TBitSet, y: TBitSet) =
|
proc bitSetDiff(x: var TBitSet, y: TBitSet) =
|
||||||
for i in 0..high(x): x[i] = x[i] and not y[i]
|
for i in countup(0, high(x)): x[i] = x[i] and not y[i]
|
||||||
|
|
||||||
proc bitSetSymDiff*(x: var TBitSet, y: TBitSet) =
|
proc bitSetSymDiff(x: var TBitSet, y: TBitSet) =
|
||||||
for i in 0..high(x): x[i] = x[i] xor y[i]
|
for i in countup(0, high(x)): x[i] = x[i] xor y[i]
|
||||||
|
|
||||||
proc bitSetIntersect*(x: var TBitSet, y: TBitSet) =
|
proc bitSetIntersect(x: var TBitSet, y: TBitSet) =
|
||||||
for i in 0..high(x): x[i] = x[i] and y[i]
|
for i in countup(0, high(x)): x[i] = x[i] and y[i]
|
||||||
|
|
||||||
proc bitSetEquals*(x, y: TBitSet): bool =
|
proc bitSetEquals(x, y: TBitSet): bool =
|
||||||
for i in 0..high(x):
|
for i in countup(0, high(x)):
|
||||||
if x[i] != y[i]:
|
if x[i] != y[i]:
|
||||||
return false
|
return false
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc bitSetContains*(x, y: TBitSet): bool =
|
proc bitSetContains(x, y: TBitSet): bool =
|
||||||
for i in 0..high(x):
|
for i in countup(0, high(x)):
|
||||||
if (x[i] and not y[i]) != Zero:
|
if (x[i] and not y[i]) != int8(0):
|
||||||
return false
|
return false
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
# Number of set bits for all values of int8
|
|
||||||
const populationCount: array[uint8, uint8] = block:
|
|
||||||
var arr: array[uint8, uint8]
|
|
||||||
|
|
||||||
proc countSetBits(x: uint8): uint8 =
|
|
||||||
return
|
|
||||||
( x and 0b00000001'u8) +
|
|
||||||
((x and 0b00000010'u8) shr 1) +
|
|
||||||
((x and 0b00000100'u8) shr 2) +
|
|
||||||
((x and 0b00001000'u8) shr 3) +
|
|
||||||
((x and 0b00010000'u8) shr 4) +
|
|
||||||
((x and 0b00100000'u8) shr 5) +
|
|
||||||
((x and 0b01000000'u8) shr 6) +
|
|
||||||
((x and 0b10000000'u8) shr 7)
|
|
||||||
|
|
||||||
|
|
||||||
for it in low(uint8)..high(uint8):
|
|
||||||
arr[it] = countSetBits(cast[uint8](it))
|
|
||||||
|
|
||||||
arr
|
|
||||||
|
|
||||||
proc bitSetCard*(x: TBitSet): BiggestInt =
|
|
||||||
for it in x:
|
|
||||||
result.inc int(populationCount[it])
|
|
||||||
|
|
|
||||||
|
|
@ -1,179 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2018 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## BTree implementation with few features, but good enough for the
|
|
||||||
## Nim compiler's needs.
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
const
|
|
||||||
M = 512 # max children per B-tree node = M-1
|
|
||||||
# (must be even and greater than 2)
|
|
||||||
Mhalf = M div 2
|
|
||||||
|
|
||||||
type
|
|
||||||
Node[Key, Val] {.acyclic.} = ref object
|
|
||||||
entries: int
|
|
||||||
keys: array[M, Key]
|
|
||||||
case isInternal: bool
|
|
||||||
of false:
|
|
||||||
vals: array[M, Val]
|
|
||||||
of true:
|
|
||||||
links: array[M, Node[Key, Val]]
|
|
||||||
BTree*[Key, Val] = object
|
|
||||||
root: Node[Key, Val]
|
|
||||||
entries: int ## number of key-value pairs
|
|
||||||
|
|
||||||
proc initBTree*[Key, Val](): BTree[Key, Val] =
|
|
||||||
BTree[Key, Val](root: Node[Key, Val](entries: 0, isInternal: false))
|
|
||||||
|
|
||||||
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 =
|
|
||||||
var x = b.root
|
|
||||||
while x.isInternal:
|
|
||||||
for j in 0..<x.entries:
|
|
||||||
if j+1 == x.entries or less(key, x.keys[j+1]):
|
|
||||||
x = x.links[j]
|
|
||||||
break
|
|
||||||
assert(not x.isInternal)
|
|
||||||
for j in 0..<x.entries:
|
|
||||||
if eq(key, x.keys[j]): return x.vals[j]
|
|
||||||
|
|
||||||
proc contains*[Key, Val](b: BTree[Key, Val], key: Key): bool =
|
|
||||||
var x = b.root
|
|
||||||
while x.isInternal:
|
|
||||||
for j in 0..<x.entries:
|
|
||||||
if j+1 == x.entries or less(key, x.keys[j+1]):
|
|
||||||
x = x.links[j]
|
|
||||||
break
|
|
||||||
assert(not x.isInternal)
|
|
||||||
for j in 0..<x.entries:
|
|
||||||
if eq(key, x.keys[j]): return true
|
|
||||||
return false
|
|
||||||
|
|
||||||
proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
|
|
||||||
for j in 0..<Mhalf:
|
|
||||||
result.keys[j] = h.keys[Mhalf + j]
|
|
||||||
if h.isInternal:
|
|
||||||
for j in 0..<Mhalf:
|
|
||||||
result.links[j] = h.links[Mhalf + j]
|
|
||||||
else:
|
|
||||||
for j in 0..<Mhalf:
|
|
||||||
when defined(gcArc) or defined(gcOrc):
|
|
||||||
result.vals[j] = move h.vals[Mhalf + j]
|
|
||||||
else:
|
|
||||||
shallowCopy(result.vals[j], h.vals[Mhalf + j])
|
|
||||||
|
|
||||||
proc split[Key, Val](h: Node[Key, Val]): Node[Key, Val] =
|
|
||||||
## split node in half
|
|
||||||
result = Node[Key, Val](entries: Mhalf, isInternal: h.isInternal)
|
|
||||||
h.entries = Mhalf
|
|
||||||
copyHalf(h, result)
|
|
||||||
|
|
||||||
proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
|
|
||||||
#var t = Entry(key: key, val: val, next: nil)
|
|
||||||
var newKey = key
|
|
||||||
var j = 0
|
|
||||||
if not h.isInternal:
|
|
||||||
while j < h.entries:
|
|
||||||
if eq(key, h.keys[j]):
|
|
||||||
h.vals[j] = val
|
|
||||||
return
|
|
||||||
if less(key, h.keys[j]): break
|
|
||||||
inc j
|
|
||||||
for i in countdown(h.entries, j+1):
|
|
||||||
when defined(gcArc) or defined(gcOrc):
|
|
||||||
h.vals[i] = move h.vals[i-1]
|
|
||||||
else:
|
|
||||||
shallowCopy(h.vals[i], h.vals[i-1])
|
|
||||||
h.vals[j] = val
|
|
||||||
else:
|
|
||||||
var newLink: Node[Key, Val] = nil
|
|
||||||
while j < h.entries:
|
|
||||||
if j+1 == h.entries or less(key, h.keys[j+1]):
|
|
||||||
let u = insert(h.links[j], key, val)
|
|
||||||
inc j
|
|
||||||
if u == nil: return nil
|
|
||||||
newKey = u.keys[0]
|
|
||||||
newLink = u
|
|
||||||
break
|
|
||||||
inc j
|
|
||||||
for i in countdown(h.entries, j+1):
|
|
||||||
h.links[i] = h.links[i-1]
|
|
||||||
h.links[j] = newLink
|
|
||||||
|
|
||||||
for i in countdown(h.entries, j+1):
|
|
||||||
h.keys[i] = h.keys[i-1]
|
|
||||||
h.keys[j] = newKey
|
|
||||||
inc h.entries
|
|
||||||
return if h.entries < M: nil else: split(h)
|
|
||||||
|
|
||||||
proc add*[Key, Val](b: var BTree[Key, Val]; key: Key; val: Val) =
|
|
||||||
let u = insert(b.root, key, val)
|
|
||||||
inc b.entries
|
|
||||||
if u == nil: return
|
|
||||||
|
|
||||||
# need to split root
|
|
||||||
let t = Node[Key, Val](entries: 2, isInternal: true)
|
|
||||||
t.keys[0] = b.root.keys[0]
|
|
||||||
t.links[0] = b.root
|
|
||||||
t.keys[1] = u.keys[0]
|
|
||||||
t.links[1] = u
|
|
||||||
b.root = t
|
|
||||||
|
|
||||||
proc toString[Key, Val](h: Node[Key, Val], indent: string; result: var string) =
|
|
||||||
if not h.isInternal:
|
|
||||||
for j in 0..<h.entries:
|
|
||||||
result.add(indent)
|
|
||||||
result.add($h.keys[j] & " " & $h.vals[j] & "\n")
|
|
||||||
else:
|
|
||||||
for j in 0..<h.entries:
|
|
||||||
if j > 0: result.add(indent & "(" & $h.keys[j] & ")\n")
|
|
||||||
toString(h.links[j], indent & " ", result)
|
|
||||||
|
|
||||||
proc `$`[Key, Val](b: BTree[Key, Val]): string =
|
|
||||||
result = ""
|
|
||||||
toString(b.root, "", result)
|
|
||||||
|
|
||||||
proc hasNext*[Key, Val](b: BTree[Key, Val]; index: int): bool = index < b.entries
|
|
||||||
|
|
||||||
proc countSubTree[Key, Val](it: Node[Key, Val]): int =
|
|
||||||
if it.isInternal:
|
|
||||||
result = 0
|
|
||||||
for k in 0..<it.entries:
|
|
||||||
inc result, countSubTree(it.links[k])
|
|
||||||
else:
|
|
||||||
result = it.entries
|
|
||||||
|
|
||||||
proc next*[Key, Val](b: BTree[Key, Val]; index: int): (Key, Val, int) =
|
|
||||||
var it = b.root
|
|
||||||
var i = index
|
|
||||||
# navigate to the right leaf:
|
|
||||||
while it.isInternal:
|
|
||||||
var sum = 0
|
|
||||||
for k in 0..<it.entries:
|
|
||||||
let c = countSubTree(it.links[k])
|
|
||||||
inc sum, c
|
|
||||||
if sum > i:
|
|
||||||
it = it.links[k]
|
|
||||||
dec i, (sum - c)
|
|
||||||
break
|
|
||||||
result = (it.keys[i], it.vals[i], index+1)
|
|
||||||
|
|
||||||
iterator pairs*[Key, Val](b: BTree[Key, Val]): (Key, Val) =
|
|
||||||
var i = 0
|
|
||||||
while hasNext(b, i):
|
|
||||||
let (k, v, i2) = next(b, i)
|
|
||||||
i = i2
|
|
||||||
yield (k, v)
|
|
||||||
|
|
||||||
proc len*[Key, Val](b: BTree[Key, Val]): int {.inline.} = b.entries
|
|
||||||
416
compiler/canonicalizer.nim
Normal file
416
compiler/canonicalizer.nim
Normal file
|
|
@ -0,0 +1,416 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module implements the canonalization for the various caching mechanisms.
|
||||||
|
|
||||||
|
import strutils, db_sqlite, md5
|
||||||
|
|
||||||
|
var db: TDbConn
|
||||||
|
|
||||||
|
# We *hash* the relevant information into 128 bit hashes. This should be good
|
||||||
|
# enough to prevent any collisions.
|
||||||
|
|
||||||
|
type
|
||||||
|
TUid = distinct MD5Digest
|
||||||
|
|
||||||
|
# For name mangling we encode these hashes via a variant of base64 (called
|
||||||
|
# 'base64a') and prepend the *primary* identifier to ease the debugging pain.
|
||||||
|
# So a signature like:
|
||||||
|
#
|
||||||
|
# proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt)
|
||||||
|
#
|
||||||
|
# is mangled into:
|
||||||
|
# gABI_MTdmOWY5MTQ1MDcyNGQ3ZA
|
||||||
|
#
|
||||||
|
# This is a good compromise between correctness and brevity. ;-)
|
||||||
|
|
||||||
|
const
|
||||||
|
cb64 = [
|
||||||
|
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
|
||||||
|
"O", "P", "Q", "R", "S", "T" "U", "V", "W", "X", "Y", "Z",
|
||||||
|
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
|
||||||
|
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
||||||
|
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||||
|
"_A", "_B"]
|
||||||
|
|
||||||
|
proc toBase64a(s: cstring, len: int): string =
|
||||||
|
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||||
|
## `newline` is added.
|
||||||
|
result = newStringOfCap(((len + 2) div 3) * 4)
|
||||||
|
var i = 0
|
||||||
|
while i < s.len - 2:
|
||||||
|
let a = ord(s[i])
|
||||||
|
let b = ord(s[i+1])
|
||||||
|
let c = ord(s[i+2])
|
||||||
|
result.add cb64[a shr 2]
|
||||||
|
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||||
|
result.add cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||||
|
result.add cb64[c and 0x3F]
|
||||||
|
inc(i, 3)
|
||||||
|
if i < s.len-1:
|
||||||
|
let a = ord(s[i])
|
||||||
|
let b = ord(s[i+1])
|
||||||
|
result.add cb64[a shr 2]
|
||||||
|
result.add cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||||
|
result.add cb64[((b and 0x0F) shl 2)]
|
||||||
|
elif i < s.len:
|
||||||
|
let a = ord(s[i])
|
||||||
|
result.add cb64[a shr 2]
|
||||||
|
result.add cb64[(a and 3) shl 4]
|
||||||
|
|
||||||
|
proc toBase64a(u: TUid): string = toBase64a(cast[cstring](u), sizeof(u))
|
||||||
|
|
||||||
|
proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
|
||||||
|
|
||||||
|
proc hashSym(c: var MD5Context, s: PSym) =
|
||||||
|
if sfAnon in s.flags or s.kind == skGenericParam:
|
||||||
|
c &= ":anon"
|
||||||
|
else:
|
||||||
|
var it = s.owner
|
||||||
|
while it != nil:
|
||||||
|
hashSym(c, it)
|
||||||
|
c &= "."
|
||||||
|
it = s.owner
|
||||||
|
c &= s.name.s
|
||||||
|
|
||||||
|
proc hashTree(c: var MD5Context, n: PNode) =
|
||||||
|
if n == nil:
|
||||||
|
c &= "\255"
|
||||||
|
return
|
||||||
|
var k = n.kind
|
||||||
|
md5Update(c, cast[cstring](addr(k)), 1)
|
||||||
|
# we really must not hash line information. 'n.typ' is debatable but
|
||||||
|
# shouldn't be necessary for now and avoids potential infinite recursions.
|
||||||
|
case n.kind
|
||||||
|
of nkEmpty, nkNilLit, nkType: discard
|
||||||
|
of nkIdent:
|
||||||
|
c &= n.ident.s
|
||||||
|
of nkSym:
|
||||||
|
hashSym(c, n.sym)
|
||||||
|
of nkCharLit..nkUInt64Lit:
|
||||||
|
var v = n.intVal
|
||||||
|
md5Update(c, cast[cstring](addr(v)), sizeof(v))
|
||||||
|
of nkFloatLit..nkFloat64Lit:
|
||||||
|
var v = n.floatVal
|
||||||
|
md5Update(c, cast[cstring](addr(v)), sizeof(v))
|
||||||
|
of nkStrLit..nkTripleStrLit:
|
||||||
|
c &= n.strVal
|
||||||
|
else:
|
||||||
|
for i in 0.. <n.len: hashTree(c, n.sons[i])
|
||||||
|
|
||||||
|
proc hashType(c: var MD5Context, t: PType) =
|
||||||
|
# modelled after 'typeToString'
|
||||||
|
if t == nil:
|
||||||
|
c &= "\254"
|
||||||
|
return
|
||||||
|
|
||||||
|
var k = t.kind
|
||||||
|
md5Update(c, cast[cstring](addr(k)), 1)
|
||||||
|
|
||||||
|
if t.sym != nil and sfAnon notin t.sym.flags:
|
||||||
|
# t.n for literals, but not for e.g. objects!
|
||||||
|
if t.kind in {tyFloat, tyInt}: c.hashNode(t.n)
|
||||||
|
c.hashSym(t.sym)
|
||||||
|
|
||||||
|
case t.kind
|
||||||
|
of tyGenericBody, tyGenericInst, tyGenericInvocation:
|
||||||
|
for i in countup(0, sonsLen(t) -1 -ord(t.kind != tyGenericInvocation)):
|
||||||
|
c.hashType t.sons[i]
|
||||||
|
of tyUserTypeClass:
|
||||||
|
internalAssert t.sym != nil and t.sym.owner != nil
|
||||||
|
c &= t.sym.owner.name.s
|
||||||
|
of tyUserTypeClassInst:
|
||||||
|
let body = t.base
|
||||||
|
c.hashSym body.sym
|
||||||
|
for i in countup(1, sonsLen(t) - 2):
|
||||||
|
c.hashType t.sons[i]
|
||||||
|
of tyFromExpr, tyFieldAccessor:
|
||||||
|
c.hashTree(t.n)
|
||||||
|
of tyArrayConstr:
|
||||||
|
c.hashTree(t.sons[0].n)
|
||||||
|
c.hashType(t.sons[1])
|
||||||
|
of tyTuple:
|
||||||
|
if t.n != nil:
|
||||||
|
assert(sonsLen(t.n) == sonsLen(t))
|
||||||
|
for i in countup(0, sonsLen(t.n) - 1):
|
||||||
|
assert(t.n.sons[i].kind == nkSym)
|
||||||
|
c &= t.n.sons[i].sym.name.s
|
||||||
|
c &= ":"
|
||||||
|
c.hashType(t.sons[i])
|
||||||
|
c &= ","
|
||||||
|
else:
|
||||||
|
for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i]
|
||||||
|
of tyRange:
|
||||||
|
c.hashTree(t.n)
|
||||||
|
c.hashType(t.sons[0])
|
||||||
|
of tyProc:
|
||||||
|
c &= (if tfIterator in t.flags: "iterator " else: "proc ")
|
||||||
|
for i in 0.. <t.len: c.hashType(t.sons[i])
|
||||||
|
md5Update(c, cast[cstring](addr(t.callConv)), 1)
|
||||||
|
|
||||||
|
if tfNoSideEffect in t.flags: c &= ".noSideEffect"
|
||||||
|
if tfThread in t.flags: c &= ".thread"
|
||||||
|
else:
|
||||||
|
for i in 0.. <t.len: c.hashType(t.sons[i])
|
||||||
|
if tfShared in t.flags: c &= "shared"
|
||||||
|
if tfNotNil in t.flags: c &= "not nil"
|
||||||
|
|
||||||
|
proc canonConst(n: PNode): TUid =
|
||||||
|
var c: MD5Context
|
||||||
|
md5Init(c)
|
||||||
|
c.hashTree(n)
|
||||||
|
c.hashType(n.typ)
|
||||||
|
md5Final(c, MD5Digest(result))
|
||||||
|
|
||||||
|
proc canonSym(s: PSym): TUid =
|
||||||
|
var c: MD5Context
|
||||||
|
md5Init(c)
|
||||||
|
c.hashSym(s)
|
||||||
|
md5Final(c, MD5Digest(result))
|
||||||
|
|
||||||
|
proc pushType(w: PRodWriter, t: PType) =
|
||||||
|
# check so that the stack does not grow too large:
|
||||||
|
if iiTableGet(w.index.tab, t.id) == InvalidKey:
|
||||||
|
w.tstack.add(t)
|
||||||
|
|
||||||
|
proc pushSym(w: PRodWriter, s: PSym) =
|
||||||
|
# check so that the stack does not grow too large:
|
||||||
|
if iiTableGet(w.index.tab, s.id) == InvalidKey:
|
||||||
|
w.sstack.add(s)
|
||||||
|
|
||||||
|
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
|
||||||
|
result: var string) =
|
||||||
|
if n == nil:
|
||||||
|
# nil nodes have to be stored too:
|
||||||
|
result.add("()")
|
||||||
|
return
|
||||||
|
result.add('(')
|
||||||
|
encodeVInt(ord(n.kind), result)
|
||||||
|
# we do not write comments for now
|
||||||
|
# Line information takes easily 20% or more of the filesize! Therefore we
|
||||||
|
# omit line information if it is the same as the father's line information:
|
||||||
|
if fInfo.fileIndex != n.info.fileIndex:
|
||||||
|
result.add('?')
|
||||||
|
encodeVInt(n.info.col, result)
|
||||||
|
result.add(',')
|
||||||
|
encodeVInt(n.info.line, result)
|
||||||
|
result.add(',')
|
||||||
|
encodeVInt(fileIdx(w, toFilename(n.info)), result)
|
||||||
|
elif fInfo.line != n.info.line:
|
||||||
|
result.add('?')
|
||||||
|
encodeVInt(n.info.col, result)
|
||||||
|
result.add(',')
|
||||||
|
encodeVInt(n.info.line, result)
|
||||||
|
elif fInfo.col != n.info.col:
|
||||||
|
result.add('?')
|
||||||
|
encodeVInt(n.info.col, result)
|
||||||
|
var f = n.flags * PersistentNodeFlags
|
||||||
|
if f != {}:
|
||||||
|
result.add('$')
|
||||||
|
encodeVInt(cast[int32](f), result)
|
||||||
|
if n.typ != nil:
|
||||||
|
result.add('^')
|
||||||
|
encodeVInt(n.typ.id, result)
|
||||||
|
pushType(w, n.typ)
|
||||||
|
case n.kind
|
||||||
|
of nkCharLit..nkInt64Lit:
|
||||||
|
if n.intVal != 0:
|
||||||
|
result.add('!')
|
||||||
|
encodeVBiggestInt(n.intVal, result)
|
||||||
|
of nkFloatLit..nkFloat64Lit:
|
||||||
|
if n.floatVal != 0.0:
|
||||||
|
result.add('!')
|
||||||
|
encodeStr($n.floatVal, result)
|
||||||
|
of nkStrLit..nkTripleStrLit:
|
||||||
|
if n.strVal != "":
|
||||||
|
result.add('!')
|
||||||
|
encodeStr(n.strVal, result)
|
||||||
|
of nkIdent:
|
||||||
|
result.add('!')
|
||||||
|
encodeStr(n.ident.s, result)
|
||||||
|
of nkSym:
|
||||||
|
result.add('!')
|
||||||
|
encodeVInt(n.sym.id, result)
|
||||||
|
pushSym(w, n.sym)
|
||||||
|
else:
|
||||||
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
|
encodeNode(w, n.info, n.sons[i], result)
|
||||||
|
add(result, ')')
|
||||||
|
|
||||||
|
proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
|
||||||
|
var oldLen = result.len
|
||||||
|
result.add('<')
|
||||||
|
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
|
||||||
|
if loc.s != low(loc.s):
|
||||||
|
add(result, '*')
|
||||||
|
encodeVInt(ord(loc.s), result)
|
||||||
|
if loc.flags != {}:
|
||||||
|
add(result, '$')
|
||||||
|
encodeVInt(cast[int32](loc.flags), result)
|
||||||
|
if loc.t != nil:
|
||||||
|
add(result, '^')
|
||||||
|
encodeVInt(cast[int32](loc.t.id), result)
|
||||||
|
pushType(w, loc.t)
|
||||||
|
if loc.r != nil:
|
||||||
|
add(result, '!')
|
||||||
|
encodeStr($loc.r, result)
|
||||||
|
if loc.a != 0:
|
||||||
|
add(result, '?')
|
||||||
|
encodeVInt(loc.a, result)
|
||||||
|
if oldLen + 1 == result.len:
|
||||||
|
# no data was necessary, so remove the '<' again:
|
||||||
|
setLen(result, oldLen)
|
||||||
|
else:
|
||||||
|
add(result, '>')
|
||||||
|
|
||||||
|
proc encodeType(w: PRodWriter, t: PType, result: var string) =
|
||||||
|
if t == nil:
|
||||||
|
# nil nodes have to be stored too:
|
||||||
|
result.add("[]")
|
||||||
|
return
|
||||||
|
# we need no surrounding [] here because the type is in a line of its own
|
||||||
|
if t.kind == tyForward: internalError("encodeType: tyForward")
|
||||||
|
# for the new rodfile viewer we use a preceeding [ so that the data section
|
||||||
|
# can easily be disambiguated:
|
||||||
|
add(result, '[')
|
||||||
|
encodeVInt(ord(t.kind), result)
|
||||||
|
add(result, '+')
|
||||||
|
encodeVInt(t.id, result)
|
||||||
|
if t.n != nil:
|
||||||
|
encodeNode(w, unknownLineInfo(), t.n, result)
|
||||||
|
if t.flags != {}:
|
||||||
|
add(result, '$')
|
||||||
|
encodeVInt(cast[int32](t.flags), result)
|
||||||
|
if t.callConv != low(t.callConv):
|
||||||
|
add(result, '?')
|
||||||
|
encodeVInt(ord(t.callConv), result)
|
||||||
|
if t.owner != nil:
|
||||||
|
add(result, '*')
|
||||||
|
encodeVInt(t.owner.id, result)
|
||||||
|
pushSym(w, t.owner)
|
||||||
|
if t.sym != nil:
|
||||||
|
add(result, '&')
|
||||||
|
encodeVInt(t.sym.id, result)
|
||||||
|
pushSym(w, t.sym)
|
||||||
|
if t.size != - 1:
|
||||||
|
add(result, '/')
|
||||||
|
encodeVBiggestInt(t.size, result)
|
||||||
|
if t.align != 2:
|
||||||
|
add(result, '=')
|
||||||
|
encodeVInt(t.align, result)
|
||||||
|
encodeLoc(w, t.loc, result)
|
||||||
|
for i in countup(0, sonsLen(t) - 1):
|
||||||
|
if t.sons[i] == nil:
|
||||||
|
add(result, "^()")
|
||||||
|
else:
|
||||||
|
add(result, '^')
|
||||||
|
encodeVInt(t.sons[i].id, result)
|
||||||
|
pushType(w, t.sons[i])
|
||||||
|
|
||||||
|
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
|
||||||
|
add(result, '|')
|
||||||
|
encodeVInt(ord(lib.kind), result)
|
||||||
|
add(result, '|')
|
||||||
|
encodeStr($lib.name, result)
|
||||||
|
add(result, '|')
|
||||||
|
encodeNode(w, info, lib.path, result)
|
||||||
|
|
||||||
|
proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
|
||||||
|
if s == nil:
|
||||||
|
# nil nodes have to be stored too:
|
||||||
|
result.add("{}")
|
||||||
|
return
|
||||||
|
# we need no surrounding {} here because the symbol is in a line of its own
|
||||||
|
encodeVInt(ord(s.kind), result)
|
||||||
|
result.add('+')
|
||||||
|
encodeVInt(s.id, result)
|
||||||
|
result.add('&')
|
||||||
|
encodeStr(s.name.s, result)
|
||||||
|
if s.typ != nil:
|
||||||
|
result.add('^')
|
||||||
|
encodeVInt(s.typ.id, result)
|
||||||
|
pushType(w, s.typ)
|
||||||
|
result.add('?')
|
||||||
|
if s.info.col != -1'i16: encodeVInt(s.info.col, result)
|
||||||
|
result.add(',')
|
||||||
|
if s.info.line != -1'i16: encodeVInt(s.info.line, result)
|
||||||
|
result.add(',')
|
||||||
|
encodeVInt(fileIdx(w, toFilename(s.info)), result)
|
||||||
|
if s.owner != nil:
|
||||||
|
result.add('*')
|
||||||
|
encodeVInt(s.owner.id, result)
|
||||||
|
pushSym(w, s.owner)
|
||||||
|
if s.flags != {}:
|
||||||
|
result.add('$')
|
||||||
|
encodeVInt(cast[int32](s.flags), result)
|
||||||
|
if s.magic != mNone:
|
||||||
|
result.add('@')
|
||||||
|
encodeVInt(ord(s.magic), result)
|
||||||
|
if s.options != w.options:
|
||||||
|
result.add('!')
|
||||||
|
encodeVInt(cast[int32](s.options), result)
|
||||||
|
if s.position != 0:
|
||||||
|
result.add('%')
|
||||||
|
encodeVInt(s.position, result)
|
||||||
|
if s.offset != - 1:
|
||||||
|
result.add('`')
|
||||||
|
encodeVInt(s.offset, result)
|
||||||
|
encodeLoc(w, s.loc, result)
|
||||||
|
if s.annex != nil: encodeLib(w, s.annex, s.info, result)
|
||||||
|
if s.constraint != nil:
|
||||||
|
add(result, '#')
|
||||||
|
encodeNode(w, unknownLineInfo(), s.constraint, result)
|
||||||
|
# lazy loading will soon reload the ast lazily, so the ast needs to be
|
||||||
|
# the last entry of a symbol:
|
||||||
|
if s.ast != nil:
|
||||||
|
# we used to attempt to save space here by only storing a dummy AST if
|
||||||
|
# it is not necessary, but Nim's heavy compile-time evaluation features
|
||||||
|
# make that unfeasible nowadays:
|
||||||
|
encodeNode(w, s.info, s.ast, result)
|
||||||
|
|
||||||
|
|
||||||
|
proc createDb() =
|
||||||
|
db.exec(sql"""
|
||||||
|
create table if not exists Module(
|
||||||
|
id integer primary key,
|
||||||
|
name varchar(256) not null,
|
||||||
|
fullpath varchar(256) not null,
|
||||||
|
interfHash varchar(256) not null,
|
||||||
|
fullHash varchar(256) not null,
|
||||||
|
|
||||||
|
created timestamp not null default (DATETIME('now')),
|
||||||
|
);""")
|
||||||
|
|
||||||
|
db.exec(sql"""
|
||||||
|
create table if not exists Symbol(
|
||||||
|
id integer primary key,
|
||||||
|
module integer not null,
|
||||||
|
name varchar(max) not null,
|
||||||
|
data varchar(max) not null,
|
||||||
|
created timestamp not null default (DATETIME('now')),
|
||||||
|
|
||||||
|
foreign key (module) references module(id)
|
||||||
|
);""")
|
||||||
|
|
||||||
|
db.exec(sql"""
|
||||||
|
create table if not exists Type(
|
||||||
|
id integer primary key,
|
||||||
|
module integer not null,
|
||||||
|
name varchar(max) not null,
|
||||||
|
data varchar(max) not null,
|
||||||
|
created timestamp not null default (DATETIME('now')),
|
||||||
|
|
||||||
|
foreign key (module) references module(id)
|
||||||
|
);""")
|
||||||
|
|
||||||
|
|
||||||
|
#db.exec(sql"""
|
||||||
|
# --create unique index if not exists TsstNameIx on TestResult(name);
|
||||||
|
# """, [])
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,118 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2018 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
# included from cgen.nim
|
|
||||||
|
|
||||||
## This include file contains the logic to produce constant string
|
|
||||||
## and seq literals. The code here is responsible that
|
|
||||||
## ``const x = ["a", "b"]`` works without hidden runtime creation code.
|
|
||||||
## The price is that seqs and strings are not purely a library
|
|
||||||
## implementation.
|
|
||||||
|
|
||||||
template detectVersion(field, corename) =
|
|
||||||
if m.g.field == 0:
|
|
||||||
let core = getCompilerProc(m.g.graph, corename)
|
|
||||||
if core == nil or core.kind != skConst:
|
|
||||||
m.g.field = 1
|
|
||||||
else:
|
|
||||||
m.g.field = toInt(ast.getInt(core.astdef))
|
|
||||||
result = m.g.field
|
|
||||||
|
|
||||||
proc detectStrVersion(m: BModule): int =
|
|
||||||
detectVersion(strVersion, "nimStrVersion")
|
|
||||||
|
|
||||||
proc detectSeqVersion(m: BModule): int =
|
|
||||||
detectVersion(seqVersion, "nimSeqVersion")
|
|
||||||
|
|
||||||
# ----- Version 1: GC'ed strings and seqs --------------------------------
|
|
||||||
|
|
||||||
proc genStringLiteralDataOnlyV1(m: BModule, s: string; result: var Rope) =
|
|
||||||
cgsym(m, "TGenericSeq")
|
|
||||||
let tmp = getTempName(m)
|
|
||||||
result.add tmp
|
|
||||||
m.s[cfsStrData].addf("STRING_LITERAL($1, $2, $3);$n",
|
|
||||||
[tmp, makeCString(s), rope(s.len)])
|
|
||||||
|
|
||||||
proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
|
|
||||||
if s.isNil:
|
|
||||||
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
|
|
||||||
else:
|
|
||||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
|
||||||
if id == m.labels:
|
|
||||||
# string literal not found in the cache:
|
|
||||||
appcg(m, result, "((#NimStringDesc*) &", [])
|
|
||||||
genStringLiteralDataOnlyV1(m, n.strVal, result)
|
|
||||||
result.add ")"
|
|
||||||
else:
|
|
||||||
appcg(m, result, "((#NimStringDesc*) &$1$2)",
|
|
||||||
[m.tmpBase, id])
|
|
||||||
|
|
||||||
# ------ Version 2: destructor based strings and seqs -----------------------
|
|
||||||
|
|
||||||
proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) =
|
|
||||||
m.s[cfsStrData].addf("static $4 struct {$n" &
|
|
||||||
" NI cap; NIM_CHAR data[$2+1];$n" &
|
|
||||||
"} $1 = { $2 | NIM_STRLIT_FLAG, $3 };$n",
|
|
||||||
[result, rope(s.len), makeCString(s),
|
|
||||||
rope(if isConst: "const" else: "")])
|
|
||||||
|
|
||||||
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
|
||||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
|
||||||
if id == m.labels:
|
|
||||||
let pureLit = getTempName(m)
|
|
||||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
|
||||||
let tmp = getTempName(m)
|
|
||||||
result.add tmp
|
|
||||||
cgsym(m, "NimStrPayload")
|
|
||||||
cgsym(m, "NimStringV2")
|
|
||||||
# string literal not found in the cache:
|
|
||||||
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
|
||||||
[tmp, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")])
|
|
||||||
else:
|
|
||||||
let tmp = getTempName(m)
|
|
||||||
result.add tmp
|
|
||||||
m.s[cfsStrData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
|
||||||
[tmp, rope(n.strVal.len), m.tmpBase & rope(id),
|
|
||||||
rope(if isConst: "const" else: "")])
|
|
||||||
|
|
||||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
|
||||||
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
|
|
||||||
var pureLit: Rope
|
|
||||||
if id == m.labels:
|
|
||||||
pureLit = getTempName(m)
|
|
||||||
cgsym(m, "NimStrPayload")
|
|
||||||
cgsym(m, "NimStringV2")
|
|
||||||
# string literal not found in the cache:
|
|
||||||
genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst)
|
|
||||||
else:
|
|
||||||
pureLit = m.tmpBase & rope(id)
|
|
||||||
result.addf "{$1, (NimStrPayload*)&$2}", [rope(n.strVal.len), pureLit]
|
|
||||||
|
|
||||||
# ------ Version selector ---------------------------------------------------
|
|
||||||
|
|
||||||
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
|
||||||
isConst: bool; result: var Rope) =
|
|
||||||
case detectStrVersion(m)
|
|
||||||
of 0, 1: genStringLiteralDataOnlyV1(m, s, result)
|
|
||||||
of 2:
|
|
||||||
let tmp = getTempName(m)
|
|
||||||
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
|
|
||||||
result.add tmp
|
|
||||||
else:
|
|
||||||
localError(m.config, info, "cannot determine how to produce code for string literal")
|
|
||||||
|
|
||||||
proc genNilStringLiteral(m: BModule; info: TLineInfo; result: var Rope) =
|
|
||||||
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
|
|
||||||
|
|
||||||
proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
|
|
||||||
case detectStrVersion(m)
|
|
||||||
of 0, 1: genStringLiteralV1(m, n, result)
|
|
||||||
of 2: genStringLiteralV2(m, n, isConst = true, result)
|
|
||||||
else:
|
|
||||||
localError(m.config, n.info, "cannot determine how to produce code for string literal")
|
|
||||||
298
compiler/ccgmerge.nim
Normal file
298
compiler/ccgmerge.nim
Normal file
|
|
@ -0,0 +1,298 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module implements the merge operation of 2 different C files. This
|
||||||
|
## is needed for incremental compilation.
|
||||||
|
|
||||||
|
import
|
||||||
|
ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils,
|
||||||
|
intsets, platform, llstream
|
||||||
|
|
||||||
|
# Careful! Section marks need to contain a tabulator so that they cannot
|
||||||
|
# be part of C string literals.
|
||||||
|
|
||||||
|
const
|
||||||
|
CFileSectionNames: array[TCFileSection, string] = [
|
||||||
|
cfsMergeInfo: "",
|
||||||
|
cfsHeaders: "NIM_merge_HEADERS",
|
||||||
|
cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
|
||||||
|
cfsTypes: "NIM_merge_TYPES",
|
||||||
|
cfsSeqTypes: "NIM_merge_SEQ_TYPES",
|
||||||
|
cfsFieldInfo: "NIM_merge_FIELD_INFO",
|
||||||
|
cfsTypeInfo: "NIM_merge_TYPE_INFO",
|
||||||
|
cfsProcHeaders: "NIM_merge_PROC_HEADERS",
|
||||||
|
cfsData: "NIM_merge_DATA",
|
||||||
|
cfsVars: "NIM_merge_VARS",
|
||||||
|
cfsProcs: "NIM_merge_PROCS",
|
||||||
|
cfsInitProc: "NIM_merge_INIT_PROC",
|
||||||
|
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
|
||||||
|
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
|
||||||
|
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
|
||||||
|
cfsDebugInit: "NIM_merge_DEBUG_INIT",
|
||||||
|
cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
|
||||||
|
cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
|
||||||
|
]
|
||||||
|
CProcSectionNames: array[TCProcSection, string] = [
|
||||||
|
cpsLocals: "NIM_merge_PROC_LOCALS",
|
||||||
|
cpsInit: "NIM_merge_PROC_INIT",
|
||||||
|
cpsStmts: "NIM_merge_PROC_BODY"
|
||||||
|
]
|
||||||
|
NimMergeEndMark = "/*\tNIM_merge_END:*/"
|
||||||
|
|
||||||
|
proc genSectionStart*(fs: TCFileSection): Rope =
|
||||||
|
if compilationCachePresent:
|
||||||
|
result = rope(tnl)
|
||||||
|
add(result, "/*\t")
|
||||||
|
add(result, CFileSectionNames[fs])
|
||||||
|
add(result, ":*/")
|
||||||
|
add(result, tnl)
|
||||||
|
|
||||||
|
proc genSectionEnd*(fs: TCFileSection): Rope =
|
||||||
|
if compilationCachePresent:
|
||||||
|
result = rope(NimMergeEndMark & tnl)
|
||||||
|
|
||||||
|
proc genSectionStart*(ps: TCProcSection): Rope =
|
||||||
|
if compilationCachePresent:
|
||||||
|
result = rope(tnl)
|
||||||
|
add(result, "/*\t")
|
||||||
|
add(result, CProcSectionNames[ps])
|
||||||
|
add(result, ":*/")
|
||||||
|
add(result, tnl)
|
||||||
|
|
||||||
|
proc genSectionEnd*(ps: TCProcSection): Rope =
|
||||||
|
if compilationCachePresent:
|
||||||
|
result = rope(NimMergeEndMark & tnl)
|
||||||
|
|
||||||
|
proc writeTypeCache(a: TIdTable, s: var string) =
|
||||||
|
var i = 0
|
||||||
|
for id, value in pairs(a):
|
||||||
|
if i == 10:
|
||||||
|
i = 0
|
||||||
|
s.add(tnl)
|
||||||
|
else:
|
||||||
|
s.add(' ')
|
||||||
|
encodeVInt(id, s)
|
||||||
|
s.add(':')
|
||||||
|
encodeStr($Rope(value), s)
|
||||||
|
inc i
|
||||||
|
s.add('}')
|
||||||
|
|
||||||
|
proc writeIntSet(a: IntSet, s: var string) =
|
||||||
|
var i = 0
|
||||||
|
for x in items(a):
|
||||||
|
if i == 10:
|
||||||
|
i = 0
|
||||||
|
s.add(tnl)
|
||||||
|
else:
|
||||||
|
s.add(' ')
|
||||||
|
encodeVInt(x, s)
|
||||||
|
inc i
|
||||||
|
s.add('}')
|
||||||
|
|
||||||
|
proc genMergeInfo*(m: BModule): Rope =
|
||||||
|
if optSymbolFiles notin gGlobalOptions: return nil
|
||||||
|
var s = "/*\tNIM_merge_INFO:"
|
||||||
|
s.add(tnl)
|
||||||
|
s.add("typeCache:{")
|
||||||
|
writeTypeCache(m.typeCache, s)
|
||||||
|
s.add("declared:{")
|
||||||
|
writeIntSet(m.declaredThings, s)
|
||||||
|
s.add("typeInfo:{")
|
||||||
|
writeIntSet(m.typeInfoMarker, s)
|
||||||
|
s.add("labels:")
|
||||||
|
encodeVInt(m.labels, s)
|
||||||
|
s.add(" hasframe:")
|
||||||
|
encodeVInt(ord(m.frameDeclared), s)
|
||||||
|
s.add(tnl)
|
||||||
|
s.add("*/")
|
||||||
|
result = s.rope
|
||||||
|
|
||||||
|
template `^`(pos: int): expr = L.buf[pos]
|
||||||
|
|
||||||
|
proc skipWhite(L: var TBaseLexer) =
|
||||||
|
var pos = L.bufpos
|
||||||
|
while true:
|
||||||
|
case ^pos
|
||||||
|
of CR: pos = nimlexbase.handleCR(L, pos)
|
||||||
|
of LF: pos = nimlexbase.handleLF(L, pos)
|
||||||
|
of ' ': inc pos
|
||||||
|
else: break
|
||||||
|
L.bufpos = pos
|
||||||
|
|
||||||
|
proc skipUntilCmd(L: var TBaseLexer) =
|
||||||
|
var pos = L.bufpos
|
||||||
|
while true:
|
||||||
|
case ^pos
|
||||||
|
of CR: pos = nimlexbase.handleCR(L, pos)
|
||||||
|
of LF: pos = nimlexbase.handleLF(L, pos)
|
||||||
|
of '\0': break
|
||||||
|
of '/':
|
||||||
|
if ^(pos+1) == '*' and ^(pos+2) == '\t':
|
||||||
|
inc pos, 3
|
||||||
|
break
|
||||||
|
inc pos
|
||||||
|
else: inc pos
|
||||||
|
L.bufpos = pos
|
||||||
|
|
||||||
|
proc atEndMark(buf: cstring, pos: int): bool =
|
||||||
|
var s = 0
|
||||||
|
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
|
||||||
|
result = s == NimMergeEndMark.len
|
||||||
|
|
||||||
|
proc readVerbatimSection(L: var TBaseLexer): Rope =
|
||||||
|
var pos = L.bufpos
|
||||||
|
var buf = L.buf
|
||||||
|
var r = newStringOfCap(30_000)
|
||||||
|
while true:
|
||||||
|
case buf[pos]
|
||||||
|
of CR:
|
||||||
|
pos = nimlexbase.handleCR(L, pos)
|
||||||
|
buf = L.buf
|
||||||
|
r.add(tnl)
|
||||||
|
of LF:
|
||||||
|
pos = nimlexbase.handleLF(L, pos)
|
||||||
|
buf = L.buf
|
||||||
|
r.add(tnl)
|
||||||
|
of '\0':
|
||||||
|
internalError("ccgmerge: expected: " & NimMergeEndMark)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if atEndMark(buf, pos):
|
||||||
|
inc pos, NimMergeEndMark.len
|
||||||
|
break
|
||||||
|
r.add(buf[pos])
|
||||||
|
inc pos
|
||||||
|
L.bufpos = pos
|
||||||
|
result = r.rope
|
||||||
|
|
||||||
|
proc readKey(L: var TBaseLexer, result: var string) =
|
||||||
|
var pos = L.bufpos
|
||||||
|
var buf = L.buf
|
||||||
|
setLen(result, 0)
|
||||||
|
while buf[pos] in IdentChars:
|
||||||
|
result.add(buf[pos])
|
||||||
|
inc pos
|
||||||
|
if buf[pos] != ':': internalError("ccgmerge: ':' expected")
|
||||||
|
L.bufpos = pos + 1 # skip ':'
|
||||||
|
|
||||||
|
proc newFakeType(id: int): PType =
|
||||||
|
new(result)
|
||||||
|
result.id = id
|
||||||
|
|
||||||
|
proc readTypeCache(L: var TBaseLexer, result: var TIdTable) =
|
||||||
|
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
|
||||||
|
inc L.bufpos
|
||||||
|
while ^L.bufpos != '}':
|
||||||
|
skipWhite(L)
|
||||||
|
var key = decodeVInt(L.buf, L.bufpos)
|
||||||
|
if ^L.bufpos != ':': internalError("ccgmerge: ':' expected")
|
||||||
|
inc L.bufpos
|
||||||
|
var value = decodeStr(L.buf, L.bufpos)
|
||||||
|
# XXX little hack: we create a "fake" type object with the correct Id
|
||||||
|
# better would be to adapt the data structure to not even store the
|
||||||
|
# object as key, but only the Id
|
||||||
|
idTablePut(result, newFakeType(key), value.rope)
|
||||||
|
inc L.bufpos
|
||||||
|
|
||||||
|
proc readIntSet(L: var TBaseLexer, result: var IntSet) =
|
||||||
|
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
|
||||||
|
inc L.bufpos
|
||||||
|
while ^L.bufpos != '}':
|
||||||
|
skipWhite(L)
|
||||||
|
var key = decodeVInt(L.buf, L.bufpos)
|
||||||
|
result.incl(key)
|
||||||
|
inc L.bufpos
|
||||||
|
|
||||||
|
proc processMergeInfo(L: var TBaseLexer, m: BModule) =
|
||||||
|
var k = newStringOfCap("typeCache".len)
|
||||||
|
while true:
|
||||||
|
skipWhite(L)
|
||||||
|
if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
|
||||||
|
inc(L.bufpos, 2)
|
||||||
|
break
|
||||||
|
readKey(L, k)
|
||||||
|
case k
|
||||||
|
of "typeCache": readTypeCache(L, m.typeCache)
|
||||||
|
of "declared": readIntSet(L, m.declaredThings)
|
||||||
|
of "typeInfo": readIntSet(L, m.typeInfoMarker)
|
||||||
|
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
|
||||||
|
of "hasframe": m.frameDeclared = decodeVInt(L.buf, L.bufpos) != 0
|
||||||
|
else: internalError("ccgmerge: unknown key: " & k)
|
||||||
|
|
||||||
|
when not defined(nimhygiene):
|
||||||
|
{.pragma: inject.}
|
||||||
|
|
||||||
|
template withCFile(cfilename: string, body: stmt) {.immediate.} =
|
||||||
|
var s = llStreamOpen(cfilename, fmRead)
|
||||||
|
if s == nil: return
|
||||||
|
var L {.inject.}: TBaseLexer
|
||||||
|
openBaseLexer(L, s)
|
||||||
|
var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
|
||||||
|
while true:
|
||||||
|
skipUntilCmd(L)
|
||||||
|
if ^L.bufpos == '\0': break
|
||||||
|
body
|
||||||
|
closeBaseLexer(L)
|
||||||
|
|
||||||
|
proc readMergeInfo*(cfilename: string, m: BModule) =
|
||||||
|
## reads the merge meta information into `m`.
|
||||||
|
withCFile(cfilename):
|
||||||
|
readKey(L, k)
|
||||||
|
if k == "NIM_merge_INFO":
|
||||||
|
processMergeInfo(L, m)
|
||||||
|
break
|
||||||
|
|
||||||
|
type
|
||||||
|
TMergeSections = object
|
||||||
|
f: TCFileSections
|
||||||
|
p: TCProcSections
|
||||||
|
|
||||||
|
proc readMergeSections(cfilename: string, m: var TMergeSections) =
|
||||||
|
## reads the merge sections into `m`.
|
||||||
|
withCFile(cfilename):
|
||||||
|
readKey(L, k)
|
||||||
|
if k == "NIM_merge_INFO":
|
||||||
|
discard
|
||||||
|
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
|
||||||
|
inc(L.bufpos, 2)
|
||||||
|
# read back into section
|
||||||
|
skipWhite(L)
|
||||||
|
var verbatim = readVerbatimSection(L)
|
||||||
|
skipWhite(L)
|
||||||
|
var sectionA = CFileSectionNames.find(k)
|
||||||
|
if sectionA > 0 and sectionA <= high(TCFileSection).int:
|
||||||
|
m.f[TCFileSection(sectionA)] = verbatim
|
||||||
|
else:
|
||||||
|
var sectionB = CProcSectionNames.find(k)
|
||||||
|
if sectionB >= 0 and sectionB <= high(TCProcSection).int:
|
||||||
|
m.p[TCProcSection(sectionB)] = verbatim
|
||||||
|
else:
|
||||||
|
internalError("ccgmerge: unknown section: " & k)
|
||||||
|
else:
|
||||||
|
internalError("ccgmerge: '*/' expected")
|
||||||
|
|
||||||
|
proc mergeRequired*(m: BModule): bool =
|
||||||
|
for i in cfsHeaders..cfsProcs:
|
||||||
|
if m.s[i] != nil:
|
||||||
|
#echo "not empty: ", i, " ", m.s[i]
|
||||||
|
return true
|
||||||
|
for i in low(TCProcSection)..high(TCProcSection):
|
||||||
|
if m.initProc.s(i) != nil:
|
||||||
|
#echo "not empty: ", i, " ", m.initProc.s[i]
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc mergeFiles*(cfilename: string, m: BModule) =
|
||||||
|
## merges the C file with the old version on hard disc.
|
||||||
|
var old: TMergeSections
|
||||||
|
readMergeSections(cfilename, old)
|
||||||
|
# do the merge; old section before new section:
|
||||||
|
for i in low(TCFileSection)..high(TCFileSection):
|
||||||
|
m.s[i] = old.f[i] & m.s[i]
|
||||||
|
for i in low(TCProcSection)..high(TCProcSection):
|
||||||
|
m.initProc.s(i) = old.p[i] & m.initProc.s(i)
|
||||||
|
|
@ -1,283 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## This module implements the merge operation of 2 different C files. This
|
|
||||||
## is needed for incremental compilation.
|
|
||||||
|
|
||||||
import
|
|
||||||
ast, ropes, options, strutils, nimlexbase, cgendata, rodutils,
|
|
||||||
intsets, llstream, tables, modulegraphs, pathutils
|
|
||||||
|
|
||||||
# Careful! Section marks need to contain a tabulator so that they cannot
|
|
||||||
# be part of C string literals.
|
|
||||||
|
|
||||||
const
|
|
||||||
CFileSectionNames: array[TCFileSection, string] = [
|
|
||||||
cfsHeaders: "NIM_merge_HEADERS",
|
|
||||||
cfsFrameDefines: "NIM_merge_FRAME_DEFINES",
|
|
||||||
cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
|
|
||||||
cfsTypes: "NIM_merge_TYPES",
|
|
||||||
cfsSeqTypes: "NIM_merge_SEQ_TYPES",
|
|
||||||
cfsTypeInfo: "NIM_merge_TYPE_INFO",
|
|
||||||
cfsProcHeaders: "NIM_merge_PROC_HEADERS",
|
|
||||||
cfsData: "NIM_merge_DATA",
|
|
||||||
cfsVars: "NIM_merge_VARS",
|
|
||||||
cfsProcs: "NIM_merge_PROCS",
|
|
||||||
cfsInitProc: "NIM_merge_INIT_PROC",
|
|
||||||
cfsDatInitProc: "NIM_merge_DATINIT_PROC",
|
|
||||||
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
|
|
||||||
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
|
|
||||||
cfsDynLibInit: "NIM_merge_DYNLIB_INIT"
|
|
||||||
]
|
|
||||||
CProcSectionNames: array[TCProcSection, string] = [
|
|
||||||
cpsLocals: "NIM_merge_PROC_LOCALS",
|
|
||||||
cpsInit: "NIM_merge_PROC_INIT",
|
|
||||||
cpsStmts: "NIM_merge_PROC_BODY"
|
|
||||||
]
|
|
||||||
NimMergeEndMark = "/*\tNIM_merge_END:*/"
|
|
||||||
|
|
||||||
proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
|
|
||||||
# useful for debugging and only adds at most a few lines in each file
|
|
||||||
result.add("\n/* section: ")
|
|
||||||
result.add(CFileSectionNames[fs])
|
|
||||||
result.add(" */\n")
|
|
||||||
if compilationCachePresent(conf):
|
|
||||||
result = nil
|
|
||||||
result.add("\n/*\t")
|
|
||||||
result.add(CFileSectionNames[fs])
|
|
||||||
result.add(":*/\n")
|
|
||||||
|
|
||||||
proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
|
|
||||||
if compilationCachePresent(conf):
|
|
||||||
result = rope(NimMergeEndMark & "\n")
|
|
||||||
|
|
||||||
proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
|
|
||||||
if compilationCachePresent(conf):
|
|
||||||
result = rope("")
|
|
||||||
result.add("\n/*\t")
|
|
||||||
result.add(CProcSectionNames[ps])
|
|
||||||
result.add(":*/\n")
|
|
||||||
|
|
||||||
proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
|
|
||||||
if compilationCachePresent(conf):
|
|
||||||
result = rope(NimMergeEndMark & "\n")
|
|
||||||
|
|
||||||
proc writeTypeCache(a: TypeCache, s: var string) =
|
|
||||||
var i = 0
|
|
||||||
for id, value in pairs(a):
|
|
||||||
if i == 10:
|
|
||||||
i = 0
|
|
||||||
s.add('\L')
|
|
||||||
else:
|
|
||||||
s.add(' ')
|
|
||||||
encodeStr($id, s)
|
|
||||||
s.add(':')
|
|
||||||
encodeStr($value, s)
|
|
||||||
inc i
|
|
||||||
s.add('}')
|
|
||||||
|
|
||||||
proc writeIntSet(a: IntSet, s: var string) =
|
|
||||||
var i = 0
|
|
||||||
for x in items(a):
|
|
||||||
if i == 10:
|
|
||||||
i = 0
|
|
||||||
s.add('\L')
|
|
||||||
else:
|
|
||||||
s.add(' ')
|
|
||||||
encodeVInt(x, s)
|
|
||||||
inc i
|
|
||||||
s.add('}')
|
|
||||||
|
|
||||||
proc genMergeInfo*(m: BModule): Rope =
|
|
||||||
if not compilationCachePresent(m.config): return nil
|
|
||||||
var s = "/*\tNIM_merge_INFO:\n"
|
|
||||||
s.add("typeCache:{")
|
|
||||||
writeTypeCache(m.typeCache, s)
|
|
||||||
s.add("declared:{")
|
|
||||||
writeIntSet(m.declaredThings, s)
|
|
||||||
when false:
|
|
||||||
s.add("typeInfo:{")
|
|
||||||
writeIntSet(m.typeInfoMarker, s)
|
|
||||||
s.add("labels:")
|
|
||||||
encodeVInt(m.labels, s)
|
|
||||||
s.add(" flags:")
|
|
||||||
encodeVInt(cast[int](m.flags), s)
|
|
||||||
s.add("\n*/")
|
|
||||||
result = s.rope
|
|
||||||
|
|
||||||
template `^`(pos: int): untyped = L.buf[pos]
|
|
||||||
|
|
||||||
proc skipWhite(L: var TBaseLexer) =
|
|
||||||
var pos = L.bufpos
|
|
||||||
while true:
|
|
||||||
case ^pos
|
|
||||||
of CR: pos = nimlexbase.handleCR(L, pos)
|
|
||||||
of LF: pos = nimlexbase.handleLF(L, pos)
|
|
||||||
of ' ': inc pos
|
|
||||||
else: break
|
|
||||||
L.bufpos = pos
|
|
||||||
|
|
||||||
proc skipUntilCmd(L: var TBaseLexer) =
|
|
||||||
var pos = L.bufpos
|
|
||||||
while true:
|
|
||||||
case ^pos
|
|
||||||
of CR: pos = nimlexbase.handleCR(L, pos)
|
|
||||||
of LF: pos = nimlexbase.handleLF(L, pos)
|
|
||||||
of '\0': break
|
|
||||||
of '/':
|
|
||||||
if ^(pos+1) == '*' and ^(pos+2) == '\t':
|
|
||||||
inc pos, 3
|
|
||||||
break
|
|
||||||
inc pos
|
|
||||||
else: inc pos
|
|
||||||
L.bufpos = pos
|
|
||||||
|
|
||||||
proc atEndMark(buf: cstring, pos: int): bool =
|
|
||||||
var s = 0
|
|
||||||
while s < NimMergeEndMark.len and buf[pos+s] == NimMergeEndMark[s]: inc s
|
|
||||||
result = s == NimMergeEndMark.len
|
|
||||||
|
|
||||||
proc readVerbatimSection(L: var TBaseLexer): Rope =
|
|
||||||
var pos = L.bufpos
|
|
||||||
var r = newStringOfCap(30_000)
|
|
||||||
while true:
|
|
||||||
case L.buf[pos]
|
|
||||||
of CR:
|
|
||||||
pos = nimlexbase.handleCR(L, pos)
|
|
||||||
r.add('\L')
|
|
||||||
of LF:
|
|
||||||
pos = nimlexbase.handleLF(L, pos)
|
|
||||||
r.add('\L')
|
|
||||||
of '\0':
|
|
||||||
doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if atEndMark(L.buf, pos):
|
|
||||||
inc pos, NimMergeEndMark.len
|
|
||||||
break
|
|
||||||
r.add(L.buf[pos])
|
|
||||||
inc pos
|
|
||||||
L.bufpos = pos
|
|
||||||
result = r.rope
|
|
||||||
|
|
||||||
proc readKey(L: var TBaseLexer, result: var string) =
|
|
||||||
var pos = L.bufpos
|
|
||||||
setLen(result, 0)
|
|
||||||
while L.buf[pos] in IdentChars:
|
|
||||||
result.add(L.buf[pos])
|
|
||||||
inc pos
|
|
||||||
if L.buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected")
|
|
||||||
L.bufpos = pos + 1 # skip ':'
|
|
||||||
|
|
||||||
proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
|
|
||||||
if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
|
|
||||||
inc L.bufpos
|
|
||||||
while ^L.bufpos != '}':
|
|
||||||
skipWhite(L)
|
|
||||||
var key = decodeStr(L.buf, L.bufpos)
|
|
||||||
if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
|
|
||||||
inc L.bufpos
|
|
||||||
discard decodeStr(L.buf, L.bufpos)
|
|
||||||
inc L.bufpos
|
|
||||||
|
|
||||||
proc readIntSet(L: var TBaseLexer, result: var IntSet) =
|
|
||||||
if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
|
|
||||||
inc L.bufpos
|
|
||||||
while ^L.bufpos != '}':
|
|
||||||
skipWhite(L)
|
|
||||||
var key = decodeVInt(L.buf, L.bufpos)
|
|
||||||
result.incl(key)
|
|
||||||
inc L.bufpos
|
|
||||||
|
|
||||||
proc processMergeInfo(L: var TBaseLexer, m: BModule) =
|
|
||||||
var k = newStringOfCap("typeCache".len)
|
|
||||||
while true:
|
|
||||||
skipWhite(L)
|
|
||||||
if ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
|
|
||||||
inc(L.bufpos, 2)
|
|
||||||
break
|
|
||||||
readKey(L, k)
|
|
||||||
case k
|
|
||||||
of "typeCache": readTypeCache(L, m.typeCache)
|
|
||||||
of "declared": readIntSet(L, m.declaredThings)
|
|
||||||
of "typeInfo":
|
|
||||||
when false: readIntSet(L, m.typeInfoMarker)
|
|
||||||
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
|
|
||||||
of "flags":
|
|
||||||
m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
|
|
||||||
else: doAssert(false, "ccgmerge: unknown key: " & k)
|
|
||||||
|
|
||||||
template withCFile(cfilename: AbsoluteFile, body: untyped) =
|
|
||||||
var s = llStreamOpen(cfilename, fmRead)
|
|
||||||
if s == nil: return
|
|
||||||
var L {.inject.}: TBaseLexer
|
|
||||||
openBaseLexer(L, s)
|
|
||||||
var k {.inject.} = newStringOfCap("NIM_merge_FORWARD_TYPES".len)
|
|
||||||
while true:
|
|
||||||
skipUntilCmd(L)
|
|
||||||
if ^L.bufpos == '\0': break
|
|
||||||
body
|
|
||||||
closeBaseLexer(L)
|
|
||||||
|
|
||||||
proc readMergeInfo*(cfilename: AbsoluteFile, m: BModule) =
|
|
||||||
## reads the merge meta information into `m`.
|
|
||||||
withCFile(cfilename):
|
|
||||||
readKey(L, k)
|
|
||||||
if k == "NIM_merge_INFO":
|
|
||||||
processMergeInfo(L, m)
|
|
||||||
break
|
|
||||||
|
|
||||||
type
|
|
||||||
TMergeSections = object
|
|
||||||
f: TCFileSections
|
|
||||||
p: TCProcSections
|
|
||||||
|
|
||||||
proc readMergeSections(cfilename: AbsoluteFile, m: var TMergeSections) =
|
|
||||||
## reads the merge sections into `m`.
|
|
||||||
withCFile(cfilename):
|
|
||||||
readKey(L, k)
|
|
||||||
if k == "NIM_merge_INFO":
|
|
||||||
discard
|
|
||||||
elif ^L.bufpos == '*' and ^(L.bufpos+1) == '/':
|
|
||||||
inc(L.bufpos, 2)
|
|
||||||
# read back into section
|
|
||||||
skipWhite(L)
|
|
||||||
var verbatim = readVerbatimSection(L)
|
|
||||||
skipWhite(L)
|
|
||||||
var sectionA = CFileSectionNames.find(k)
|
|
||||||
if sectionA > 0 and sectionA <= high(TCFileSection).int:
|
|
||||||
m.f[TCFileSection(sectionA)] = verbatim
|
|
||||||
else:
|
|
||||||
var sectionB = CProcSectionNames.find(k)
|
|
||||||
if sectionB >= 0 and sectionB <= high(TCProcSection).int:
|
|
||||||
m.p[TCProcSection(sectionB)] = verbatim
|
|
||||||
else:
|
|
||||||
doAssert(false, "ccgmerge: unknown section: " & k)
|
|
||||||
else:
|
|
||||||
doAssert(false, "ccgmerge: '*/' expected")
|
|
||||||
|
|
||||||
proc mergeRequired*(m: BModule): bool =
|
|
||||||
for i in cfsHeaders..cfsProcs:
|
|
||||||
if m.s[i] != nil:
|
|
||||||
#echo "not empty: ", i, " ", m.s[i]
|
|
||||||
return true
|
|
||||||
for i in TCProcSection:
|
|
||||||
if m.initProc.s(i) != nil:
|
|
||||||
#echo "not empty: ", i, " ", m.initProc.s[i]
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc mergeFiles*(cfilename: AbsoluteFile, m: BModule) =
|
|
||||||
## merges the C file with the old version on hard disc.
|
|
||||||
var old: TMergeSections
|
|
||||||
readMergeSections(cfilename, old)
|
|
||||||
# do the merge; old section before new section:
|
|
||||||
for i in TCFileSection:
|
|
||||||
m.s[i] = old.f[i] & m.s[i]
|
|
||||||
for i in TCProcSection:
|
|
||||||
m.initProc.s(i) = old.p[i] & m.initProc.s(i)
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2020 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
# included from cgen.nim
|
|
||||||
|
|
||||||
## Code specialization instead of the old, incredibly slow 'genericReset'
|
|
||||||
## implementation.
|
|
||||||
|
|
||||||
proc specializeResetT(p: BProc, accessor: Rope, typ: PType)
|
|
||||||
|
|
||||||
proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
|
|
||||||
typ: PType) =
|
|
||||||
if n == nil: return
|
|
||||||
case n.kind
|
|
||||||
of nkRecList:
|
|
||||||
for i in 0..<n.len:
|
|
||||||
specializeResetN(p, accessor, n[i], typ)
|
|
||||||
of nkRecCase:
|
|
||||||
if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
|
|
||||||
let disc = n[0].sym
|
|
||||||
if disc.loc.r == "": fillObjectFields(p.module, typ)
|
|
||||||
if disc.loc.t == nil:
|
|
||||||
internalError(p.config, n.info, "specializeResetN()")
|
|
||||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
|
||||||
for i in 1..<n.len:
|
|
||||||
let branch = n[i]
|
|
||||||
assert branch.kind in {nkOfBranch, nkElse}
|
|
||||||
if branch.kind == nkOfBranch:
|
|
||||||
genCaseRange(p, branch)
|
|
||||||
else:
|
|
||||||
lineF(p, cpsStmts, "default:$n", [])
|
|
||||||
specializeResetN(p, accessor, lastSon(branch), typ)
|
|
||||||
lineF(p, cpsStmts, "break;$n", [])
|
|
||||||
lineF(p, cpsStmts, "} $n", [])
|
|
||||||
specializeResetT(p, "$1.$2" % [accessor, disc.loc.r], disc.loc.t)
|
|
||||||
of nkSym:
|
|
||||||
let field = n.sym
|
|
||||||
if field.typ.kind == tyVoid: return
|
|
||||||
if field.loc.r == "": fillObjectFields(p.module, typ)
|
|
||||||
if field.loc.t == nil:
|
|
||||||
internalError(p.config, n.info, "specializeResetN()")
|
|
||||||
specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
|
||||||
else: internalError(p.config, n.info, "specializeResetN()")
|
|
||||||
|
|
||||||
proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
|
||||||
if typ == nil: return
|
|
||||||
|
|
||||||
case typ.kind
|
|
||||||
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
|
|
||||||
tySink, tyOwned:
|
|
||||||
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)
|
|
||||||
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])
|
|
||||||
lineF(p, cpsStmts, "}$n", [])
|
|
||||||
of tyObject:
|
|
||||||
for i in 0..<typ.len:
|
|
||||||
var x = typ[i]
|
|
||||||
if x != nil: x = x.skipTypes(skipPtrs)
|
|
||||||
specializeResetT(p, accessor.parentObj(p.module), x)
|
|
||||||
if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
|
|
||||||
of tyTuple:
|
|
||||||
let typ = getUniqueType(typ)
|
|
||||||
for i in 0..<typ.len:
|
|
||||||
specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), typ[i])
|
|
||||||
|
|
||||||
of tyString, tyRef, tySequence:
|
|
||||||
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
|
|
||||||
|
|
||||||
of tyProc:
|
|
||||||
if typ.callConv == ccClosure:
|
|
||||||
lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1.ClE_0, NIM_NIL);$n", [accessor])
|
|
||||||
lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
|
|
||||||
else:
|
|
||||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
|
||||||
of tyChar, tyBool, tyEnum, tyInt..tyUInt64:
|
|
||||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
|
||||||
of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
|
|
||||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
|
||||||
of tySet:
|
|
||||||
case mapSetType(p.config, typ)
|
|
||||||
of ctArray:
|
|
||||||
lineCg(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
|
|
||||||
[accessor, getTypeDesc(p.module, typ)])
|
|
||||||
of ctInt8, ctInt16, ctInt32, ctInt64:
|
|
||||||
lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
|
|
||||||
else:
|
|
||||||
doAssert false, "unexpected set type kind"
|
|
||||||
of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
|
|
||||||
tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs,
|
|
||||||
tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
|
||||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
|
|
||||||
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}:
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc specializeReset(p: BProc, a: TLoc) =
|
|
||||||
specializeResetT(p, rdLoc(a), a.t)
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -7,53 +7,59 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Thread var support for architectures that lack native support for
|
## Thread var support for crappy architectures that lack native support for
|
||||||
## thread local storage.
|
## thread local storage. (**Thank you Mac OS X!**)
|
||||||
|
|
||||||
# included from cgen.nim
|
# included from cgen.nim
|
||||||
|
|
||||||
proc emulatedThreadVars(conf: ConfigRef): bool =
|
proc emulatedThreadVars(): bool =
|
||||||
result = {optThreads, optTlsEmulation} <= conf.globalOptions
|
result = {optThreads, optTlsEmulation} <= gGlobalOptions
|
||||||
|
|
||||||
proc accessThreadLocalVar(p: BProc, s: PSym) =
|
proc accessThreadLocalVar(p: BProc, s: PSym) =
|
||||||
if emulatedThreadVars(p.config) and threadVarAccessed notin p.flags:
|
if emulatedThreadVars() and not p.threadVarAccessed:
|
||||||
p.flags.incl threadVarAccessed
|
p.threadVarAccessed = true
|
||||||
incl p.module.flags, usesThreadVars
|
p.module.usesThreadVars = true
|
||||||
p.procSec(cpsLocals).addf("\tNimThreadVars* NimTV_;$n", [])
|
addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV;$n", [])
|
||||||
p.procSec(cpsInit).add(
|
add(p.procSec(cpsInit),
|
||||||
ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n", []))
|
ropecg(p.module, "\tNimTV = (NimThreadVars*) #GetThreadLocalVars();$n"))
|
||||||
|
|
||||||
|
var
|
||||||
|
nimtv: Rope # nimrod thread vars; the struct body
|
||||||
|
nimtvDeps: seq[PType] = @[] # type deps: every module needs whole struct
|
||||||
|
nimtvDeclared = initIntSet() # so that every var/field exists only once
|
||||||
|
# in the struct
|
||||||
|
|
||||||
|
# 'nimtv' is incredibly hard to modularize! Best effort is to store all thread
|
||||||
|
# vars in a ROD section and with their type deps and load them
|
||||||
|
# unconditionally...
|
||||||
|
|
||||||
|
# nimtvDeps is VERY hard to cache because it's not a list of IDs nor can it be
|
||||||
|
# made to be one.
|
||||||
|
|
||||||
proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
||||||
if emulatedThreadVars(m.config):
|
if emulatedThreadVars():
|
||||||
# we gather all thread locals var into a struct; we need to allocate
|
# we gather all thread locals var into a struct; we need to allocate
|
||||||
# storage for that somehow, can't use the thread local storage
|
# storage for that somehow, can't use the thread local storage
|
||||||
# allocator for it :-(
|
# allocator for it :-(
|
||||||
if not containsOrIncl(m.g.nimtvDeclared, s.id):
|
if not containsOrIncl(nimtvDeclared, s.id):
|
||||||
m.g.nimtvDeps.add(s.loc.t)
|
nimtvDeps.add(s.loc.t)
|
||||||
m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
|
addf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
|
||||||
else:
|
else:
|
||||||
if isExtern: m.s[cfsVars].add("extern ")
|
if isExtern: add(m.s[cfsVars], "extern ")
|
||||||
elif lfExportLib in s.loc.flags: m.s[cfsVars].add("N_LIB_EXPORT_VAR ")
|
if optThreads in gGlobalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
|
||||||
else: m.s[cfsVars].add("N_LIB_PRIVATE ")
|
add(m.s[cfsVars], getTypeDesc(m, s.loc.t))
|
||||||
if optThreads in m.config.globalOptions:
|
addf(m.s[cfsVars], " $1;$n", [s.loc.r])
|
||||||
let sym = s.typ.sym
|
|
||||||
if sym != nil and sfCppNonPod in sym.flags:
|
|
||||||
m.s[cfsVars].add("NIM_THREAD_LOCAL ")
|
|
||||||
else: m.s[cfsVars].add("NIM_THREADVAR ")
|
|
||||||
m.s[cfsVars].add(getTypeDesc(m, s.loc.t))
|
|
||||||
m.s[cfsVars].addf(" $1;$n", [s.loc.r])
|
|
||||||
|
|
||||||
proc generateThreadLocalStorage(m: BModule) =
|
proc generateThreadLocalStorage(m: BModule) =
|
||||||
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
|
if nimtv != nil and (m.usesThreadVars or sfMainModule in m.module.flags):
|
||||||
for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
|
for t in items(nimtvDeps): discard getTypeDesc(m, t)
|
||||||
finishTypeDescriptions(m)
|
addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [nimtv])
|
||||||
m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
|
|
||||||
|
|
||||||
proc generateThreadVarsSize(m: BModule) =
|
proc generateThreadVarsSize(m: BModule) =
|
||||||
if m.g.nimtv != "":
|
if nimtv != nil:
|
||||||
let externc = if m.config.backend == backendCpp or
|
let externc = if gCmd != cmdCompileToCpp and
|
||||||
sfCompileToCpp in m.module.flags: "extern \"C\" "
|
sfCompileToCpp in m.module.flags: "extern \"C\""
|
||||||
else: ""
|
else: ""
|
||||||
m.s[cfsProcs].addf(
|
addf(m.s[cfsProcs],
|
||||||
"$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
|
"$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n",
|
||||||
[externc.rope])
|
[externc.rope])
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,8 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Generates traversal procs for the C backend.
|
## Generates traversal procs for the C backend. Traversal procs are only an
|
||||||
|
## optimization; the GC works without them too.
|
||||||
|
|
||||||
# included from cgen.nim
|
# included from cgen.nim
|
||||||
|
|
||||||
|
|
@ -16,46 +17,37 @@ type
|
||||||
p: BProc
|
p: BProc
|
||||||
visitorFrmt: string
|
visitorFrmt: string
|
||||||
|
|
||||||
const
|
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType)
|
||||||
visitorFrmt = "#nimGCvisit((void*)$1, $2);$n"
|
|
||||||
|
|
||||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
|
|
||||||
proc genCaseRange(p: BProc, branch: PNode)
|
proc genCaseRange(p: BProc, branch: PNode)
|
||||||
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false)
|
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false)
|
||||||
|
|
||||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, n: PNode) =
|
||||||
typ: PType) =
|
|
||||||
if n == nil: return
|
if n == nil: return
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for i in 0..<n.len:
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
genTraverseProc(c, accessor, n[i], typ)
|
genTraverseProc(c, accessor, n.sons[i])
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
|
if (n.sons[0].kind != nkSym): internalError(n.info, "genTraverseProc")
|
||||||
var p = c.p
|
var p = c.p
|
||||||
let disc = n[0].sym
|
let disc = n.sons[0].sym
|
||||||
if disc.loc.r == "": fillObjectFields(c.p.module, typ)
|
|
||||||
if disc.loc.t == nil:
|
|
||||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
|
||||||
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
|
||||||
for i in 1..<n.len:
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
let branch = n[i]
|
let branch = n.sons[i]
|
||||||
assert branch.kind in {nkOfBranch, nkElse}
|
assert branch.kind in {nkOfBranch, nkElse}
|
||||||
if branch.kind == nkOfBranch:
|
if branch.kind == nkOfBranch:
|
||||||
genCaseRange(c.p, branch)
|
genCaseRange(c.p, branch)
|
||||||
else:
|
else:
|
||||||
lineF(p, cpsStmts, "default:$n", [])
|
lineF(p, cpsStmts, "default:$n", [])
|
||||||
genTraverseProc(c, accessor, lastSon(branch), typ)
|
genTraverseProc(c, accessor, lastSon(branch))
|
||||||
lineF(p, cpsStmts, "break;$n", [])
|
lineF(p, cpsStmts, "break;$n", [])
|
||||||
lineF(p, cpsStmts, "} $n", [])
|
lineF(p, cpsStmts, "} $n", [])
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let field = n.sym
|
let field = n.sym
|
||||||
if field.typ.kind == tyVoid: return
|
|
||||||
if field.loc.r == "": fillObjectFields(c.p.module, typ)
|
|
||||||
if field.loc.t == nil:
|
if field.loc.t == nil:
|
||||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
internalError(n.info, "genTraverseProc()")
|
||||||
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
||||||
else: internalError(c.p.config, n.info, "genTraverseProc()")
|
else: internalError(n.info, "genTraverseProc()")
|
||||||
|
|
||||||
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
|
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
|
||||||
if not m.compileToCpp:
|
if not m.compileToCpp:
|
||||||
|
|
@ -63,132 +55,93 @@ proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
|
||||||
else:
|
else:
|
||||||
result = accessor
|
result = accessor
|
||||||
|
|
||||||
proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType)
|
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) =
|
||||||
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
|
||||||
if typ == nil: return
|
if typ == nil: return
|
||||||
|
|
||||||
var p = c.p
|
var p = c.p
|
||||||
case typ.kind
|
case typ.kind
|
||||||
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
|
of tyGenericInst, tyGenericBody, tyTypeDesc:
|
||||||
tySink, tyOwned:
|
|
||||||
genTraverseProc(c, accessor, lastSon(typ))
|
genTraverseProc(c, accessor, lastSon(typ))
|
||||||
of tyArray:
|
of tyArrayConstr, tyArray:
|
||||||
let arraySize = lengthOrd(c.p.config, typ[0])
|
let arraySize = lengthOrd(typ.sons[0])
|
||||||
var i: TLoc
|
var i: TLoc
|
||||||
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
|
getTemp(p, getSysType(tyInt), i)
|
||||||
var oldCode = p.s(cpsStmts)
|
|
||||||
freeze oldCode
|
|
||||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||||
[i.r, arraySize])
|
i.r, arraySize.rope)
|
||||||
let oldLen = p.s(cpsStmts).len
|
genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1])
|
||||||
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.r]), typ[1])
|
lineF(p, cpsStmts, "}$n", [])
|
||||||
if p.s(cpsStmts).len == oldLen:
|
|
||||||
# do not emit dummy long loops for faster debug builds:
|
|
||||||
p.s(cpsStmts) = oldCode
|
|
||||||
else:
|
|
||||||
lineF(p, cpsStmts, "}$n", [])
|
|
||||||
of tyObject:
|
of tyObject:
|
||||||
for i in 0..<typ.len:
|
for i in countup(0, sonsLen(typ) - 1):
|
||||||
var x = typ[i]
|
genTraverseProc(c, accessor.parentObj(c.p.module), typ.sons[i])
|
||||||
if x != nil: x = x.skipTypes(skipPtrs)
|
if typ.n != nil: genTraverseProc(c, accessor, typ.n)
|
||||||
genTraverseProc(c, accessor.parentObj(c.p.module), x)
|
|
||||||
if typ.n != nil: genTraverseProc(c, accessor, typ.n, typ)
|
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
let typ = getUniqueType(typ)
|
let typ = getUniqueType(typ)
|
||||||
for i in 0..<typ.len:
|
for i in countup(0, sonsLen(typ) - 1):
|
||||||
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), typ[i])
|
genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.rope), typ.sons[i])
|
||||||
of tyRef:
|
of tyRef, tyString, tySequence:
|
||||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
lineCg(p, cpsStmts, c.visitorFrmt, accessor)
|
||||||
of tySequence:
|
|
||||||
if optSeqDestructors notin c.p.module.config.globalOptions:
|
|
||||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
|
||||||
elif containsGarbageCollectedRef(typ.lastSon):
|
|
||||||
# destructor based seqs are themselves not traced but their data is, if
|
|
||||||
# they contain a GC'ed type:
|
|
||||||
lineCg(p, cpsStmts, "#nimGCvisitSeq((void*)$1, $2);$n", [accessor, c.visitorFrmt])
|
|
||||||
#genTraverseProcSeq(c, accessor, typ)
|
|
||||||
of tyString:
|
|
||||||
if tfHasAsgn notin typ.flags:
|
|
||||||
lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
|
|
||||||
of tyProc:
|
of tyProc:
|
||||||
if typ.callConv == ccClosure:
|
if typ.callConv == ccClosure:
|
||||||
lineCg(p, cpsStmts, visitorFrmt, [ropecg(c.p.module, "$1.ClE_0", [accessor]), c.visitorFrmt])
|
lineCg(p, cpsStmts, c.visitorFrmt, rfmt(nil, "$1.ClEnv", accessor))
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) =
|
||||||
var p = c.p
|
var p = c.p
|
||||||
assert typ.kind == tySequence
|
assert typ.kind == tySequence
|
||||||
var i: TLoc
|
var i: TLoc
|
||||||
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
|
getTemp(p, getSysType(tyInt), i)
|
||||||
var oldCode = p.s(cpsStmts)
|
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n",
|
||||||
freeze oldCode
|
[i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")])
|
||||||
var a: TLoc
|
genTraverseProc(c, "$1->data[$2]" % [accessor, i.r], typ.sons[0])
|
||||||
a.r = accessor
|
lineF(p, cpsStmts, "}$n", [])
|
||||||
|
|
||||||
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): Rope =
|
||||||
[i.r, lenExpr(c.p, a)])
|
|
||||||
let oldLen = p.s(cpsStmts).len
|
|
||||||
genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ[0])
|
|
||||||
if p.s(cpsStmts).len == oldLen:
|
|
||||||
# do not emit dummy long loops for faster debug builds:
|
|
||||||
p.s(cpsStmts) = oldCode
|
|
||||||
else:
|
|
||||||
lineF(p, cpsStmts, "}$n", [])
|
|
||||||
|
|
||||||
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
|
||||||
var c: TTraversalClosure
|
var c: TTraversalClosure
|
||||||
var p = newProc(nil, m)
|
var p = newProc(nil, m)
|
||||||
result = "Marker_" & getTypeName(m, origTyp, sig)
|
result = getGlobalTempName()
|
||||||
let
|
|
||||||
hcrOn = m.hcrOn
|
|
||||||
typ = origTyp.skipTypes(abstractInstOwned)
|
|
||||||
markerName = if hcrOn: result & "_actual" else: result
|
|
||||||
header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [markerName]
|
|
||||||
t = getTypeDesc(m, typ)
|
|
||||||
|
|
||||||
|
case reason
|
||||||
|
of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
|
||||||
|
else: assert false
|
||||||
|
|
||||||
|
let header = "N_NIMCALL(void, $1)(void* p, NI op)" % [result]
|
||||||
|
|
||||||
|
let t = getTypeDesc(m, typ)
|
||||||
lineF(p, cpsLocals, "$1 a;$n", [t])
|
lineF(p, cpsLocals, "$1 a;$n", [t])
|
||||||
lineF(p, cpsInit, "a = ($1)p;$n", [t])
|
lineF(p, cpsInit, "a = ($1)p;$n", [t])
|
||||||
|
|
||||||
c.p = p
|
c.p = p
|
||||||
c.visitorFrmt = "op" # "#nimGCvisit((void*)$1, op);$n"
|
|
||||||
|
|
||||||
assert typ.kind != tyTypeDesc
|
assert typ.kind != tyTypeDesc
|
||||||
if typ.kind == tySequence:
|
if typ.kind == tySequence:
|
||||||
genTraverseProcSeq(c, "a".rope, typ)
|
genTraverseProcSeq(c, "a".rope, typ)
|
||||||
else:
|
else:
|
||||||
if skipTypes(typ[0], typedescInst+{tyOwned}).kind == tyArray:
|
if skipTypes(typ.sons[0], typedescInst).kind in {tyArrayConstr, tyArray}:
|
||||||
# C's arrays are broken beyond repair:
|
# C's arrays are broken beyond repair:
|
||||||
genTraverseProc(c, "a".rope, typ[0])
|
genTraverseProc(c, "a".rope, typ.sons[0])
|
||||||
else:
|
else:
|
||||||
genTraverseProc(c, "(*a)".rope, typ[0])
|
genTraverseProc(c, "(*a)".rope, typ.sons[0])
|
||||||
|
|
||||||
let generatedProc = "$1 {$n$2$3$4}\n" %
|
let generatedProc = "$1 {$n$2$3$4}$n" %
|
||||||
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
|
[header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
|
||||||
|
|
||||||
m.s[cfsProcHeaders].addf("$1;\n", [header])
|
m.s[cfsProcHeaders].addf("$1;$n", [header])
|
||||||
m.s[cfsProcs].add(generatedProc)
|
m.s[cfsProcs].add(generatedProc)
|
||||||
|
|
||||||
if hcrOn:
|
proc genTraverseProcForGlobal(m: BModule, s: PSym): Rope =
|
||||||
m.s[cfsProcHeaders].addf("N_NIMCALL_PTR(void, $1)(void*, NI);\n", [result])
|
discard genTypeInfo(m, s.loc.t)
|
||||||
m.s[cfsDynLibInit].addf("\t$1 = (N_NIMCALL_PTR(void, )(void*, NI)) hcrRegisterProc($3, \"$1\", (void*)$2);\n",
|
|
||||||
[result, markerName, getModuleDllPath(m)])
|
|
||||||
|
|
||||||
proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
|
|
||||||
discard genTypeInfoV1(m, s.loc.t, info)
|
|
||||||
|
|
||||||
var c: TTraversalClosure
|
var c: TTraversalClosure
|
||||||
var p = newProc(nil, m)
|
var p = newProc(nil, m)
|
||||||
var sLoc = rdLoc(s.loc)
|
var sLoc = s.loc.r
|
||||||
result = getTempName(m)
|
result = getGlobalTempName()
|
||||||
|
|
||||||
if sfThread in s.flags and emulatedThreadVars(m.config):
|
if sfThread in s.flags and emulatedThreadVars():
|
||||||
accessThreadLocalVar(p, s)
|
accessThreadLocalVar(p, s)
|
||||||
sLoc = "NimTV_->" & sLoc
|
sLoc = "NimTV->" & sLoc
|
||||||
|
|
||||||
c.visitorFrmt = "0" # "#nimGCvisit((void*)$1, 0);$n"
|
c.visitorFrmt = "#nimGCvisit((void*)$1, 0);$n"
|
||||||
c.p = p
|
c.p = p
|
||||||
let header = "static N_NIMCALL(void, $1)(void)" % [result]
|
let header = "N_NIMCALL(void, $1)()" % [result]
|
||||||
genTraverseProc(c, sLoc, s.loc.t)
|
genTraverseProc(c, sLoc, s.loc.t)
|
||||||
|
|
||||||
let generatedProc = "$1 {$n$2$3$4}$n" %
|
let generatedProc = "$1 {$n$2$3$4}$n" %
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -10,138 +10,203 @@
|
||||||
# This module declares some helpers for the C code generator.
|
# This module declares some helpers for the C code generator.
|
||||||
|
|
||||||
import
|
import
|
||||||
ast, types, hashes, strutils, msgs, wordrecg,
|
ast, astalgo, ropes, lists, hashes, strutils, types, msgs, wordrecg,
|
||||||
platform, trees, options, cgendata
|
platform, trees
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
|
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkStmtList:
|
of nkStmtList:
|
||||||
for i in 0..<n.len:
|
for i in 0 .. < n.len:
|
||||||
result = getPragmaStmt(n[i], w)
|
result = getPragmaStmt(n[i], w)
|
||||||
if result != nil: break
|
if result != nil: break
|
||||||
of nkPragma:
|
of nkPragma:
|
||||||
for i in 0..<n.len:
|
for i in 0 .. < n.len:
|
||||||
if whichPragma(n[i]) == w: return n[i]
|
if whichPragma(n[i]) == w: return n[i]
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
|
proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
|
||||||
result = getPragmaStmt(n, w) != nil
|
result = getPragmaStmt(n, w) != nil
|
||||||
|
|
||||||
proc hashString*(conf: ConfigRef; s: string): BiggestInt =
|
proc hashString*(s: string): BiggestInt =
|
||||||
# has to be the same algorithm as strmantle.hashString!
|
# has to be the same algorithm as system.hashString!
|
||||||
if CPU[conf.target.targetCPU].bit == 64:
|
if CPU[targetCPU].bit == 64:
|
||||||
# we have to use the same bitwidth
|
# we have to use the same bitwidth
|
||||||
# as the target CPU
|
# as the target CPU
|
||||||
var b = 0'u64
|
var b = 0'i64
|
||||||
for i in 0..<s.len:
|
for i in countup(0, len(s) - 1):
|
||||||
b = b + uint(s[i])
|
b = b +% ord(s[i])
|
||||||
b = b + (b shl 10)
|
b = b +% `shl`(b, 10)
|
||||||
b = b xor (b shr 6)
|
b = b xor `shr`(b, 6)
|
||||||
b = b + (b shl 3)
|
b = b +% `shl`(b, 3)
|
||||||
b = b xor (b shr 11)
|
b = b xor `shr`(b, 11)
|
||||||
b = b + (b shl 15)
|
b = b +% `shl`(b, 15)
|
||||||
result = cast[Hash](b)
|
result = b
|
||||||
else:
|
else:
|
||||||
var a = 0'u32
|
var a = 0'i32
|
||||||
for i in 0..<s.len:
|
for i in countup(0, len(s) - 1):
|
||||||
a = a + uint32(s[i])
|
a = a +% ord(s[i]).int32
|
||||||
a = a + (a shl 10)
|
a = a +% `shl`(a, 10'i32)
|
||||||
a = a xor (a shr 6)
|
a = a xor `shr`(a, 6'i32)
|
||||||
a = a + (a shl 3)
|
a = a +% `shl`(a, 3'i32)
|
||||||
a = a xor (a shr 11)
|
a = a xor `shr`(a, 11'i32)
|
||||||
a = a + (a shl 15)
|
a = a +% `shl`(a, 15'i32)
|
||||||
result = cast[Hash](uint(a))
|
result = a
|
||||||
|
|
||||||
template getUniqueType*(key: PType): PType = key
|
var
|
||||||
|
gTypeTable: array[TTypeKind, TIdTable]
|
||||||
|
gCanonicalTypes: array[TTypeKind, PType]
|
||||||
|
|
||||||
|
proc initTypeTables() =
|
||||||
|
for i in countup(low(TTypeKind), high(TTypeKind)): initIdTable(gTypeTable[i])
|
||||||
|
|
||||||
|
proc resetCaches* =
|
||||||
|
## XXX: fix that more properly
|
||||||
|
initTypeTables()
|
||||||
|
for i in low(gCanonicalTypes)..high(gCanonicalTypes):
|
||||||
|
gCanonicalTypes[i] = nil
|
||||||
|
|
||||||
|
when false:
|
||||||
|
proc echoStats*() =
|
||||||
|
for i in countup(low(TTypeKind), high(TTypeKind)):
|
||||||
|
echo i, " ", gTypeTable[i].counter
|
||||||
|
|
||||||
|
proc slowSearch(key: PType; k: TTypeKind): PType =
|
||||||
|
# tuples are quite horrible as C does not support them directly and
|
||||||
|
# tuple[string, string] is a (strange) subtype of
|
||||||
|
# tuple[nameA, nameB: string]. This bites us here, so we
|
||||||
|
# use 'sameBackendType' instead of 'sameType'.
|
||||||
|
if idTableHasObjectAsKey(gTypeTable[k], key): return key
|
||||||
|
for h in countup(0, high(gTypeTable[k].data)):
|
||||||
|
var t = PType(gTypeTable[k].data[h].key)
|
||||||
|
if t != nil and sameBackendType(t, key):
|
||||||
|
return t
|
||||||
|
idTablePut(gTypeTable[k], key, key)
|
||||||
|
result = key
|
||||||
|
|
||||||
|
proc getUniqueType*(key: PType): PType =
|
||||||
|
# this is a hotspot in the compiler!
|
||||||
|
if key == nil: return
|
||||||
|
var k = key.kind
|
||||||
|
case k
|
||||||
|
of tyBool, tyChar, tyInt..tyUInt64:
|
||||||
|
# no canonicalization for integral types, so that e.g. ``pid_t`` is
|
||||||
|
# produced instead of ``NI``.
|
||||||
|
result = key
|
||||||
|
of tyEmpty, tyNil, tyExpr, tyStmt, tyPointer, tyString,
|
||||||
|
tyCString, tyNone, tyBigNum:
|
||||||
|
result = gCanonicalTypes[k]
|
||||||
|
if result == nil:
|
||||||
|
gCanonicalTypes[k] = key
|
||||||
|
result = key
|
||||||
|
of tyTypeDesc, tyTypeClasses, tyGenericParam, tyFromExpr, tyFieldAccessor:
|
||||||
|
internalError("getUniqueType")
|
||||||
|
of tyDistinct:
|
||||||
|
if key.deepCopy != nil: result = key
|
||||||
|
else: result = getUniqueType(lastSon(key))
|
||||||
|
of tyGenericInst, tyOrdinal, tyMutable, tyConst, tyIter, tyStatic:
|
||||||
|
result = getUniqueType(lastSon(key))
|
||||||
|
#let obj = lastSon(key)
|
||||||
|
#if obj.sym != nil and obj.sym.name.s == "TOption":
|
||||||
|
# echo "for ", typeToString(key), " I returned "
|
||||||
|
# debug result
|
||||||
|
of tyPtr, tyRef, tyVar:
|
||||||
|
let elemType = lastSon(key)
|
||||||
|
if elemType.kind in {tyBool, tyChar, tyInt..tyUInt64}:
|
||||||
|
# no canonicalization for integral types, so that e.g. ``ptr pid_t`` is
|
||||||
|
# produced instead of ``ptr NI``.
|
||||||
|
result = key
|
||||||
|
else:
|
||||||
|
result = slowSearch(key, k)
|
||||||
|
of tyArrayConstr, tyGenericInvocation, tyGenericBody,
|
||||||
|
tyOpenArray, tyArray, tySet, tyRange, tyTuple,
|
||||||
|
tySequence, tyForward, tyVarargs, tyProxy:
|
||||||
|
# we have to do a slow linear search because types may need
|
||||||
|
# to be compared by their structure:
|
||||||
|
result = slowSearch(key, k)
|
||||||
|
of tyObject:
|
||||||
|
if tfFromGeneric notin key.flags:
|
||||||
|
# fast case; lookup per id suffices:
|
||||||
|
result = PType(idTableGet(gTypeTable[k], key))
|
||||||
|
if result == nil:
|
||||||
|
idTablePut(gTypeTable[k], key, key)
|
||||||
|
result = key
|
||||||
|
else:
|
||||||
|
# ugly slow case: need to compare by structure
|
||||||
|
if idTableHasObjectAsKey(gTypeTable[k], key): return key
|
||||||
|
for h in countup(0, high(gTypeTable[k].data)):
|
||||||
|
var t = PType(gTypeTable[k].data[h].key)
|
||||||
|
if t != nil and sameBackendType(t, key):
|
||||||
|
return t
|
||||||
|
idTablePut(gTypeTable[k], key, key)
|
||||||
|
result = key
|
||||||
|
of tyEnum:
|
||||||
|
result = PType(idTableGet(gTypeTable[k], key))
|
||||||
|
if result == nil:
|
||||||
|
idTablePut(gTypeTable[k], key, key)
|
||||||
|
result = key
|
||||||
|
of tyProc:
|
||||||
|
if key.callConv != ccClosure:
|
||||||
|
result = key
|
||||||
|
else:
|
||||||
|
# ugh, we need the canon here:
|
||||||
|
result = slowSearch(key, k)
|
||||||
|
|
||||||
|
proc tableGetType*(tab: TIdTable, key: PType): RootRef =
|
||||||
|
# returns nil if we need to declare this type
|
||||||
|
result = idTableGet(tab, key)
|
||||||
|
if (result == nil) and (tab.counter > 0):
|
||||||
|
# we have to do a slow linear search because types may need
|
||||||
|
# to be compared by their structure:
|
||||||
|
for h in countup(0, high(tab.data)):
|
||||||
|
var t = PType(tab.data[h].key)
|
||||||
|
if t != nil:
|
||||||
|
if sameType(t, key):
|
||||||
|
return tab.data[h].val
|
||||||
|
|
||||||
proc makeSingleLineCString*(s: string): string =
|
proc makeSingleLineCString*(s: string): string =
|
||||||
result = "\""
|
result = "\""
|
||||||
for c in items(s):
|
for c in items(s):
|
||||||
c.toCChar(result)
|
result.add(c.toCChar)
|
||||||
result.add('\"')
|
result.add('\"')
|
||||||
|
|
||||||
proc mangle*(name: string): string =
|
proc mangle*(name: string): string =
|
||||||
|
## Lowercases the given name and manges any non-alphanumeric characters
|
||||||
|
## so they are represented as `HEX____`. If the name starts with a number,
|
||||||
|
## `N` is prepended
|
||||||
result = newStringOfCap(name.len)
|
result = newStringOfCap(name.len)
|
||||||
var start = 0
|
case name[0]
|
||||||
if name[0] in Digits:
|
of Letters:
|
||||||
result.add("X" & name[0])
|
result.add(name[0])
|
||||||
start = 1
|
of Digits:
|
||||||
var requiresUnderscore = false
|
result.add("N" & name[0])
|
||||||
template special(x) =
|
else:
|
||||||
result.add x
|
result = "HEX" & toHex(ord(name[0]), 2)
|
||||||
requiresUnderscore = true
|
for i in 1..(name.len-1):
|
||||||
for i in start..<name.len:
|
|
||||||
let c = name[i]
|
let c = name[i]
|
||||||
case c
|
case c
|
||||||
of 'a'..'z', '0'..'9', 'A'..'Z':
|
of 'A'..'Z':
|
||||||
result.add(c)
|
add(result, c.toLower)
|
||||||
of '_':
|
of '_':
|
||||||
# we generate names like 'foo_9' for scope disambiguations and so
|
discard
|
||||||
# disallow this here:
|
of 'a'..'z', '0'..'9':
|
||||||
if i > 0 and i < name.len-1 and name[i+1] in Digits:
|
add(result, c)
|
||||||
discard
|
|
||||||
else:
|
|
||||||
result.add(c)
|
|
||||||
of '$': special "dollar"
|
|
||||||
of '%': special "percent"
|
|
||||||
of '&': special "amp"
|
|
||||||
of '^': special "roof"
|
|
||||||
of '!': special "emark"
|
|
||||||
of '?': special "qmark"
|
|
||||||
of '*': special "star"
|
|
||||||
of '+': special "plus"
|
|
||||||
of '-': special "minus"
|
|
||||||
of '/': special "slash"
|
|
||||||
of '\\': special "backslash"
|
|
||||||
of '=': special "eq"
|
|
||||||
of '<': special "lt"
|
|
||||||
of '>': special "gt"
|
|
||||||
of '~': special "tilde"
|
|
||||||
of ':': special "colon"
|
|
||||||
of '.': special "dot"
|
|
||||||
of '@': special "at"
|
|
||||||
of '|': special "bar"
|
|
||||||
else:
|
else:
|
||||||
result.add("X" & toHex(ord(c), 2))
|
add(result, "HEX" & toHex(ord(c), 2))
|
||||||
requiresUnderscore = true
|
|
||||||
if requiresUnderscore:
|
|
||||||
result.add "_"
|
|
||||||
|
|
||||||
proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
|
proc makeLLVMString*(s: string): Rope =
|
||||||
case int(getSize(conf, typ))
|
const MaxLineLength = 64
|
||||||
of 1: result = ctInt8
|
result = nil
|
||||||
of 2: result = ctInt16
|
var res = "c\""
|
||||||
of 4: result = ctInt32
|
for i in countup(0, len(s) - 1):
|
||||||
of 8: result = ctInt64
|
if (i + 1) mod MaxLineLength == 0:
|
||||||
else: result = ctArray
|
add(result, rope(res))
|
||||||
|
setLen(res, 0)
|
||||||
proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
|
case s[i]
|
||||||
var pt = skipTypes(s.typ, typedescInst)
|
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
|
||||||
assert skResult != s.kind
|
add(res, '\\')
|
||||||
|
add(res, toHex(ord(s[i]), 2))
|
||||||
if tfByRef in pt.flags: return true
|
else: add(res, s[i])
|
||||||
elif tfByCopy in pt.flags: return false
|
add(res, "\\00\"")
|
||||||
case pt.kind
|
add(result, rope(res))
|
||||||
of tyObject:
|
|
||||||
if s.typ.sym != nil and sfForward in s.typ.sym.flags:
|
|
||||||
# forwarded objects are *always* passed by pointers for consistency!
|
|
||||||
result = true
|
|
||||||
elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
|
|
||||||
result = true # requested anyway
|
|
||||||
elif (tfFinal in pt.flags) and (pt[0] == nil):
|
|
||||||
result = false # no need, because no subtyping possible
|
|
||||||
else:
|
|
||||||
result = true # ordinary objects are always passed by reference,
|
|
||||||
# otherwise casting doesn't work
|
|
||||||
of tyTuple:
|
|
||||||
result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options)
|
|
||||||
else:
|
|
||||||
result = false
|
|
||||||
# first parameter and return type is 'lent T'? --> use pass by pointer
|
|
||||||
if s.position == 0 and retType != nil and retType.kind == tyLent:
|
|
||||||
result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or
|
|
||||||
pt.kind == tySet and mapSetType(conf, pt) == ctArray)
|
|
||||||
|
|
||||||
|
initTypeTables()
|
||||||
|
|
|
||||||
2412
compiler/cgen.nim
2412
compiler/cgen.nim
File diff suppressed because it is too large
Load diff
|
|
@ -10,30 +10,34 @@
|
||||||
## This module contains the data structures for the C code generation phase.
|
## This module contains the data structures for the C code generation phase.
|
||||||
|
|
||||||
import
|
import
|
||||||
ast, ropes, options, intsets,
|
ast, astalgo, ropes, passes, options, intsets, lists, platform
|
||||||
tables, ndi, lineinfos, pathutils, modulegraphs, sets
|
|
||||||
|
from msgs import TLineInfo
|
||||||
|
|
||||||
type
|
type
|
||||||
TLabel* = Rope # for the C generator a label is just a rope
|
TLabel* = Rope # for the C generator a label is just a rope
|
||||||
TCFileSection* = enum # the sections a generated C file consists of
|
TCFileSection* = enum # the sections a generated C file consists of
|
||||||
|
cfsMergeInfo, # section containing merge information
|
||||||
cfsHeaders, # section for C include file headers
|
cfsHeaders, # section for C include file headers
|
||||||
cfsFrameDefines # section for nim frame macros
|
|
||||||
cfsForwardTypes, # section for C forward typedefs
|
cfsForwardTypes, # section for C forward typedefs
|
||||||
cfsTypes, # section for C typedefs
|
cfsTypes, # section for C typedefs
|
||||||
cfsSeqTypes, # section for sequence types only
|
cfsSeqTypes, # section for sequence types only
|
||||||
# this is needed for strange type generation
|
# this is needed for strange type generation
|
||||||
# reasons
|
# reasons
|
||||||
cfsTypeInfo, # section for type information (ag ABI checks)
|
cfsFieldInfo, # section for field information
|
||||||
|
cfsTypeInfo, # section for type information
|
||||||
cfsProcHeaders, # section for C procs prototypes
|
cfsProcHeaders, # section for C procs prototypes
|
||||||
cfsStrData, # section for constant string literals
|
|
||||||
cfsData, # section for C constant data
|
cfsData, # section for C constant data
|
||||||
cfsVars, # section for C variable declarations
|
cfsVars, # section for C variable declarations
|
||||||
cfsProcs, # section for C procs that are not inline
|
cfsProcs, # section for C procs that are not inline
|
||||||
cfsInitProc, # section for the C init proc
|
cfsInitProc, # section for the C init proc
|
||||||
cfsDatInitProc, # section for the C datInit proc
|
|
||||||
cfsTypeInit1, # section 1 for declarations of type information
|
cfsTypeInit1, # section 1 for declarations of type information
|
||||||
|
cfsTypeInit2, # section 2 for init of type information
|
||||||
cfsTypeInit3, # section 3 for init of type information
|
cfsTypeInit3, # section 3 for init of type information
|
||||||
|
cfsDebugInit, # section for init of debug information
|
||||||
cfsDynLibInit, # section for init of dynamic library binding
|
cfsDynLibInit, # section for init of dynamic library binding
|
||||||
|
cfsDynLibDeinit # section for deinitialization of dynamic
|
||||||
|
# libraries
|
||||||
TCTypeKind* = enum # describes the type kind of a C type
|
TCTypeKind* = enum # describes the type kind of a C type
|
||||||
ctVoid, ctChar, ctBool,
|
ctVoid, ctChar, ctBool,
|
||||||
ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
|
ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
|
||||||
|
|
@ -49,36 +53,28 @@ type
|
||||||
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
|
TCProcSections* = array[TCProcSection, Rope] # represents a generated C proc
|
||||||
BModule* = ref TCGen
|
BModule* = ref TCGen
|
||||||
BProc* = ref TCProc
|
BProc* = ref TCProc
|
||||||
TBlock* = object
|
TBlock*{.final.} = object
|
||||||
id*: int # the ID of the label; positive means that it
|
id*: int # the ID of the label; positive means that it
|
||||||
label*: Rope # generated text for the label
|
label*: Rope # generated text for the label
|
||||||
# nil if label is not used
|
# nil if label is not used
|
||||||
sections*: TCProcSections # the code belonging
|
sections*: TCProcSections # the code beloging
|
||||||
isLoop*: bool # whether block is a loop
|
isLoop*: bool # whether block is a loop
|
||||||
nestedTryStmts*: int16 # how many try statements is it nested into
|
nestedTryStmts*: int16 # how many try statements is it nested into
|
||||||
nestedExceptStmts*: int16 # how many except statements is it nested into
|
nestedExceptStmts*: int16 # how many except statements is it nested into
|
||||||
frameLen*: int16
|
frameLen*: int16
|
||||||
|
|
||||||
TCProcFlag* = enum
|
TCProc{.final.} = object # represents C proc that is currently generated
|
||||||
beforeRetNeeded,
|
|
||||||
threadVarAccessed,
|
|
||||||
hasCurFramePointer,
|
|
||||||
noSafePoints,
|
|
||||||
nimErrorFlagAccessed,
|
|
||||||
nimErrorFlagDeclared,
|
|
||||||
nimErrorFlagDisabled
|
|
||||||
|
|
||||||
TCProc = object # represents C proc that is currently generated
|
|
||||||
prc*: PSym # the Nim proc that this C proc belongs to
|
prc*: PSym # the Nim proc that this C proc belongs to
|
||||||
flags*: set[TCProcFlag]
|
beforeRetNeeded*: bool # true iff 'BeforeRet' label for proc is needed
|
||||||
|
threadVarAccessed*: bool # true if the proc already accessed some threadvar
|
||||||
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
|
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
|
||||||
currLineInfo*: TLineInfo # AST codegen will make this superfluous
|
nestedTryStmts*: seq[PNode] # in how many nested try statements we are
|
||||||
nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool, label: Natural]]
|
# (the vars must be volatile then)
|
||||||
# in how many nested try statements we are
|
inExceptBlock*: int # are we currently inside an except block?
|
||||||
# (the vars must be volatile then)
|
# leaving such scopes by raise or by return must
|
||||||
# bool is true when are in the except part of a try block
|
# execute any applicable finally blocks
|
||||||
finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
|
finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
|
||||||
# using return in finally statements
|
# using return in finally statements
|
||||||
labels*: Natural # for generating unique labels in the C proc
|
labels*: Natural # for generating unique labels in the C proc
|
||||||
blocks*: seq[TBlock] # nested blocks
|
blocks*: seq[TBlock] # nested blocks
|
||||||
breakIdx*: int # the block that will be exited
|
breakIdx*: int # the block that will be exited
|
||||||
|
|
@ -86,128 +82,81 @@ type
|
||||||
options*: TOptions # options that should be used for code
|
options*: TOptions # options that should be used for code
|
||||||
# generation; this is the same as prc.options
|
# generation; this is the same as prc.options
|
||||||
# unless prc == nil
|
# unless prc == nil
|
||||||
optionsStack*: seq[TOptions]
|
maxFrameLen*: int # max length of frame descriptor
|
||||||
module*: BModule # used to prevent excessive parameter passing
|
module*: BModule # used to prevent excessive parameter passing
|
||||||
withinLoop*: int # > 0 if we are within a loop
|
withinLoop*: int # > 0 if we are within a loop
|
||||||
splitDecls*: int # > 0 if we are in some context for C++ that
|
splitDecls*: int # > 0 if we are in some context for C++ that
|
||||||
# requires 'T x = T()' to become 'T x; x = T()'
|
# requires 'T x = T()' to become 'T x; x = T()'
|
||||||
# (yes, C++ is weird like that)
|
# (yes, C++ is weird like that)
|
||||||
withinTryWithExcept*: int # required for goto based exception handling
|
gcFrameId*: Natural # for the GC stack marking
|
||||||
withinBlockLeaveActions*: int # complex to explain
|
gcFrameType*: Rope # the struct {} we put the GC markers into
|
||||||
sigConflicts*: CountTable[string]
|
|
||||||
inUncheckedAssignSection*: int
|
|
||||||
|
|
||||||
TTypeSeq* = seq[PType]
|
TTypeSeq* = seq[PType]
|
||||||
TypeCache* = Table[SigHash, Rope]
|
TCGen = object of TPassContext # represents a C source file
|
||||||
TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: int32]]
|
|
||||||
|
|
||||||
CodegenFlag* = enum
|
|
||||||
preventStackTrace, # true if stack traces need to be prevented
|
|
||||||
usesThreadVars, # true if the module uses a thread var
|
|
||||||
frameDeclared, # hack for ROD support so that we don't declare
|
|
||||||
# a frame var twice in an init proc
|
|
||||||
isHeaderFile, # C source file is the header file
|
|
||||||
includesStringh, # C source file already includes ``<string.h>``
|
|
||||||
objHasKidsValid # whether we can rely on tfObjHasKids
|
|
||||||
useAliveDataFromDce # use the `alive: IntSet` field instead of
|
|
||||||
# computing alive data on our own.
|
|
||||||
|
|
||||||
BModuleList* = ref object of RootObj
|
|
||||||
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
|
||||||
mapping*: Rope # the generated mapping file (if requested)
|
|
||||||
modules*: seq[BModule] # list of all compiled modules
|
|
||||||
modulesClosed*: seq[BModule] # list of the same compiled modules, but in the order they were closed
|
|
||||||
forwardedProcs*: seq[PSym] # proc:s that did not yet have a body
|
|
||||||
generatedHeader*: BModule
|
|
||||||
typeInfoMarker*: TypeCacheWithOwner
|
|
||||||
typeInfoMarkerV2*: TypeCacheWithOwner
|
|
||||||
config*: ConfigRef
|
|
||||||
graph*: ModuleGraph
|
|
||||||
strVersion*, seqVersion*: int # version of the string/seq implementation to use
|
|
||||||
|
|
||||||
nimtv*: Rope # Nim thread vars; the struct body
|
|
||||||
nimtvDeps*: seq[PType] # type deps: every module needs whole struct
|
|
||||||
nimtvDeclared*: IntSet # so that every var/field exists only once
|
|
||||||
# in the struct
|
|
||||||
# 'nimtv' is incredibly hard to modularize! Best
|
|
||||||
# effort is to store all thread vars in a ROD
|
|
||||||
# section and with their type deps and load them
|
|
||||||
# unconditionally...
|
|
||||||
# nimtvDeps is VERY hard to cache because it's
|
|
||||||
# not a list of IDs nor can it be made to be one.
|
|
||||||
|
|
||||||
TCGen = object of PPassContext # represents a C source file
|
|
||||||
s*: TCFileSections # sections of the C file
|
|
||||||
flags*: set[CodegenFlag]
|
|
||||||
module*: PSym
|
module*: PSym
|
||||||
filename*: AbsoluteFile
|
filename*: string
|
||||||
cfilename*: AbsoluteFile # filename of the module (including path,
|
s*: TCFileSections # sections of the C file
|
||||||
|
preventStackTrace*: bool # true if stack traces need to be prevented
|
||||||
|
usesThreadVars*: bool # true if the module uses a thread var
|
||||||
|
frameDeclared*: bool # hack for ROD support so that we don't declare
|
||||||
|
# a frame var twice in an init proc
|
||||||
|
isHeaderFile*: bool # C source file is the header file
|
||||||
|
includesStringh*: bool # C source file already includes ``<string.h>``
|
||||||
|
objHasKidsValid*: bool # whether we can rely on tfObjHasKids
|
||||||
|
cfilename*: string # filename of the module (including path,
|
||||||
# without extension)
|
# without extension)
|
||||||
tmpBase*: Rope # base for temp identifier generation
|
typeCache*: TIdTable # cache the generated types
|
||||||
typeCache*: TypeCache # cache the generated types
|
forwTypeCache*: TIdTable # cache for forward declarations of types
|
||||||
typeABICache*: HashSet[SigHash] # cache for ABI checks; reusing typeCache
|
declaredThings*: IntSet # things we have declared in this .c file
|
||||||
# would be ideal but for some reason enums
|
declaredProtos*: IntSet # prototypes we have declared in this .c file
|
||||||
# don't seem to get cached so it'd generate
|
headerFiles*: TLinkedList # needed headers to include
|
||||||
# 1 ABI check per occurence in code
|
typeInfoMarker*: IntSet # needed for generating type information
|
||||||
forwTypeCache*: TypeCache # cache for forward declarations of types
|
|
||||||
declaredThings*: IntSet # things we have declared in this .c file
|
|
||||||
declaredProtos*: IntSet # prototypes we have declared in this .c file
|
|
||||||
alive*: IntSet # symbol IDs of alive data as computed by `dce.nim`
|
|
||||||
headerFiles*: seq[string] # needed headers to include
|
|
||||||
typeInfoMarker*: TypeCache # needed for generating type information
|
|
||||||
typeInfoMarkerV2*: TypeCache
|
|
||||||
initProc*: BProc # code for init procedure
|
initProc*: BProc # code for init procedure
|
||||||
|
postInitProc*: BProc # code to be executed after the init proc
|
||||||
preInitProc*: BProc # code executed before the init proc
|
preInitProc*: BProc # code executed before the init proc
|
||||||
hcrCreateTypeInfosProc*: Rope # type info globals are in here when HCR=on
|
|
||||||
inHcrInitGuard*: bool # We are currently within a HCR reloading guard.
|
|
||||||
typeStack*: TTypeSeq # used for type generation
|
typeStack*: TTypeSeq # used for type generation
|
||||||
dataCache*: TNodeTable
|
dataCache*: TNodeTable
|
||||||
|
forwardedProcs*: TSymSeq # keep forwarded procs here
|
||||||
typeNodes*, nimTypes*: int # used for type info generation
|
typeNodes*, nimTypes*: int # used for type info generation
|
||||||
typeNodesName*, nimTypesName*: Rope # used for type info generation
|
typeNodesName*, nimTypesName*: Rope # used for type info generation
|
||||||
labels*: Natural # for generating unique module-scope names
|
labels*: Natural # for generating unique module-scope names
|
||||||
extensionLoaders*: array['0'..'9', Rope] # special procs for the
|
extensionLoaders*: array['0'..'9', Rope] # special procs for the
|
||||||
# OpenGL wrapper
|
# OpenGL wrapper
|
||||||
sigConflicts*: CountTable[SigHash]
|
injectStmt*: Rope
|
||||||
g*: BModuleList
|
|
||||||
ndi*: NdiFile
|
|
||||||
|
|
||||||
template config*(m: BModule): ConfigRef = m.g.config
|
var
|
||||||
template config*(p: BProc): ConfigRef = p.module.g.config
|
mainModProcs*, mainModInit*, otherModsInit*, mainDatInit*: Rope
|
||||||
template vccAndC*(p: BProc): bool = p.module.config.cCompiler == ccVcc and p.module.config.backend == backendC
|
# varuious parts of the main module
|
||||||
|
gMapping*: Rope # the generated mapping file (if requested)
|
||||||
proc includeHeader*(this: BModule; header: string) =
|
gModules*: seq[BModule] = @[] # list of all compiled modules
|
||||||
if not this.headerFiles.contains header:
|
gForwardedProcsCounter*: int = 0
|
||||||
this.headerFiles.add header
|
|
||||||
|
|
||||||
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
proc s*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||||
# section in the current block
|
# section in the current block
|
||||||
result = p.blocks[^1].sections[s]
|
result = p.blocks[p.blocks.len - 1].sections[s]
|
||||||
|
|
||||||
proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||||
# top level proc sections
|
# top level proc sections
|
||||||
result = p.blocks[0].sections[s]
|
result = p.blocks[0].sections[s]
|
||||||
|
|
||||||
proc initBlock*(): TBlock =
|
proc bmod*(module: PSym): BModule =
|
||||||
result = TBlock()
|
# obtains the BModule for a given module PSym
|
||||||
for i in low(result.sections)..high(result.sections):
|
result = gModules[module.position]
|
||||||
result.sections[i] = newRopeAppender()
|
|
||||||
|
|
||||||
proc newProc*(prc: PSym, module: BModule): BProc =
|
proc newProc*(prc: PSym, module: BModule): BProc =
|
||||||
new(result)
|
new(result)
|
||||||
result.prc = prc
|
result.prc = prc
|
||||||
result.module = module
|
result.module = module
|
||||||
result.options = if prc != nil: prc.options
|
if prc != nil: result.options = prc.options
|
||||||
else: module.config.options
|
else: result.options = gOptions
|
||||||
result.blocks = @[initBlock()]
|
newSeq(result.blocks, 1)
|
||||||
result.nestedTryStmts = @[]
|
result.nestedTryStmts = @[]
|
||||||
result.finallySafePoints = @[]
|
result.finallySafePoints = @[]
|
||||||
result.sigConflicts = initCountTable[string]()
|
|
||||||
|
|
||||||
proc newModuleList*(g: ModuleGraph): BModuleList =
|
iterator cgenModules*: BModule =
|
||||||
BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](),
|
for i in 0..high(gModules):
|
||||||
config: g.config, graph: g, nimtvDeclared: initIntSet())
|
# ultimately, we are iterating over the file ids here.
|
||||||
|
# some "files" won't have an associated cgen module (like stdin)
|
||||||
|
# and we must skip over them.
|
||||||
|
if gModules[i] != nil: yield gModules[i]
|
||||||
|
|
||||||
iterator cgenModules*(g: BModuleList): BModule =
|
|
||||||
for m in g.modulesClosed:
|
|
||||||
# iterate modules in the order they were closed
|
|
||||||
yield m
|
|
||||||
|
|
|
||||||
|
|
@ -7,279 +7,236 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module implements code generation for methods.
|
## This module implements code generation for multi methods.
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, options, ast, msgs, idents, renderer, types, magicsys,
|
intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
|
||||||
sempass2, modulegraphs, lineinfos
|
sempass2, strutils
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
proc genConv(n: PNode, d: PType, downcast: bool): PNode =
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
|
|
||||||
proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
|
|
||||||
var dest = skipTypes(d, abstractPtrs)
|
var dest = skipTypes(d, abstractPtrs)
|
||||||
var source = skipTypes(n.typ, abstractPtrs)
|
var source = skipTypes(n.typ, abstractPtrs)
|
||||||
if (source.kind == tyObject) and (dest.kind == tyObject):
|
if (source.kind == tyObject) and (dest.kind == tyObject):
|
||||||
var diff = inheritanceDiff(dest, source)
|
var diff = inheritanceDiff(dest, source)
|
||||||
if diff == high(int):
|
if diff == high(int): internalError(n.info, "cgmeth.genConv")
|
||||||
# no subtype relation, nothing to do
|
if diff < 0:
|
||||||
result = n
|
|
||||||
elif diff < 0:
|
|
||||||
result = newNodeIT(nkObjUpConv, n.info, d)
|
result = newNodeIT(nkObjUpConv, n.info, d)
|
||||||
result.add n
|
addSon(result, n)
|
||||||
if downcast: internalError(conf, n.info, "cgmeth.genConv: no upcast allowed")
|
if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
|
||||||
elif diff > 0:
|
elif diff > 0:
|
||||||
result = newNodeIT(nkObjDownConv, n.info, d)
|
result = newNodeIT(nkObjDownConv, n.info, d)
|
||||||
result.add n
|
addSon(result, n)
|
||||||
if not downcast:
|
if not downcast:
|
||||||
internalError(conf, n.info, "cgmeth.genConv: no downcast allowed")
|
internalError(n.info, "cgmeth.genConv: no downcast allowed")
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
proc getDispatcher*(s: PSym): PSym =
|
proc methodCall*(n: PNode): PNode =
|
||||||
## can return nil if is has no dispatcher.
|
|
||||||
if dispatcherPos < s.ast.len:
|
|
||||||
result = s.ast[dispatcherPos].sym
|
|
||||||
doAssert sfDispatcher in result.flags
|
|
||||||
|
|
||||||
proc methodCall*(n: PNode; conf: ConfigRef): PNode =
|
|
||||||
result = n
|
result = n
|
||||||
# replace ordinary method by dispatcher method:
|
# replace ordinary method by dispatcher method:
|
||||||
let disp = getDispatcher(result[0].sym)
|
var disp = lastSon(result.sons[0].sym.ast).sym
|
||||||
if disp != nil:
|
assert sfDispatcher in disp.flags
|
||||||
result[0].typ = disp.typ
|
result.sons[0].sym = disp
|
||||||
result[0].sym = disp
|
# change the arguments to up/downcasts to fit the dispatcher's parameters:
|
||||||
# change the arguments to up/downcasts to fit the dispatcher's parameters:
|
for i in countup(1, sonsLen(result)-1):
|
||||||
for i in 1..<result.len:
|
result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true)
|
||||||
result[i] = genConv(result[i], disp.typ[i], true, conf)
|
|
||||||
else:
|
|
||||||
localError(conf, n.info, "'" & $result[0] & "' lacks a dispatcher")
|
|
||||||
|
|
||||||
type
|
# save for incremental compilation:
|
||||||
MethodResult = enum No, Invalid, Yes
|
var
|
||||||
|
gMethods: seq[tuple[methods: TSymSeq, dispatcher: PSym]] = @[]
|
||||||
|
|
||||||
proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
|
proc sameMethodBucket(a, b: PSym): bool =
|
||||||
if a.name.id != b.name.id: return
|
result = false
|
||||||
if a.typ.len != b.typ.len:
|
if a.name.id != b.name.id: return
|
||||||
return
|
if sonsLen(a.typ) != sonsLen(b.typ):
|
||||||
|
return # check for return type:
|
||||||
for i in 1..<a.typ.len:
|
if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return
|
||||||
var aa = a.typ[i]
|
for i in countup(1, sonsLen(a.typ) - 1):
|
||||||
var bb = b.typ[i]
|
var aa = a.typ.sons[i]
|
||||||
while true:
|
var bb = b.typ.sons[i]
|
||||||
aa = skipTypes(aa, {tyGenericInst, tyAlias})
|
while true:
|
||||||
bb = skipTypes(bb, {tyGenericInst, tyAlias})
|
aa = skipTypes(aa, {tyGenericInst})
|
||||||
if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}:
|
bb = skipTypes(bb, {tyGenericInst})
|
||||||
|
if (aa.kind == bb.kind) and (aa.kind in {tyVar, tyPtr, tyRef}):
|
||||||
aa = aa.lastSon
|
aa = aa.lastSon
|
||||||
bb = bb.lastSon
|
bb = bb.lastSon
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
if sameType(a.typ[i], b.typ[i]):
|
if sameType(aa, bb) or
|
||||||
if aa.kind == tyObject and result != Invalid:
|
(aa.kind == tyObject) and (bb.kind == tyObject) and
|
||||||
result = Yes
|
(inheritanceDiff(bb, aa) < 0):
|
||||||
elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods):
|
discard
|
||||||
let diff = inheritanceDiff(bb, aa)
|
|
||||||
if diff < 0:
|
|
||||||
if result != Invalid:
|
|
||||||
result = Yes
|
|
||||||
else:
|
|
||||||
return No
|
|
||||||
elif diff != high(int) and sfFromGeneric notin (a.flags+b.flags):
|
|
||||||
result = Invalid
|
|
||||||
else:
|
|
||||||
return No
|
|
||||||
else:
|
else:
|
||||||
return No
|
return
|
||||||
if result == Yes:
|
result = true
|
||||||
# check for return type:
|
|
||||||
if not sameTypeOrNil(a.typ[0], b.typ[0]):
|
|
||||||
if b.typ[0] != nil and b.typ[0].kind == tyUntyped:
|
|
||||||
# infer 'auto' from the base to make it consistent:
|
|
||||||
b.typ[0] = a.typ[0]
|
|
||||||
else:
|
|
||||||
return No
|
|
||||||
|
|
||||||
proc attachDispatcher(s: PSym, dispatcher: PNode) =
|
proc attachDispatcher(s: PSym, dispatcher: PNode) =
|
||||||
if dispatcherPos < s.ast.len:
|
var L = s.ast.len-1
|
||||||
|
var x = s.ast.sons[L]
|
||||||
|
if x.kind == nkSym and sfDispatcher in x.sym.flags:
|
||||||
# we've added a dispatcher already, so overwrite it
|
# we've added a dispatcher already, so overwrite it
|
||||||
s.ast[dispatcherPos] = dispatcher
|
s.ast.sons[L] = dispatcher
|
||||||
else:
|
else:
|
||||||
setLen(s.ast.sons, dispatcherPos+1)
|
s.ast.add(dispatcher)
|
||||||
if s.ast[resultPos] == nil:
|
|
||||||
s.ast[resultPos] = newNodeI(nkEmpty, s.info)
|
|
||||||
s.ast[dispatcherPos] = dispatcher
|
|
||||||
|
|
||||||
proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
|
proc createDispatcher(s: PSym): PSym =
|
||||||
var disp = copySym(s, nextSymId(idgen))
|
var disp = copySym(s)
|
||||||
incl(disp.flags, sfDispatcher)
|
incl(disp.flags, sfDispatcher)
|
||||||
excl(disp.flags, sfExported)
|
excl(disp.flags, sfExported)
|
||||||
let old = disp.typ
|
disp.typ = copyType(disp.typ, disp.typ.owner, false)
|
||||||
disp.typ = copyType(disp.typ, nextTypeId(idgen), disp.typ.owner)
|
|
||||||
copyTypeProps(g, idgen.module, disp.typ, old)
|
|
||||||
|
|
||||||
# we can't inline the dispatcher itself (for now):
|
# we can't inline the dispatcher itself (for now):
|
||||||
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
|
if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault
|
||||||
disp.ast = copyTree(s.ast)
|
disp.ast = copyTree(s.ast)
|
||||||
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
|
disp.ast.sons[bodyPos] = ast.emptyNode
|
||||||
disp.loc.r = ""
|
disp.loc.r = nil
|
||||||
if s.typ[0] != nil:
|
if s.typ.sons[0] != nil:
|
||||||
if disp.ast.len > resultPos:
|
if disp.ast.sonsLen > resultPos:
|
||||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen))
|
disp.ast.sons[resultPos].sym = copySym(s.ast.sons[resultPos].sym)
|
||||||
else:
|
else:
|
||||||
# We've encountered a method prototype without a filled-in
|
# We've encountered a method prototype without a filled-in
|
||||||
# resultPos slot. We put a placeholder in there that will
|
# resultPos slot. We put a placeholder in there that will
|
||||||
# be updated in fixupDispatcher().
|
# be updated in fixupDispatcher().
|
||||||
disp.ast.add newNodeI(nkEmpty, s.info)
|
disp.ast.addSon(ast.emptyNode)
|
||||||
attachDispatcher(s, newSymNode(disp))
|
attachDispatcher(s, newSymNode(disp))
|
||||||
# attach to itself to prevent bugs:
|
# attach to itself to prevent bugs:
|
||||||
attachDispatcher(disp, newSymNode(disp))
|
attachDispatcher(disp, newSymNode(disp))
|
||||||
return disp
|
return disp
|
||||||
|
|
||||||
proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
|
proc fixupDispatcher(meth, disp: PSym) =
|
||||||
# We may have constructed the dispatcher from a method prototype
|
# We may have constructed the dispatcher from a method prototype
|
||||||
# and need to augment the incomplete dispatcher with information
|
# and need to augment the incomplete dispatcher with information
|
||||||
# from later definitions, particularly the resultPos slot. Also,
|
# from later definitions, particularly the resultPos slot. Also,
|
||||||
# the lock level of the dispatcher needs to be updated/checked
|
# the lock level of the dispatcher needs to be updated/checked
|
||||||
# against that of the method.
|
# against that of the method.
|
||||||
if disp.ast.len > resultPos and meth.ast.len > resultPos and
|
if disp.ast.sonsLen > resultPos and meth.ast.sonsLen > resultPos and
|
||||||
disp.ast[resultPos].kind == nkEmpty:
|
disp.ast.sons[resultPos] == ast.emptyNode:
|
||||||
disp.ast[resultPos] = copyTree(meth.ast[resultPos])
|
disp.ast.sons[resultPos] = copyTree(meth.ast.sons[resultPos])
|
||||||
|
|
||||||
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
# The following code works only with lock levels, so we disable
|
||||||
var witness: PSym
|
# it when they're not available.
|
||||||
for i in 0..<g.methods.len:
|
when declared(TLockLevel):
|
||||||
let disp = g.methods[i].dispatcher
|
proc `<`(a, b: TLockLevel): bool {.borrow.}
|
||||||
case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions)
|
proc `==`(a, b: TLockLevel): bool {.borrow.}
|
||||||
of Yes:
|
if disp.typ.lockLevel == UnspecifiedLockLevel:
|
||||||
g.methods[i].methods.add(s)
|
disp.typ.lockLevel = meth.typ.lockLevel
|
||||||
attachDispatcher(s, disp.ast[dispatcherPos])
|
elif meth.typ.lockLevel != UnspecifiedLockLevel and
|
||||||
fixupDispatcher(s, disp, g.config)
|
meth.typ.lockLevel != disp.typ.lockLevel:
|
||||||
#echo "fixup ", disp.name.s, " ", disp.id
|
message(meth.info, warnLockLevel,
|
||||||
when useEffectSystem: checkMethodEffects(g, disp, s)
|
"method has lock level $1, but another method has $2" %
|
||||||
if {sfBase, sfFromGeneric} * s.flags == {sfBase} and
|
[$meth.typ.lockLevel, $disp.typ.lockLevel])
|
||||||
g.methods[i].methods[0] != s:
|
# XXX The following code silences a duplicate warning in
|
||||||
# already exists due to forwarding definition?
|
# checkMethodeffects() in sempass2.nim for now.
|
||||||
localError(g.config, s.info, "method is not a base")
|
if disp.typ.lockLevel < meth.typ.lockLevel:
|
||||||
return
|
disp.typ.lockLevel = meth.typ.lockLevel
|
||||||
of No: discard
|
|
||||||
of Invalid:
|
proc methodDef*(s: PSym, fromCache: bool) =
|
||||||
if witness.isNil: witness = g.methods[i].methods[0]
|
var L = len(gMethods)
|
||||||
|
for i in countup(0, L - 1):
|
||||||
|
var disp = gMethods[i].dispatcher
|
||||||
|
if sameMethodBucket(disp, s):
|
||||||
|
add(gMethods[i].methods, s)
|
||||||
|
attachDispatcher(s, lastSon(disp.ast))
|
||||||
|
fixupDispatcher(s, disp)
|
||||||
|
when useEffectSystem: checkMethodEffects(disp, s)
|
||||||
|
return
|
||||||
# create a new dispatcher:
|
# create a new dispatcher:
|
||||||
g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
|
add(gMethods, (methods: @[s], dispatcher: createDispatcher(s)))
|
||||||
#echo "adding ", s.info
|
if fromCache:
|
||||||
if witness != nil:
|
internalError(s.info, "no method dispatcher found")
|
||||||
localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s &
|
|
||||||
"' to method defined here: " & g.config$witness.info)
|
|
||||||
elif sfBase notin s.flags:
|
|
||||||
message(g.config, s.info, warnUseBase)
|
|
||||||
|
|
||||||
proc relevantCol(methods: seq[PSym], col: int): bool =
|
proc relevantCol(methods: TSymSeq, col: int): bool =
|
||||||
# returns true iff the position is relevant
|
# returns true iff the position is relevant
|
||||||
var t = methods[0].typ[col].skipTypes(skipPtrs)
|
var t = methods[0].typ.sons[col].skipTypes(skipPtrs)
|
||||||
if t.kind == tyObject:
|
if t.kind == tyObject:
|
||||||
for i in 1..high(methods):
|
for i in countup(1, high(methods)):
|
||||||
let t2 = skipTypes(methods[i].typ[col], skipPtrs)
|
let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
|
||||||
if not sameType(t2, t):
|
if not sameType(t2, t):
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
|
proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
|
||||||
for col in 1..<a.typ.len:
|
for col in countup(1, sonsLen(a.typ) - 1):
|
||||||
if contains(relevantCols, col):
|
if contains(relevantCols, col):
|
||||||
var aa = skipTypes(a.typ[col], skipPtrs)
|
var aa = skipTypes(a.typ.sons[col], skipPtrs)
|
||||||
var bb = skipTypes(b.typ[col], skipPtrs)
|
var bb = skipTypes(b.typ.sons[col], skipPtrs)
|
||||||
var d = inheritanceDiff(aa, bb)
|
var d = inheritanceDiff(aa, bb)
|
||||||
if (d != high(int)) and d != 0:
|
if (d != high(int)):
|
||||||
return d
|
return d
|
||||||
|
|
||||||
proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
|
proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
|
||||||
# we use shellsort here; fast and simple
|
# we use shellsort here; fast and simple
|
||||||
var n = a.len
|
var n = len(a)
|
||||||
var h = 1
|
var h = 1
|
||||||
while true:
|
while true:
|
||||||
h = 3 * h + 1
|
h = 3 * h + 1
|
||||||
if h > n: break
|
if h > n: break
|
||||||
while true:
|
while true:
|
||||||
h = h div 3
|
h = h div 3
|
||||||
for i in h..<n:
|
for i in countup(h, n - 1):
|
||||||
var v = a[i]
|
var v = a[i]
|
||||||
var j = i
|
var j = i
|
||||||
while cmpSignatures(a[j - h], v, relevantCols) >= 0:
|
while cmpSignatures(a[j - h], v, relevantCols) >= 0:
|
||||||
a[j] = a[j - h]
|
a[j] = a[j - h]
|
||||||
j = j - h
|
j = j - h
|
||||||
if j < h: break
|
if j < h: break
|
||||||
a[j] = v
|
a[j] = v
|
||||||
if h == 1: break
|
if h == 1: break
|
||||||
|
|
||||||
proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PSym =
|
proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
|
||||||
var base = methods[0].ast[dispatcherPos].sym
|
var base = lastSon(methods[0].ast).sym
|
||||||
result = base
|
result = base
|
||||||
var paramLen = base.typ.len
|
var paramLen = sonsLen(base.typ)
|
||||||
var nilchecks = newNodeI(nkStmtList, base.info)
|
|
||||||
var disp = newNodeI(nkIfStmt, base.info)
|
var disp = newNodeI(nkIfStmt, base.info)
|
||||||
var ands = getSysMagic(g, unknownLineInfo, "and", mAnd)
|
var ands = getSysSym("and")
|
||||||
var iss = getSysMagic(g, unknownLineInfo, "of", mOf)
|
var iss = getSysSym("of")
|
||||||
let boolType = getSysType(g, unknownLineInfo, tyBool)
|
for meth in countup(0, high(methods)):
|
||||||
for col in 1..<paramLen:
|
|
||||||
if contains(relevantCols, col):
|
|
||||||
let param = base.typ.n[col].sym
|
|
||||||
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
|
|
||||||
nilchecks.add newTree(nkCall,
|
|
||||||
newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(param))
|
|
||||||
for meth in 0..high(methods):
|
|
||||||
var curr = methods[meth] # generate condition:
|
var curr = methods[meth] # generate condition:
|
||||||
var cond: PNode = nil
|
var cond: PNode = nil
|
||||||
for col in 1..<paramLen:
|
for col in countup(1, paramLen - 1):
|
||||||
if contains(relevantCols, col):
|
if contains(relevantCols, col):
|
||||||
var isn = newNodeIT(nkCall, base.info, boolType)
|
var isn = newNodeIT(nkCall, base.info, getSysType(tyBool))
|
||||||
isn.add newSymNode(iss)
|
addSon(isn, newSymNode(iss))
|
||||||
let param = base.typ.n[col].sym
|
addSon(isn, newSymNode(base.typ.n.sons[col].sym))
|
||||||
isn.add newSymNode(param)
|
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
|
||||||
isn.add newNodeIT(nkType, base.info, curr.typ[col])
|
if cond != nil:
|
||||||
if cond != nil:
|
var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
|
||||||
var a = newNodeIT(nkCall, base.info, boolType)
|
addSon(a, newSymNode(ands))
|
||||||
a.add newSymNode(ands)
|
addSon(a, cond)
|
||||||
a.add cond
|
addSon(a, isn)
|
||||||
a.add isn
|
|
||||||
cond = a
|
cond = a
|
||||||
else:
|
else:
|
||||||
cond = isn
|
cond = isn
|
||||||
let retTyp = base.typ[0]
|
var call = newNodeI(nkCall, base.info)
|
||||||
let call = newNodeIT(nkCall, base.info, retTyp)
|
addSon(call, newSymNode(curr))
|
||||||
call.add newSymNode(curr)
|
for col in countup(1, paramLen - 1):
|
||||||
for col in 1..<paramLen:
|
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
|
||||||
call.add genConv(newSymNode(base.typ.n[col].sym),
|
curr.typ.sons[col], false))
|
||||||
curr.typ[col], false, g.config)
|
|
||||||
var ret: PNode
|
var ret: PNode
|
||||||
if retTyp != nil:
|
if base.typ.sons[0] != nil:
|
||||||
var a = newNodeI(nkFastAsgn, base.info)
|
var a = newNodeI(nkAsgn, base.info)
|
||||||
a.add newSymNode(base.ast[resultPos].sym)
|
addSon(a, newSymNode(base.ast.sons[resultPos].sym))
|
||||||
a.add call
|
addSon(a, call)
|
||||||
ret = newNodeI(nkReturnStmt, base.info)
|
ret = newNodeI(nkReturnStmt, base.info)
|
||||||
ret.add a
|
addSon(ret, a)
|
||||||
else:
|
else:
|
||||||
ret = call
|
ret = call
|
||||||
if cond != nil:
|
if cond != nil:
|
||||||
var a = newNodeI(nkElifBranch, base.info)
|
var a = newNodeI(nkElifBranch, base.info)
|
||||||
a.add cond
|
addSon(a, cond)
|
||||||
a.add ret
|
addSon(a, ret)
|
||||||
disp.add a
|
addSon(disp, a)
|
||||||
else:
|
else:
|
||||||
disp = ret
|
disp = ret
|
||||||
nilchecks.add disp
|
result.ast.sons[bodyPos] = disp
|
||||||
nilchecks.flags.incl nfTransf # should not be further transformed
|
|
||||||
result.ast[bodyPos] = nilchecks
|
|
||||||
|
|
||||||
proc generateMethodDispatchers*(g: ModuleGraph): PNode =
|
proc generateMethodDispatchers*(): PNode =
|
||||||
result = newNode(nkStmtList)
|
result = newNode(nkStmtList)
|
||||||
for bucket in 0..<g.methods.len:
|
for bucket in countup(0, len(gMethods) - 1):
|
||||||
var relevantCols = initIntSet()
|
var relevantCols = initIntSet()
|
||||||
for col in 1..<g.methods[bucket].methods[0].typ.len:
|
for col in countup(1, sonsLen(gMethods[bucket].methods[0].typ) - 1):
|
||||||
if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col)
|
if relevantCol(gMethods[bucket].methods, col): incl(relevantCols, col)
|
||||||
if optMultiMethods notin g.config.globalOptions:
|
sortBucket(gMethods[bucket].methods, relevantCols)
|
||||||
# if multi-methods are not enabled, we are interested only in the first field
|
addSon(result,
|
||||||
break
|
newSymNode(genDispatcher(gMethods[bucket].methods, relevantCols)))
|
||||||
sortBucket(g.methods[bucket].methods, relevantCols)
|
|
||||||
result.add newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols))
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,78 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2018 Nim contributors
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest, nimfix
|
|
||||||
|
|
||||||
import
|
|
||||||
options, idents, nimconf, extccomp, commands, msgs,
|
|
||||||
lineinfos, modulegraphs, condsyms, os, pathutils, parseopt
|
|
||||||
|
|
||||||
proc prependCurDir*(f: AbsoluteFile): AbsoluteFile =
|
|
||||||
when defined(unix):
|
|
||||||
if os.isAbsolute(f.string): result = f
|
|
||||||
else: result = AbsoluteFile("./" & f.string)
|
|
||||||
else:
|
|
||||||
result = f
|
|
||||||
|
|
||||||
proc addCmdPrefix*(result: var string, kind: CmdLineKind) =
|
|
||||||
# consider moving this to std/parseopt
|
|
||||||
case kind
|
|
||||||
of cmdLongOption: result.add "--"
|
|
||||||
of cmdShortOption: result.add "-"
|
|
||||||
of cmdArgument, cmdEnd: discard
|
|
||||||
|
|
||||||
type
|
|
||||||
NimProg* = ref object
|
|
||||||
suggestMode*: bool
|
|
||||||
supportsStdinFile*: bool
|
|
||||||
processCmdLine*: proc(pass: TCmdLinePass, cmd: string; config: ConfigRef)
|
|
||||||
|
|
||||||
proc initDefinesProg*(self: NimProg, conf: ConfigRef, name: string) =
|
|
||||||
condsyms.initDefines(conf.symbols)
|
|
||||||
defineSymbol conf.symbols, name
|
|
||||||
|
|
||||||
proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) =
|
|
||||||
self.processCmdLine(passCmd1, "", conf)
|
|
||||||
if conf.projectIsCmd and conf.projectName in ["-", ""]:
|
|
||||||
handleCmdInput(conf)
|
|
||||||
elif self.supportsStdinFile and conf.projectName == "-":
|
|
||||||
handleStdinInput(conf)
|
|
||||||
elif conf.projectName != "":
|
|
||||||
setFromProjectName(conf, conf.projectName)
|
|
||||||
else:
|
|
||||||
conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir())
|
|
||||||
|
|
||||||
proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: ConfigRef;
|
|
||||||
graph: ModuleGraph): bool =
|
|
||||||
if self.suggestMode:
|
|
||||||
conf.setCmd cmdIdeTools
|
|
||||||
if conf.cmd == cmdNimscript:
|
|
||||||
incl(conf.globalOptions, optWasNimscript)
|
|
||||||
loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
|
|
||||||
|
|
||||||
if not self.suggestMode:
|
|
||||||
let scriptFile = conf.projectFull.changeFileExt("nims")
|
|
||||||
# 'nim foo.nims' means to just run the NimScript file and do nothing more:
|
|
||||||
if fileExists(scriptFile) and scriptFile == conf.projectFull:
|
|
||||||
if conf.cmd == cmdNone: conf.setCmd cmdNimscript
|
|
||||||
if conf.cmd == cmdNimscript: return false
|
|
||||||
# now process command line arguments again, because some options in the
|
|
||||||
# command line can overwrite the config file's settings
|
|
||||||
if conf.backend != backendJs: # bug #19059
|
|
||||||
extccomp.initVars(conf)
|
|
||||||
self.processCmdLine(passCmd2, "", conf)
|
|
||||||
if conf.cmd == cmdNone:
|
|
||||||
rawMessage(conf, errGenerated, "command missing")
|
|
||||||
|
|
||||||
graph.suggestMode = self.suggestMode
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef; graph: ModuleGraph): bool =
|
|
||||||
## Alias for loadConfigsAndProcessCmdLine, here for backwards compatibility
|
|
||||||
loadConfigsAndProcessCmdLine(self, cache, conf, graph)
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,344 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2020 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## New styled concepts for Nim. See https://github.com/nim-lang/RFCs/issues/168
|
|
||||||
## for details. Note this is a first implementation and only the "Concept matching"
|
|
||||||
## section has been implemented.
|
|
||||||
|
|
||||||
import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, intsets
|
|
||||||
|
|
||||||
from magicsys import addSonSkipIntLit
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
const
|
|
||||||
logBindings = false
|
|
||||||
|
|
||||||
## Code dealing with Concept declarations
|
|
||||||
## --------------------------------------
|
|
||||||
|
|
||||||
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)
|
|
||||||
s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow)
|
|
||||||
s.typ.flags.incl {tfUnresolved, tfPacked}
|
|
||||||
s.typ.add newType(tyEmpty, nextTypeId(c.idgen), ow)
|
|
||||||
addDecl(c, s, info)
|
|
||||||
|
|
||||||
proc isSelf*(t: PType): bool {.inline.} =
|
|
||||||
## Is this the magical 'Self' type?
|
|
||||||
t.kind == tyTypeDesc and tfPacked in t.flags
|
|
||||||
|
|
||||||
proc makeTypeDesc*(c: PContext, typ: PType): PType =
|
|
||||||
if typ.kind == tyTypeDesc and not isSelf(typ):
|
|
||||||
result = typ
|
|
||||||
else:
|
|
||||||
result = newTypeS(tyTypeDesc, c)
|
|
||||||
incl result.flags, tfCheckedForDestructor
|
|
||||||
result.addSonSkipIntLit(typ, c.idgen)
|
|
||||||
|
|
||||||
proc semConceptDecl(c: PContext; n: PNode): PNode =
|
|
||||||
## Recursive helper for semantic checking for the concept declaration.
|
|
||||||
## Currently we only support (possibly empty) lists of statements
|
|
||||||
## containing 'proc' declarations and the like.
|
|
||||||
case n.kind
|
|
||||||
of nkStmtList, nkStmtListExpr:
|
|
||||||
result = shallowCopy(n)
|
|
||||||
for i in 0..<n.len:
|
|
||||||
result[i] = semConceptDecl(c, n[i])
|
|
||||||
of nkProcDef..nkIteratorDef, nkFuncDef:
|
|
||||||
result = c.semExpr(c, n, {efWantStmt})
|
|
||||||
of nkTypeClassTy:
|
|
||||||
result = shallowCopy(n)
|
|
||||||
for i in 0..<n.len-1:
|
|
||||||
result[i] = n[i]
|
|
||||||
result[^1] = semConceptDecl(c, n[^1])
|
|
||||||
of nkCommentStmt:
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n))
|
|
||||||
result = n
|
|
||||||
|
|
||||||
proc semConceptDeclaration*(c: PContext; n: PNode): PNode =
|
|
||||||
## Semantic checking for the concept declaration. Runs
|
|
||||||
## when we process the concept itself, not its matching process.
|
|
||||||
assert n.kind == nkTypeClassTy
|
|
||||||
inc c.inConceptDecl
|
|
||||||
openScope(c)
|
|
||||||
declareSelf(c, n.info)
|
|
||||||
result = semConceptDecl(c, n)
|
|
||||||
rawCloseScope(c)
|
|
||||||
dec c.inConceptDecl
|
|
||||||
|
|
||||||
## Concept matching
|
|
||||||
## ----------------
|
|
||||||
|
|
||||||
type
|
|
||||||
MatchCon = object ## Context we pass around during concept matching.
|
|
||||||
inferred: seq[(PType, PType)] ## we need a seq here so that we can easily undo inferences \
|
|
||||||
## that turned out to be wrong.
|
|
||||||
marker: IntSet ## Some protection against wild runaway recursions.
|
|
||||||
potentialImplementation: PType ## the concrete type that might match the concept we try to match.
|
|
||||||
magic: TMagic ## mArrGet and mArrPut is wrong in system.nim and
|
|
||||||
## cannot be fixed that easily.
|
|
||||||
## Thus we special case it here.
|
|
||||||
|
|
||||||
proc existingBinding(m: MatchCon; key: PType): PType =
|
|
||||||
## checks if we bound the type variable 'key' already to some
|
|
||||||
## concrete type.
|
|
||||||
for i in 0..<m.inferred.len:
|
|
||||||
if m.inferred[i][0] == key: return m.inferred[i][1]
|
|
||||||
return nil
|
|
||||||
|
|
||||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool
|
|
||||||
|
|
||||||
proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
|
||||||
## The heart of the concept matching process. 'f' is the formal parameter of some
|
|
||||||
## routine inside the concept that we're looking for. 'a' is the formal parameter
|
|
||||||
## of a routine that might match.
|
|
||||||
const
|
|
||||||
ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
|
|
||||||
case f.kind
|
|
||||||
of tyAlias:
|
|
||||||
result = matchType(c, f.lastSon, a, m)
|
|
||||||
of tyTypeDesc:
|
|
||||||
if isSelf(f):
|
|
||||||
#let oldLen = m.inferred.len
|
|
||||||
result = matchType(c, a, m.potentialImplementation, m)
|
|
||||||
#echo "self is? ", result, " ", a.kind, " ", a, " ", m.potentialImplementation, " ", m.potentialImplementation.kind
|
|
||||||
#m.inferred.setLen oldLen
|
|
||||||
#echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
|
||||||
else:
|
|
||||||
if a.kind == tyTypeDesc and f.len == a.len:
|
|
||||||
for i in 0..<a.len:
|
|
||||||
if not matchType(c, f[i], a[i], m): return false
|
|
||||||
return true
|
|
||||||
|
|
||||||
of tyGenericInvocation:
|
|
||||||
if a.kind == tyGenericInst and a[0].kind == tyGenericBody:
|
|
||||||
if sameType(f[0], a[0]) and f.len == a.len-1:
|
|
||||||
for i in 1 ..< f.len:
|
|
||||||
if not matchType(c, f[i], a[i], m): return false
|
|
||||||
return true
|
|
||||||
of tyGenericParam:
|
|
||||||
let ak = a.skipTypes({tyVar, tySink, tyLent, tyOwned})
|
|
||||||
if ak.kind in {tyTypeDesc, tyStatic} and not isSelf(ak):
|
|
||||||
result = false
|
|
||||||
else:
|
|
||||||
let old = existingBinding(m, f)
|
|
||||||
if old == nil:
|
|
||||||
if f.len > 0 and f[0].kind != tyNone:
|
|
||||||
# also check the generic's constraints:
|
|
||||||
let oldLen = m.inferred.len
|
|
||||||
result = matchType(c, f[0], a, m)
|
|
||||||
m.inferred.setLen oldLen
|
|
||||||
if result:
|
|
||||||
when logBindings: echo "A adding ", f, " ", ak
|
|
||||||
m.inferred.add((f, ak))
|
|
||||||
elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}:
|
|
||||||
when logBindings: echo "B adding ", f, " ", lastSon ak
|
|
||||||
m.inferred.add((f, lastSon ak))
|
|
||||||
result = true
|
|
||||||
else:
|
|
||||||
when logBindings: echo "C adding ", f, " ", ak
|
|
||||||
m.inferred.add((f, ak))
|
|
||||||
#echo "binding ", typeToString(ak), " to ", typeToString(f)
|
|
||||||
result = true
|
|
||||||
elif not m.marker.containsOrIncl(old.id):
|
|
||||||
result = matchType(c, old, ak, m)
|
|
||||||
if m.magic == mArrPut and ak.kind == tyGenericParam:
|
|
||||||
result = true
|
|
||||||
#echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
|
|
||||||
|
|
||||||
of tyVar, tySink, tyLent, tyOwned:
|
|
||||||
# modifiers in the concept must be there in the actual implementation
|
|
||||||
# too but not vice versa.
|
|
||||||
if a.kind == f.kind:
|
|
||||||
result = matchType(c, f.sons[0], a.sons[0], m)
|
|
||||||
elif m.magic == mArrPut:
|
|
||||||
result = matchType(c, f.sons[0], a, m)
|
|
||||||
else:
|
|
||||||
result = false
|
|
||||||
of tyEnum, tyObject, tyDistinct:
|
|
||||||
result = sameType(f, a)
|
|
||||||
of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid:
|
|
||||||
result = a.skipTypes(ignorableForArgType).kind == f.kind
|
|
||||||
of tyBool, tyChar, tyInt..tyUInt64:
|
|
||||||
let ak = a.skipTypes(ignorableForArgType)
|
|
||||||
result = ak.kind == f.kind or ak.kind == tyOrdinal or
|
|
||||||
(ak.kind == tyGenericParam and ak.len > 0 and ak[0].kind == tyOrdinal)
|
|
||||||
of tyConcept:
|
|
||||||
let oldLen = m.inferred.len
|
|
||||||
let oldPotentialImplementation = m.potentialImplementation
|
|
||||||
m.potentialImplementation = a
|
|
||||||
result = conceptMatchNode(c, f.n.lastSon, m)
|
|
||||||
m.potentialImplementation = oldPotentialImplementation
|
|
||||||
if not result:
|
|
||||||
m.inferred.setLen oldLen
|
|
||||||
of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
|
|
||||||
tyGenericInst:
|
|
||||||
let ak = a.skipTypes(ignorableForArgType - {f.kind})
|
|
||||||
if ak.kind == f.kind and f.len == ak.len:
|
|
||||||
for i in 0..<ak.len:
|
|
||||||
if not matchType(c, f[i], ak[i], m): return false
|
|
||||||
return true
|
|
||||||
of tyOr:
|
|
||||||
let oldLen = m.inferred.len
|
|
||||||
if a.kind == tyOr:
|
|
||||||
# say the concept requires 'int|float|string' if the potentialImplementation
|
|
||||||
# says 'int|string' that is good enough.
|
|
||||||
var covered = 0
|
|
||||||
for i in 0..<f.len:
|
|
||||||
for j in 0..<a.len:
|
|
||||||
let oldLenB = m.inferred.len
|
|
||||||
let r = matchType(c, f[i], a[j], m)
|
|
||||||
if r:
|
|
||||||
inc covered
|
|
||||||
break
|
|
||||||
m.inferred.setLen oldLenB
|
|
||||||
|
|
||||||
result = covered >= a.len
|
|
||||||
if not result:
|
|
||||||
m.inferred.setLen oldLen
|
|
||||||
else:
|
|
||||||
for i in 0..<f.len:
|
|
||||||
result = matchType(c, f[i], a, m)
|
|
||||||
if result: break # and remember the binding!
|
|
||||||
m.inferred.setLen oldLen
|
|
||||||
of tyNot:
|
|
||||||
if a.kind == tyNot:
|
|
||||||
result = matchType(c, f[0], a[0], m)
|
|
||||||
else:
|
|
||||||
let oldLen = m.inferred.len
|
|
||||||
result = not matchType(c, f[0], a, m)
|
|
||||||
m.inferred.setLen oldLen
|
|
||||||
of tyAnything:
|
|
||||||
result = true
|
|
||||||
of tyOrdinal:
|
|
||||||
result = isOrdinalType(a, allowEnumWithHoles = false) or a.kind == tyGenericParam
|
|
||||||
else:
|
|
||||||
result = false
|
|
||||||
|
|
||||||
proc matchReturnType(c: PContext; f, a: PType; m: var MatchCon): bool =
|
|
||||||
## Like 'matchType' but with extra logic dealing with proc return types
|
|
||||||
## which can be nil or the 'void' type.
|
|
||||||
if f.isEmptyType:
|
|
||||||
result = a.isEmptyType
|
|
||||||
elif a == nil:
|
|
||||||
result = false
|
|
||||||
else:
|
|
||||||
result = matchType(c, f, a, m)
|
|
||||||
|
|
||||||
proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
|
||||||
## Checks if 'candidate' matches 'n' from the concept body. 'n' is a nkProcDef
|
|
||||||
## or similar.
|
|
||||||
|
|
||||||
# watch out: only add bindings after a completely successful match.
|
|
||||||
let oldLen = m.inferred.len
|
|
||||||
|
|
||||||
let can = candidate.typ.n
|
|
||||||
let con = n[0].sym.typ.n
|
|
||||||
|
|
||||||
if can.len < con.len:
|
|
||||||
# too few arguments, cannot be a match:
|
|
||||||
return false
|
|
||||||
|
|
||||||
let common = min(can.len, con.len)
|
|
||||||
for i in 1 ..< common:
|
|
||||||
if not matchType(c, con[i].typ, can[i].typ, m):
|
|
||||||
m.inferred.setLen oldLen
|
|
||||||
return false
|
|
||||||
|
|
||||||
if not matchReturnType(c, n[0].sym.typ.sons[0], candidate.typ.sons[0], m):
|
|
||||||
m.inferred.setLen oldLen
|
|
||||||
return false
|
|
||||||
|
|
||||||
# all other parameters have to be optional parameters:
|
|
||||||
for i in common ..< can.len:
|
|
||||||
assert can[i].kind == nkSym
|
|
||||||
if can[i].sym.ast == nil:
|
|
||||||
# has too many arguments one of which is not optional:
|
|
||||||
m.inferred.setLen oldLen
|
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
|
|
||||||
## Walk the current scope, extract candidates which the same name as 'n[namePos]',
|
|
||||||
## 'n' is the nkProcDef or similar from the concept that we try to match.
|
|
||||||
let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
|
|
||||||
for candidate in candidates:
|
|
||||||
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
|
|
||||||
m.magic = candidate.magic
|
|
||||||
if matchSym(c, candidate, n, m): return true
|
|
||||||
result = false
|
|
||||||
|
|
||||||
proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
|
|
||||||
## Traverse the concept's AST ('n') and see if every declaration inside 'n'
|
|
||||||
## can be matched with the current scope.
|
|
||||||
case n.kind
|
|
||||||
of nkStmtList, nkStmtListExpr:
|
|
||||||
for i in 0..<n.len:
|
|
||||||
if not conceptMatchNode(c, n[i], m):
|
|
||||||
return false
|
|
||||||
return true
|
|
||||||
of nkProcDef, nkFuncDef:
|
|
||||||
# procs match any of: proc, template, macro, func, method, converter.
|
|
||||||
# The others are more specific.
|
|
||||||
# XXX: Enforce .noSideEffect for 'nkFuncDef'? But then what are the use cases...
|
|
||||||
const filter = {skProc, skTemplate, skMacro, skFunc, skMethod, skConverter}
|
|
||||||
result = matchSyms(c, n, filter, m)
|
|
||||||
of nkTemplateDef:
|
|
||||||
result = matchSyms(c, n, {skTemplate}, m)
|
|
||||||
of nkMacroDef:
|
|
||||||
result = matchSyms(c, n, {skMacro}, m)
|
|
||||||
of nkConverterDef:
|
|
||||||
result = matchSyms(c, n, {skConverter}, m)
|
|
||||||
of nkMethodDef:
|
|
||||||
result = matchSyms(c, n, {skMethod}, m)
|
|
||||||
of nkIteratorDef:
|
|
||||||
result = matchSyms(c, n, {skIterator}, m)
|
|
||||||
else:
|
|
||||||
# error was reported earlier.
|
|
||||||
result = false
|
|
||||||
|
|
||||||
proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invocation: PType): bool =
|
|
||||||
## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
|
|
||||||
## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fullfill the
|
|
||||||
## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
|
|
||||||
## (typeVar, instance) pairs. ('typeVar' is usually simply written as a generic 'T'.)
|
|
||||||
## 'invocation' can be nil for atomic concepts. For non-atomic concepts, it contains the
|
|
||||||
## `C[S, T]` parent type that we look for. We need this because we need to store bindings
|
|
||||||
## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
|
|
||||||
## we do not add any bindings at all on an unsuccessful match!
|
|
||||||
var m = MatchCon(inferred: @[], potentialImplementation: arg)
|
|
||||||
result = conceptMatchNode(c, concpt.n.lastSon, m)
|
|
||||||
if result:
|
|
||||||
for (a, b) in m.inferred:
|
|
||||||
if b.kind == tyGenericParam:
|
|
||||||
var dest = b
|
|
||||||
while true:
|
|
||||||
dest = existingBinding(m, dest)
|
|
||||||
if dest == nil or dest.kind != tyGenericParam: break
|
|
||||||
if dest != nil:
|
|
||||||
bindings.idTablePut(a, dest)
|
|
||||||
when logBindings: echo "A bind ", a, " ", dest
|
|
||||||
else:
|
|
||||||
bindings.idTablePut(a, b)
|
|
||||||
when logBindings: echo "B bind ", a, " ", b
|
|
||||||
# we have a match, so bind 'arg' itself to 'concpt':
|
|
||||||
bindings.idTablePut(concpt, arg)
|
|
||||||
# invocation != nil means we have a non-atomic concept:
|
|
||||||
if invocation != nil and arg.kind == tyGenericInst and invocation.len == arg.len-1:
|
|
||||||
# bind even more generic parameters
|
|
||||||
assert invocation.kind == tyGenericInvocation
|
|
||||||
for i in 1 ..< invocation.len:
|
|
||||||
bindings.idTablePut(invocation[i], arg[i])
|
|
||||||
|
|
@ -10,147 +10,81 @@
|
||||||
# This module handles the conditional symbols.
|
# This module handles the conditional symbols.
|
||||||
|
|
||||||
import
|
import
|
||||||
strtabs
|
strtabs, platform, strutils, idents
|
||||||
|
|
||||||
from options import Feature
|
# We need to use a StringTableRef here as defined symbols are always guaranteed
|
||||||
from lineinfos import hintMin, hintMax, warnMin, warnMax
|
# to be style insensitive. Otherwise hell would break lose.
|
||||||
|
var gSymbols: StringTableRef
|
||||||
|
|
||||||
proc defineSymbol*(symbols: StringTableRef; symbol: string, value: string = "true") =
|
const
|
||||||
symbols[symbol] = value
|
catNone = "false"
|
||||||
|
|
||||||
proc undefSymbol*(symbols: StringTableRef; symbol: string) =
|
proc defineSymbol*(symbol: string) =
|
||||||
symbols.del(symbol)
|
gSymbols[symbol] = "true"
|
||||||
|
|
||||||
#proc lookupSymbol*(symbols: StringTableRef; symbol: string): string =
|
proc undefSymbol*(symbol: string) =
|
||||||
# result = if isDefined(symbol): gSymbols[symbol] else: nil
|
gSymbols[symbol] = catNone
|
||||||
|
|
||||||
iterator definedSymbolNames*(symbols: StringTableRef): string =
|
proc isDefined*(symbol: string): bool =
|
||||||
for key in keys(symbols):
|
if gSymbols.hasKey(symbol):
|
||||||
yield key
|
result = gSymbols[symbol] != catNone
|
||||||
|
elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
|
||||||
|
result = true
|
||||||
|
elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0:
|
||||||
|
result = true
|
||||||
|
else:
|
||||||
|
case symbol.normalize
|
||||||
|
of "x86": result = targetCPU == cpuI386
|
||||||
|
of "itanium": result = targetCPU == cpuIa64
|
||||||
|
of "x8664": result = targetCPU == cpuAmd64
|
||||||
|
of "posix", "unix":
|
||||||
|
result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
|
||||||
|
osQnx, osAtari, osAix,
|
||||||
|
osHaiku, osVxWorks, osSolaris, osNetbsd,
|
||||||
|
osFreebsd, osOpenbsd, osMacosx}
|
||||||
|
of "bsd":
|
||||||
|
result = targetOS in {osNetbsd, osFreebsd, osOpenbsd}
|
||||||
|
of "emulatedthreadvars":
|
||||||
|
result = platform.OS[targetOS].props.contains(ospLacksThreadVars)
|
||||||
|
of "msdos": result = targetOS == osDos
|
||||||
|
of "mswindows", "win32": result = targetOS == osWindows
|
||||||
|
of "macintosh": result = targetOS in {osMacos, osMacosx}
|
||||||
|
of "sunos": result = targetOS == osSolaris
|
||||||
|
of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian
|
||||||
|
of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian
|
||||||
|
of "cpu8": result = CPU[targetCPU].bit == 8
|
||||||
|
of "cpu16": result = CPU[targetCPU].bit == 16
|
||||||
|
of "cpu32": result = CPU[targetCPU].bit == 32
|
||||||
|
of "cpu64": result = CPU[targetCPU].bit == 64
|
||||||
|
of "nimrawsetjmp":
|
||||||
|
result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd, osMacosx}
|
||||||
|
else: discard
|
||||||
|
|
||||||
proc countDefinedSymbols*(symbols: StringTableRef): int =
|
proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
|
||||||
symbols.len
|
|
||||||
|
|
||||||
proc initDefines*(symbols: StringTableRef) =
|
iterator definedSymbolNames*: string =
|
||||||
|
for key, val in pairs(gSymbols):
|
||||||
|
if val != catNone: yield key
|
||||||
|
|
||||||
|
proc countDefinedSymbols*(): int =
|
||||||
|
result = 0
|
||||||
|
for key, val in pairs(gSymbols):
|
||||||
|
if val != catNone: inc(result)
|
||||||
|
|
||||||
|
proc initDefines*() =
|
||||||
|
gSymbols = newStringTable(modeStyleInsensitive)
|
||||||
|
defineSymbol("nimrod") # 'nimrod' is always defined
|
||||||
# for bootstrapping purposes and old code:
|
# for bootstrapping purposes and old code:
|
||||||
template defineSymbol(s) = symbols.defineSymbol(s)
|
defineSymbol("nimhygiene")
|
||||||
defineSymbol("nimhygiene") # deadcode
|
defineSymbol("niminheritable")
|
||||||
defineSymbol("niminheritable") # deadcode
|
defineSymbol("nimmixin")
|
||||||
defineSymbol("nimmixin") # deadcode
|
defineSymbol("nimeffects")
|
||||||
defineSymbol("nimeffects") # deadcode
|
defineSymbol("nimbabel")
|
||||||
defineSymbol("nimbabel") # deadcode
|
defineSymbol("nimcomputedgoto")
|
||||||
defineSymbol("nimcomputedgoto") # deadcode
|
defineSymbol("nimunion")
|
||||||
defineSymbol("nimunion") # deadcode
|
defineSymbol("nimnewshared")
|
||||||
defineSymbol("nimnewshared") # deadcode
|
defineSymbol("nimrequiresnimframe")
|
||||||
defineSymbol("nimNewTypedesc") # deadcode
|
defineSymbol("nimparsebiggestfloatmagic")
|
||||||
defineSymbol("nimrequiresnimframe") # deadcode
|
defineSymbol("nimalias")
|
||||||
defineSymbol("nimparsebiggestfloatmagic") # deadcode
|
defineSymbol("nimlocks")
|
||||||
defineSymbol("nimalias") # deadcode
|
defineSymbol("nimnode")
|
||||||
defineSymbol("nimlocks") # deadcode
|
|
||||||
defineSymbol("nimnode") # deadcode pending `nimnode` reference in opengl package
|
|
||||||
# refs https://github.com/nim-lang/opengl/pull/79
|
|
||||||
defineSymbol("nimvarargstyped") # deadcode
|
|
||||||
defineSymbol("nimtypedescfixed") # deadcode
|
|
||||||
defineSymbol("nimKnowsNimvm") # deadcode
|
|
||||||
defineSymbol("nimArrIdx") # deadcode
|
|
||||||
defineSymbol("nimHasalignOf") # deadcode
|
|
||||||
defineSymbol("nimDistros") # deadcode
|
|
||||||
defineSymbol("nimHasCppDefine") # deadcode
|
|
||||||
defineSymbol("nimGenericInOutFlags") # deadcode
|
|
||||||
when false: defineSymbol("nimHasOpt") # deadcode
|
|
||||||
defineSymbol("nimNoArrayToCstringConversion") # deadcode
|
|
||||||
defineSymbol("nimHasRunnableExamples") # deadcode
|
|
||||||
defineSymbol("nimNewDot") # deadcode
|
|
||||||
defineSymbol("nimHasNilChecks") # deadcode
|
|
||||||
defineSymbol("nimSymKind") # deadcode
|
|
||||||
defineSymbol("nimVmEqIdent") # deadcode
|
|
||||||
defineSymbol("nimNoNil") # deadcode
|
|
||||||
defineSymbol("nimNoZeroTerminator") # deadcode
|
|
||||||
defineSymbol("nimNotNil") # deadcode
|
|
||||||
defineSymbol("nimVmExportFixed") # deadcode
|
|
||||||
defineSymbol("nimHasSymOwnerInMacro") # deadcode
|
|
||||||
defineSymbol("nimNewRuntime") # deadcode
|
|
||||||
defineSymbol("nimIncrSeqV3") # deadcode
|
|
||||||
defineSymbol("nimAshr") # deadcode
|
|
||||||
defineSymbol("nimNoNilSeqs") # deadcode
|
|
||||||
defineSymbol("nimNoNilSeqs2") # deadcode
|
|
||||||
defineSymbol("nimHasUserErrors") # deadcode
|
|
||||||
defineSymbol("nimUncheckedArrayTyp") # deadcode
|
|
||||||
defineSymbol("nimHasTypeof") # deadcode
|
|
||||||
defineSymbol("nimErrorProcCanHaveBody") # deadcode
|
|
||||||
defineSymbol("nimHasInstantiationOfInMacro") # deadcode
|
|
||||||
defineSymbol("nimHasHotCodeReloading") # deadcode
|
|
||||||
defineSymbol("nimHasNilSeqs") # deadcode
|
|
||||||
defineSymbol("nimHasSignatureHashInMacro") # deadcode
|
|
||||||
defineSymbol("nimHasDefault") # deadcode
|
|
||||||
defineSymbol("nimMacrosSizealignof") # deadcode
|
|
||||||
defineSymbol("nimNoZeroExtendMagic") # deadcode
|
|
||||||
defineSymbol("nimMacrosGetNodeId") # deadcode
|
|
||||||
defineSymbol("nimFixedForwardGeneric") # deadcode
|
|
||||||
defineSymbol("nimToOpenArrayCString") # deadcode
|
|
||||||
defineSymbol("nimHasUsed") # deadcode
|
|
||||||
defineSymbol("nimnomagic64") # deadcode
|
|
||||||
defineSymbol("nimNewShiftOps") # deadcode
|
|
||||||
defineSymbol("nimHasCursor") # deadcode
|
|
||||||
defineSymbol("nimAlignPragma") # deadcode
|
|
||||||
defineSymbol("nimHasExceptionsQuery") # deadcode
|
|
||||||
defineSymbol("nimHasIsNamedTuple") # deadcode
|
|
||||||
defineSymbol("nimHashOrdinalFixed") # deadcode
|
|
||||||
defineSymbol("nimHasSinkInference") # deadcode
|
|
||||||
defineSymbol("nimNewIntegerOps") # deadcode
|
|
||||||
defineSymbol("nimHasInvariant") # deadcode
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for f in Feature:
|
|
||||||
defineSymbol("nimHas" & $f)
|
|
||||||
|
|
||||||
for s in warnMin..warnMax:
|
|
||||||
defineSymbol("nimHasWarning" & $s)
|
|
||||||
for s in hintMin..hintMax:
|
|
||||||
defineSymbol("nimHasHint" & $s)
|
|
||||||
|
|
||||||
defineSymbol("nimFixedOwned")
|
|
||||||
defineSymbol("nimHasStyleChecks")
|
|
||||||
|
|
||||||
when defined(nimHasLibFFI):
|
|
||||||
# Renaming as we can't conflate input vs output define flags; e.g. this
|
|
||||||
# will report the right thing regardless of whether user adds
|
|
||||||
# `-d:nimHasLibFFI` in his user config.
|
|
||||||
defineSymbol("nimHasLibFFIEnabled") # deadcode
|
|
||||||
|
|
||||||
defineSymbol("nimHasStacktraceMsgs") # deadcode
|
|
||||||
defineSymbol("nimDoesntTrackDefects")
|
|
||||||
defineSymbol("nimHasLentIterators") # deadcode
|
|
||||||
defineSymbol("nimHasDeclaredMagic") # deadcode
|
|
||||||
defineSymbol("nimHasStacktracesModule") # deadcode
|
|
||||||
defineSymbol("nimHasEffectTraitsModule")
|
|
||||||
defineSymbol("nimHasCastPragmaBlocks")
|
|
||||||
defineSymbol("nimHasDeclaredLocs")
|
|
||||||
defineSymbol("nimHasJsBigIntBackend")
|
|
||||||
defineSymbol("nimHasWarningAsError")
|
|
||||||
defineSymbol("nimHasHintAsError")
|
|
||||||
defineSymbol("nimHasSpellSuggest")
|
|
||||||
defineSymbol("nimHasCustomLiterals")
|
|
||||||
defineSymbol("nimHasUnifiedTuple")
|
|
||||||
defineSymbol("nimHasIterable")
|
|
||||||
defineSymbol("nimHasTypeofVoid") # deadcode
|
|
||||||
defineSymbol("nimHasDragonBox") # deadcode
|
|
||||||
defineSymbol("nimHasHintAll")
|
|
||||||
defineSymbol("nimHasTrace")
|
|
||||||
defineSymbol("nimHasEffectsOf")
|
|
||||||
|
|
||||||
defineSymbol("nimHasEnforceNoRaises")
|
|
||||||
defineSymbol("nimHasTopDownInference")
|
|
||||||
defineSymbol("nimHasTemplateRedefinitionPragma")
|
|
||||||
defineSymbol("nimHasCstringCase")
|
|
||||||
defineSymbol("nimHasCallsitePragma")
|
|
||||||
defineSymbol("nimHasAmbiguousEnumHint")
|
|
||||||
|
|
||||||
defineSymbol("nimHasWarnCastSizes")
|
|
||||||
defineSymbol("nimHasOutParams")
|
|
||||||
defineSymbol("nimHasSystemRaisesDefect")
|
|
||||||
defineSymbol("nimHasWarnUnnamedBreak")
|
|
||||||
defineSymbol("nimHasGenericDefine")
|
|
||||||
defineSymbol("nimHasDefineAliases")
|
|
||||||
defineSymbol("nimHasWarnBareExcept")
|
|
||||||
|
|
|
||||||
147
compiler/crc.nim
Normal file
147
compiler/crc.nim
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
import
|
||||||
|
strutils
|
||||||
|
|
||||||
|
type
|
||||||
|
TCrc32* = int32
|
||||||
|
|
||||||
|
const
|
||||||
|
InitCrc32* = TCrc32(- 1)
|
||||||
|
InitAdler32* = int32(1)
|
||||||
|
|
||||||
|
proc updateCrc32*(val: int8, crc: TCrc32): TCrc32 {.inline.}
|
||||||
|
proc updateCrc32*(val: char, crc: TCrc32): TCrc32 {.inline.}
|
||||||
|
proc crcFromBuf*(buf: pointer, length: int): TCrc32
|
||||||
|
proc strCrc32*(s: string): TCrc32
|
||||||
|
proc crcFromFile*(filename: string): TCrc32
|
||||||
|
proc updateAdler32*(adler: int32, buf: pointer, length: int): int32
|
||||||
|
# implementation
|
||||||
|
|
||||||
|
type
|
||||||
|
TCRC_TabEntry = int
|
||||||
|
|
||||||
|
const
|
||||||
|
crc32table: array[0..255, TCRC_TabEntry] = [0, 1996959894, - 301047508,
|
||||||
|
- 1727442502, 124634137, 1886057615, - 379345611, - 1637575261, 249268274,
|
||||||
|
2044508324, - 522852066, - 1747789432, 162941995, 2125561021, - 407360249,
|
||||||
|
- 1866523247, 498536548, 1789927666, - 205950648, - 2067906082, 450548861,
|
||||||
|
1843258603, - 187386543, - 2083289657, 325883990, 1684777152, - 43845254,
|
||||||
|
- 1973040660, 335633487, 1661365465, - 99664541, - 1928851979, 997073096,
|
||||||
|
1281953886, - 715111964, - 1570279054, 1006888145, 1258607687, - 770865667,
|
||||||
|
- 1526024853, 901097722, 1119000684, - 608450090, - 1396901568, 853044451,
|
||||||
|
1172266101, - 589951537, - 1412350631, 651767980, 1373503546, - 925412992,
|
||||||
|
- 1076862698, 565507253, 1454621731, - 809855591, - 1195530993, 671266974,
|
||||||
|
1594198024, - 972236366, - 1324619484, 795835527, 1483230225, - 1050600021,
|
||||||
|
- 1234817731, 1994146192, 31158534, - 1731059524, - 271249366, 1907459465,
|
||||||
|
112637215, - 1614814043, - 390540237, 2013776290, 251722036, - 1777751922,
|
||||||
|
- 519137256, 2137656763, 141376813, - 1855689577, - 429695999, 1802195444,
|
||||||
|
476864866, - 2056965928, - 228458418, 1812370925, 453092731, - 2113342271,
|
||||||
|
- 183516073, 1706088902, 314042704, - 1950435094, - 54949764, 1658658271,
|
||||||
|
366619977, - 1932296973, - 69972891, 1303535960, 984961486, - 1547960204,
|
||||||
|
- 725929758, 1256170817, 1037604311, - 1529756563, - 740887301, 1131014506,
|
||||||
|
879679996, - 1385723834, - 631195440, 1141124467, 855842277, - 1442165665,
|
||||||
|
- 586318647, 1342533948, 654459306, - 1106571248, - 921952122, 1466479909,
|
||||||
|
544179635, - 1184443383, - 832445281, 1591671054, 702138776, - 1328506846,
|
||||||
|
- 942167884, 1504918807, 783551873, - 1212326853, - 1061524307, - 306674912,
|
||||||
|
- 1698712650, 62317068, 1957810842, - 355121351, - 1647151185, 81470997,
|
||||||
|
1943803523, - 480048366, - 1805370492, 225274430, 2053790376, - 468791541,
|
||||||
|
- 1828061283, 167816743, 2097651377, - 267414716, - 2029476910, 503444072,
|
||||||
|
1762050814, - 144550051, - 2140837941, 426522225, 1852507879, - 19653770,
|
||||||
|
- 1982649376, 282753626, 1742555852, - 105259153, - 1900089351, 397917763,
|
||||||
|
1622183637, - 690576408, - 1580100738, 953729732, 1340076626, - 776247311,
|
||||||
|
- 1497606297, 1068828381, 1219638859, - 670225446, - 1358292148, 906185462,
|
||||||
|
1090812512, - 547295293, - 1469587627, 829329135, 1181335161, - 882789492,
|
||||||
|
- 1134132454, 628085408, 1382605366, - 871598187, - 1156888829, 570562233,
|
||||||
|
1426400815, - 977650754, - 1296233688, 733239954, 1555261956, - 1026031705,
|
||||||
|
- 1244606671, 752459403, 1541320221, - 1687895376, - 328994266, 1969922972,
|
||||||
|
40735498, - 1677130071, - 351390145, 1913087877, 83908371, - 1782625662,
|
||||||
|
- 491226604, 2075208622, 213261112, - 1831694693, - 438977011, 2094854071,
|
||||||
|
198958881, - 2032938284, - 237706686, 1759359992, 534414190, - 2118248755,
|
||||||
|
- 155638181, 1873836001, 414664567, - 2012718362, - 15766928, 1711684554,
|
||||||
|
285281116, - 1889165569, - 127750551, 1634467795, 376229701, - 1609899400,
|
||||||
|
- 686959890, 1308918612, 956543938, - 1486412191, - 799009033, 1231636301,
|
||||||
|
1047427035, - 1362007478, - 640263460, 1088359270, 936918000, - 1447252397,
|
||||||
|
- 558129467, 1202900863, 817233897, - 1111625188, - 893730166, 1404277552,
|
||||||
|
615818150, - 1160759803, - 841546093, 1423857449, 601450431, - 1285129682,
|
||||||
|
- 1000256840, 1567103746, 711928724, - 1274298825, - 1022587231, 1510334235,
|
||||||
|
755167117]
|
||||||
|
|
||||||
|
proc updateCrc32(val: int8, crc: TCrc32): TCrc32 =
|
||||||
|
result = TCrc32(crc32table[(int(crc) xor (int(val) and 0x000000FF)) and
|
||||||
|
0x000000FF]) xor (crc shr TCrc32(8))
|
||||||
|
|
||||||
|
proc updateCrc32(val: char, crc: TCrc32): TCrc32 =
|
||||||
|
result = updateCrc32(toU8(ord(val)), crc)
|
||||||
|
|
||||||
|
proc strCrc32(s: string): TCrc32 =
|
||||||
|
result = InitCrc32
|
||||||
|
for i in countup(0, len(s) - 1): result = updateCrc32(s[i], result)
|
||||||
|
|
||||||
|
proc `><`*(c: TCrc32, s: string): TCrc32 =
|
||||||
|
result = c
|
||||||
|
for i in 0..len(s)-1: result = updateCrc32(s[i], result)
|
||||||
|
|
||||||
|
type
|
||||||
|
TByteArray = array[0..10000000, int8]
|
||||||
|
PByteArray = ref TByteArray
|
||||||
|
|
||||||
|
proc crcFromBuf(buf: pointer, length: int): TCrc32 =
|
||||||
|
var p = cast[PByteArray](buf)
|
||||||
|
result = InitCrc32
|
||||||
|
for i in countup(0, length - 1): result = updateCrc32(p[i], result)
|
||||||
|
|
||||||
|
proc crcFromFile(filename: string): TCrc32 =
|
||||||
|
const
|
||||||
|
bufSize = 8000 # don't use 8K for the memory allocator!
|
||||||
|
var
|
||||||
|
bin: File
|
||||||
|
result = InitCrc32
|
||||||
|
if not open(bin, filename):
|
||||||
|
return # not equal if file does not exist
|
||||||
|
var buf = alloc(bufSize)
|
||||||
|
var p = cast[PByteArray](buf)
|
||||||
|
while true:
|
||||||
|
var readBytes = readBuffer(bin, buf, bufSize)
|
||||||
|
for i in countup(0, readBytes - 1): result = updateCrc32(p[i], result)
|
||||||
|
if readBytes != bufSize: break
|
||||||
|
dealloc(buf)
|
||||||
|
close(bin)
|
||||||
|
|
||||||
|
const
|
||||||
|
base = int32(65521) # largest prime smaller than 65536
|
||||||
|
# NMAX = 5552; original code with unsigned 32 bit integer
|
||||||
|
# NMAX is the largest n
|
||||||
|
# such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
|
||||||
|
nmax = 3854 # code with signed 32 bit integer
|
||||||
|
# NMAX is the largest n such that
|
||||||
|
# 255n(n+1)/2 + (n+1)(BASE-1) <= 2^31-1
|
||||||
|
# The penalty is the time loss in the extra MOD-calls.
|
||||||
|
|
||||||
|
proc updateAdler32(adler: int32, buf: pointer, length: int): int32 =
|
||||||
|
var
|
||||||
|
s1, s2: int32
|
||||||
|
L, k, b: int
|
||||||
|
s1 = adler and int32(0x0000FFFF)
|
||||||
|
s2 = (adler shr int32(16)) and int32(0x0000FFFF)
|
||||||
|
L = length
|
||||||
|
b = 0
|
||||||
|
while (L > 0):
|
||||||
|
if L < nmax: k = L
|
||||||
|
else: k = nmax
|
||||||
|
dec(L, k)
|
||||||
|
while (k > 0):
|
||||||
|
s1 = s1 +% int32((cast[cstring](buf))[b])
|
||||||
|
s2 = s2 +% s1
|
||||||
|
inc(b)
|
||||||
|
dec(k)
|
||||||
|
s1 = `%%`(s1, base)
|
||||||
|
s2 = `%%`(s2, base)
|
||||||
|
result = (s2 shl int32(16)) or s1
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2017 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## The compiler can generate debuginfo to help debuggers in translating back
|
|
||||||
## from C/C++/JS code to Nim. The data structure has been designed to produce
|
|
||||||
## something useful with Nim's marshal module.
|
|
||||||
|
|
||||||
import sighashes
|
|
||||||
|
|
||||||
type
|
|
||||||
FilenameMapping* = object
|
|
||||||
package*, file*: string
|
|
||||||
mangled*: SigHash
|
|
||||||
EnumDesc* = object
|
|
||||||
size*: int
|
|
||||||
owner*: SigHash
|
|
||||||
id*: int
|
|
||||||
name*: string
|
|
||||||
values*: seq[(string, int)]
|
|
||||||
DebugInfo* = object
|
|
||||||
version*: int
|
|
||||||
files*: seq[FilenameMapping]
|
|
||||||
enums*: seq[EnumDesc]
|
|
||||||
conflicts*: bool
|
|
||||||
|
|
||||||
proc sdbmHash(package, file: string): SigHash =
|
|
||||||
result = 0
|
|
||||||
for i in 0..<package.len:
|
|
||||||
result &= package[i]
|
|
||||||
result &= '.'
|
|
||||||
for i in 0..<file.len:
|
|
||||||
result &= file[i]
|
|
||||||
|
|
||||||
proc register*(self: var DebugInfo; package, file: string): SigHash =
|
|
||||||
result = sdbmHash(package, file)
|
|
||||||
for f in self.files:
|
|
||||||
if f.mangled == result:
|
|
||||||
if f.package == package and f.file == file: return
|
|
||||||
self.conflicts = true
|
|
||||||
break
|
|
||||||
self.files.add(FilenameMapping(package: package, file: file, mangled: result))
|
|
||||||
|
|
||||||
proc hasEnum*(self: DebugInfo; ename: string; id: int; owner: SigHash): bool =
|
|
||||||
for en in self.enums:
|
|
||||||
if en.owner == owner and en.name == ename and en.id == id: return true
|
|
||||||
|
|
||||||
proc registerEnum*(self: var DebugInfo; ed: EnumDesc) =
|
|
||||||
self.enums.add ed
|
|
||||||
|
|
||||||
proc init*(self: var DebugInfo) =
|
|
||||||
self.version = 1
|
|
||||||
self.files = @[]
|
|
||||||
self.enums = @[]
|
|
||||||
|
|
||||||
var gDebugInfo*: DebugInfo
|
|
||||||
debuginfo.init gDebugInfo
|
|
||||||
|
|
||||||
import marshal, streams
|
|
||||||
|
|
||||||
proc writeDebugInfo*(self: var DebugInfo; file: string) =
|
|
||||||
let s = newFileStream(file, fmWrite)
|
|
||||||
store(s, self)
|
|
||||||
s.close
|
|
||||||
|
|
||||||
proc writeDebugInfo*(file: string) = writeDebugInfo(gDebugInfo, file)
|
|
||||||
|
|
||||||
proc loadDebugInfo*(self: var DebugInfo; file: string) =
|
|
||||||
let s = newFileStream(file, fmRead)
|
|
||||||
load(s, self)
|
|
||||||
s.close
|
|
||||||
|
|
||||||
proc loadDebugInfo*(file: string) = loadDebugInfo(gDebugInfo, file)
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
##[
|
|
||||||
Utilities to help with debugging nim compiler.
|
|
||||||
|
|
||||||
Experimental API, subject to change.
|
|
||||||
]##
|
|
||||||
|
|
||||||
#[
|
|
||||||
## example
|
|
||||||
useful debugging flags:
|
|
||||||
--stacktrace -d:debug -d:nimDebugUtils
|
|
||||||
nim c -o:bin/nim_temp --stacktrace -d:debug -d:nimDebugUtils compiler/nim
|
|
||||||
|
|
||||||
## future work
|
|
||||||
* expose and improve astalgo.debug, replacing it by std/prettyprints,
|
|
||||||
refs https://github.com/nim-lang/RFCs/issues/385
|
|
||||||
]#
|
|
||||||
|
|
||||||
import options
|
|
||||||
import std/wrapnils
|
|
||||||
export wrapnils
|
|
||||||
# allows using things like: `?.n.sym.typ.len`
|
|
||||||
|
|
||||||
import std/stackframes
|
|
||||||
export stackframes
|
|
||||||
# allows using things like: `setFrameMsg c.config$n.info & " " & $n.kind`
|
|
||||||
# which doesn't log, but augments stacktrace with side channel information
|
|
||||||
|
|
||||||
var conf0: ConfigRef
|
|
||||||
|
|
||||||
proc onNewConfigRef*(conf: ConfigRef) {.inline.} =
|
|
||||||
## Caches `conf`, which can be retrieved with `getConfigRef`.
|
|
||||||
## This avoids having to forward `conf` all the way down the call chain to
|
|
||||||
## procs that need it during a debugging session.
|
|
||||||
conf0 = conf
|
|
||||||
|
|
||||||
proc getConfigRef*(): ConfigRef =
|
|
||||||
## nil, if -d:nimDebugUtils wasn't specified
|
|
||||||
result = conf0
|
|
||||||
|
|
||||||
proc isCompilerDebug*(): bool =
|
|
||||||
##[
|
|
||||||
Provides a simple way for user code to enable/disable logging in the compiler
|
|
||||||
in a granular way. This can then be used in the compiler as follows:
|
|
||||||
```nim
|
|
||||||
if isCompilerDebug():
|
|
||||||
echo ?.n.sym.typ.len
|
|
||||||
```
|
|
||||||
]##
|
|
||||||
runnableExamples:
|
|
||||||
proc main =
|
|
||||||
echo 2
|
|
||||||
{.define(nimCompilerDebug).}
|
|
||||||
echo 3.5 # code section in which `isCompilerDebug` will be true
|
|
||||||
{.undef(nimCompilerDebug).}
|
|
||||||
echo 'x'
|
|
||||||
conf0.isDefined("nimCompilerDebug")
|
|
||||||
|
|
||||||
proc enteringDebugSection*() {.exportc, dynlib.} =
|
|
||||||
## Provides a way for native debuggers to enable breakpoints, watchpoints, etc
|
|
||||||
## when code of interest is being compiled.
|
|
||||||
##
|
|
||||||
## Set your debugger to break on entering `nimCompilerIsEnteringDebugSection`
|
|
||||||
## and then execute a desired command.
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc exitingDebugSection*() {.exportc, dynlib.} =
|
|
||||||
## Provides a way for native debuggers to disable breakpoints, watchpoints, etc
|
|
||||||
## when code of interest is no longer being compiled.
|
|
||||||
##
|
|
||||||
## Set your debugger to break on entering `exitingDebugSection`
|
|
||||||
## and then execute a desired command.
|
|
||||||
discard
|
|
||||||
|
|
@ -9,103 +9,48 @@
|
||||||
|
|
||||||
# This module implements a dependency file generator.
|
# This module implements a dependency file generator.
|
||||||
|
|
||||||
import options, ast, ropes, pathutils, msgs, lineinfos
|
import
|
||||||
|
os, options, ast, astalgo, msgs, ropes, idents, passes, importer
|
||||||
import modulegraphs
|
|
||||||
|
|
||||||
import std/[os, parseutils]
|
|
||||||
import strutils except addf
|
|
||||||
import std/private/globs
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
|
proc generateDot*(project: string)
|
||||||
|
|
||||||
type
|
type
|
||||||
TGen = object of PPassContext
|
TGen = object of TPassContext
|
||||||
module: PSym
|
module*: PSym
|
||||||
config: ConfigRef
|
|
||||||
graph: ModuleGraph
|
|
||||||
PGen = ref TGen
|
PGen = ref TGen
|
||||||
|
|
||||||
Backend = ref object of RootRef
|
var gDotGraph: Rope # the generated DOT file; we need a global variable
|
||||||
dotGraph: Rope
|
|
||||||
|
|
||||||
proc addDependencyAux(b: Backend; importing, imported: string) =
|
proc addDependencyAux(importing, imported: string) =
|
||||||
b.dotGraph.addf("\"$1\" -> \"$2\";$n", [rope(importing), rope(imported)])
|
addf(gDotGraph, "$1 -> $2;$n", [rope(importing), rope(imported)])
|
||||||
# s1 -> s2_4[label="[0-9]"];
|
# s1 -> s2_4[label="[0-9]"];
|
||||||
|
|
||||||
proc toNimblePath(s: string, isStdlib: bool): string =
|
proc addDotDependency(c: PPassContext, n: PNode): PNode =
|
||||||
const stdPrefix = "std/"
|
|
||||||
const pkgPrefix = "pkg/"
|
|
||||||
if isStdlib:
|
|
||||||
let sub = "lib/"
|
|
||||||
var start = s.find(sub)
|
|
||||||
if start < 0:
|
|
||||||
doAssert false
|
|
||||||
else:
|
|
||||||
start += sub.len
|
|
||||||
let base = s[start..^1]
|
|
||||||
|
|
||||||
if base.startsWith("system") or base.startsWith("std"):
|
|
||||||
result = base
|
|
||||||
else:
|
|
||||||
for dir in stdlibDirs:
|
|
||||||
if base.startsWith(dir):
|
|
||||||
return stdPrefix & base.splitFile.name
|
|
||||||
|
|
||||||
result = stdPrefix & base
|
|
||||||
else:
|
|
||||||
var sub = getEnv("NIMBLE_DIR")
|
|
||||||
if sub.len == 0:
|
|
||||||
sub = ".nimble/pkgs/"
|
|
||||||
else:
|
|
||||||
sub.add "/pkgs/"
|
|
||||||
var start = s.find(sub)
|
|
||||||
if start < 0:
|
|
||||||
result = s
|
|
||||||
else:
|
|
||||||
start += sub.len
|
|
||||||
start += skipUntil(s, '/', start)
|
|
||||||
start += 1
|
|
||||||
result = pkgPrefix & s[start..^1]
|
|
||||||
|
|
||||||
proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) =
|
|
||||||
doAssert n.kind == nkSym, $n.kind
|
|
||||||
|
|
||||||
let path = splitFile(toProjPath(g.config, n.sym.position.FileIndex))
|
|
||||||
let modulePath = splitFile(toProjPath(g.config, g.module.position.FileIndex))
|
|
||||||
let parent = nativeToUnixPath(modulePath.dir / modulePath.name).toNimblePath(belongsToStdlib(g.graph, g.module))
|
|
||||||
let child = nativeToUnixPath(path.dir / path.name).toNimblePath(belongsToStdlib(g.graph, n.sym))
|
|
||||||
addDependencyAux(b, parent, child)
|
|
||||||
|
|
||||||
proc addDotDependency*(c: PPassContext, n: PNode): PNode =
|
|
||||||
result = n
|
result = n
|
||||||
let g = PGen(c)
|
var g = PGen(c)
|
||||||
let b = Backend(g.graph.backend)
|
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkImportStmt:
|
of nkImportStmt:
|
||||||
for i in 0..<n.len:
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
addDependency(c, g, b, n[i])
|
var imported = getModuleName(n.sons[i])
|
||||||
|
addDependencyAux(g.module.name.s, imported)
|
||||||
of nkFromStmt, nkImportExceptStmt:
|
of nkFromStmt, nkImportExceptStmt:
|
||||||
addDependency(c, g, b, n[0])
|
var imported = getModuleName(n.sons[0])
|
||||||
|
addDependencyAux(g.module.name.s, imported)
|
||||||
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
|
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
|
||||||
for i in 0..<n.len: discard addDotDependency(c, n[i])
|
for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i])
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
|
proc generateDot(project: string) =
|
||||||
let b = Backend(graph.backend)
|
writeRope("digraph $1 {$n$2}$n" % [
|
||||||
discard writeRope("digraph $1 {$n$2}$n" % [
|
rope(changeFileExt(extractFilename(project), "")), gDotGraph],
|
||||||
rope(project.splitFile.name), b.dotGraph],
|
|
||||||
changeFileExt(project, "dot"))
|
changeFileExt(project, "dot"))
|
||||||
|
|
||||||
proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
proc myOpen(module: PSym): PPassContext =
|
||||||
var g: PGen
|
var g: PGen
|
||||||
new(g)
|
new(g)
|
||||||
g.module = module
|
g.module = module
|
||||||
g.config = graph.config
|
|
||||||
g.graph = graph
|
|
||||||
if graph.backend == nil:
|
|
||||||
graph.backend = Backend(dotGraph: "")
|
|
||||||
result = g
|
result = g
|
||||||
|
|
||||||
|
const gendependPass* = makePass(open = myOpen, process = addDotDependency)
|
||||||
|
|
||||||
|
|
|
||||||
501
compiler/dfa.nim
501
compiler/dfa.nim
|
|
@ -1,501 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2017 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Data flow analysis for Nim.
|
|
||||||
## We transform the AST into a linear list of instructions first to
|
|
||||||
## make this easier to handle: There are only 3 different branching
|
|
||||||
## instructions: 'goto X' is an unconditional goto, 'fork X'
|
|
||||||
## is a conditional goto (either the next instruction or 'X' can be
|
|
||||||
## taken), 'loop X' is the only jump that jumps back.
|
|
||||||
##
|
|
||||||
## Exhaustive case statements are translated
|
|
||||||
## so that the last branch is transformed into an 'else' branch.
|
|
||||||
## ``return`` and ``break`` are all covered by 'goto'.
|
|
||||||
##
|
|
||||||
## The data structures and algorithms used here are inspired by
|
|
||||||
## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
|
|
||||||
## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
|
|
||||||
|
|
||||||
import ast, intsets, lineinfos, renderer, aliasanalysis
|
|
||||||
import std/private/asciitables
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
|
||||||
InstrKind* = enum
|
|
||||||
goto, loop, fork, def, use
|
|
||||||
Instr* = object
|
|
||||||
case kind*: InstrKind
|
|
||||||
of goto, fork, loop: dest*: int
|
|
||||||
of def, use:
|
|
||||||
n*: PNode # contains the def/use location.
|
|
||||||
|
|
||||||
ControlFlowGraph* = seq[Instr]
|
|
||||||
|
|
||||||
TPosition = distinct int
|
|
||||||
|
|
||||||
TBlock = object
|
|
||||||
case isTryBlock: bool
|
|
||||||
of false:
|
|
||||||
label: PSym
|
|
||||||
breakFixups: seq[(TPosition, seq[PNode])] #Contains the gotos for the breaks along with their pending finales
|
|
||||||
of true:
|
|
||||||
finale: PNode
|
|
||||||
raiseFixups: seq[TPosition] #Contains the gotos for the raises
|
|
||||||
|
|
||||||
Con = object
|
|
||||||
code: ControlFlowGraph
|
|
||||||
inTryStmt, interestingInstructions: int
|
|
||||||
blocks: seq[TBlock]
|
|
||||||
owner: PSym
|
|
||||||
root: PSym
|
|
||||||
|
|
||||||
proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
|
|
||||||
# for debugging purposes
|
|
||||||
# first iteration: compute all necessary labels:
|
|
||||||
var jumpTargets = initIntSet()
|
|
||||||
let last = if last < 0: c.len-1 else: min(last, c.len-1)
|
|
||||||
for i in start..last:
|
|
||||||
if c[i].kind in {goto, fork, loop}:
|
|
||||||
jumpTargets.incl(i+c[i].dest)
|
|
||||||
var i = start
|
|
||||||
while i <= last:
|
|
||||||
if i in jumpTargets: result.add("L" & $i & ":\n")
|
|
||||||
result.add "\t"
|
|
||||||
result.add ($i & " " & $c[i].kind)
|
|
||||||
result.add "\t"
|
|
||||||
case c[i].kind
|
|
||||||
of def, use:
|
|
||||||
result.add renderTree(c[i].n)
|
|
||||||
result.add("\t#")
|
|
||||||
result.add($c[i].n.info.line)
|
|
||||||
result.add("\n")
|
|
||||||
of goto, fork, loop:
|
|
||||||
result.add "L"
|
|
||||||
result.addInt c[i].dest+i
|
|
||||||
inc i
|
|
||||||
if i in jumpTargets: result.add("L" & $i & ": End\n")
|
|
||||||
|
|
||||||
proc echoCfg*(c: ControlFlowGraph; start = 0; last = -1) {.deprecated.} =
|
|
||||||
## echos the ControlFlowGraph for debugging purposes.
|
|
||||||
echo codeListing(c, start, last).alignTable
|
|
||||||
|
|
||||||
proc forkI(c: var Con): TPosition =
|
|
||||||
result = TPosition(c.code.len)
|
|
||||||
c.code.add Instr(kind: fork, dest: 0)
|
|
||||||
|
|
||||||
proc gotoI(c: var Con): TPosition =
|
|
||||||
result = TPosition(c.code.len)
|
|
||||||
c.code.add Instr(kind: goto, dest: 0)
|
|
||||||
|
|
||||||
proc genLabel(c: Con): TPosition = TPosition(c.code.len)
|
|
||||||
|
|
||||||
template checkedDistance(dist): int =
|
|
||||||
doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2
|
|
||||||
dist
|
|
||||||
|
|
||||||
proc jmpBack(c: var Con, p = TPosition(0)) =
|
|
||||||
c.code.add Instr(kind: loop, dest: checkedDistance(p.int - c.code.len))
|
|
||||||
|
|
||||||
proc patch(c: var Con, p: TPosition) =
|
|
||||||
# patch with current index
|
|
||||||
c.code[p.int].dest = checkedDistance(c.code.len - p.int)
|
|
||||||
|
|
||||||
proc gen(c: var Con; n: PNode)
|
|
||||||
|
|
||||||
proc popBlock(c: var Con; oldLen: int) =
|
|
||||||
var exits: seq[TPosition]
|
|
||||||
exits.add c.gotoI()
|
|
||||||
for f in c.blocks[oldLen].breakFixups:
|
|
||||||
c.patch(f[0])
|
|
||||||
for finale in f[1]:
|
|
||||||
c.gen(finale)
|
|
||||||
exits.add c.gotoI()
|
|
||||||
for e in exits:
|
|
||||||
c.patch e
|
|
||||||
c.blocks.setLen(oldLen)
|
|
||||||
|
|
||||||
template withBlock(labl: PSym; body: untyped) =
|
|
||||||
let oldLen = c.blocks.len
|
|
||||||
c.blocks.add TBlock(isTryBlock: false, label: labl)
|
|
||||||
body
|
|
||||||
popBlock(c, oldLen)
|
|
||||||
|
|
||||||
proc isTrue(n: PNode): bool =
|
|
||||||
n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
|
|
||||||
n.kind == nkIntLit and n.intVal != 0
|
|
||||||
|
|
||||||
template forkT(body) =
|
|
||||||
let lab1 = c.forkI()
|
|
||||||
body
|
|
||||||
c.patch(lab1)
|
|
||||||
|
|
||||||
proc genWhile(c: var Con; n: PNode) =
|
|
||||||
# lab1:
|
|
||||||
# cond, tmp
|
|
||||||
# fork tmp, lab2
|
|
||||||
# body
|
|
||||||
# jmp lab1
|
|
||||||
# lab2:
|
|
||||||
let lab1 = c.genLabel
|
|
||||||
withBlock(nil):
|
|
||||||
if isTrue(n[0]):
|
|
||||||
c.gen(n[1])
|
|
||||||
c.jmpBack(lab1)
|
|
||||||
else:
|
|
||||||
c.gen(n[0])
|
|
||||||
forkT:
|
|
||||||
c.gen(n[1])
|
|
||||||
c.jmpBack(lab1)
|
|
||||||
|
|
||||||
proc genIf(c: var Con, n: PNode) =
|
|
||||||
#[
|
|
||||||
|
|
||||||
if cond:
|
|
||||||
A
|
|
||||||
elif condB:
|
|
||||||
B
|
|
||||||
elif condC:
|
|
||||||
C
|
|
||||||
else:
|
|
||||||
D
|
|
||||||
|
|
||||||
cond
|
|
||||||
fork lab1
|
|
||||||
A
|
|
||||||
goto Lend
|
|
||||||
lab1:
|
|
||||||
condB
|
|
||||||
fork lab2
|
|
||||||
B
|
|
||||||
goto Lend2
|
|
||||||
lab2:
|
|
||||||
condC
|
|
||||||
fork L3
|
|
||||||
C
|
|
||||||
goto Lend3
|
|
||||||
L3:
|
|
||||||
D
|
|
||||||
goto Lend3 # not eliminated to simplify the join generation
|
|
||||||
Lend3:
|
|
||||||
join F3
|
|
||||||
Lend2:
|
|
||||||
join F2
|
|
||||||
Lend:
|
|
||||||
join F1
|
|
||||||
|
|
||||||
]#
|
|
||||||
var endings: seq[TPosition] = @[]
|
|
||||||
let oldInteresting = c.interestingInstructions
|
|
||||||
let oldLen = c.code.len
|
|
||||||
|
|
||||||
for i in 0..<n.len:
|
|
||||||
let it = n[i]
|
|
||||||
c.gen(it[0])
|
|
||||||
if it.len == 2:
|
|
||||||
forkT:
|
|
||||||
c.gen(it.lastSon)
|
|
||||||
endings.add c.gotoI()
|
|
||||||
|
|
||||||
if oldInteresting == c.interestingInstructions:
|
|
||||||
setLen c.code, oldLen
|
|
||||||
else:
|
|
||||||
for i in countdown(endings.high, 0):
|
|
||||||
c.patch(endings[i])
|
|
||||||
|
|
||||||
proc genAndOr(c: var Con; n: PNode) =
|
|
||||||
# asgn dest, a
|
|
||||||
# fork lab1
|
|
||||||
# asgn dest, b
|
|
||||||
# lab1:
|
|
||||||
# join F1
|
|
||||||
c.gen(n[1])
|
|
||||||
forkT:
|
|
||||||
c.gen(n[2])
|
|
||||||
|
|
||||||
proc genCase(c: var Con; n: PNode) =
|
|
||||||
# if (!expr1) goto lab1;
|
|
||||||
# thenPart
|
|
||||||
# goto LEnd
|
|
||||||
# lab1:
|
|
||||||
# if (!expr2) goto lab2;
|
|
||||||
# thenPart2
|
|
||||||
# goto LEnd
|
|
||||||
# lab2:
|
|
||||||
# elsePart
|
|
||||||
# Lend:
|
|
||||||
let isExhaustive = skipTypes(n[0].typ,
|
|
||||||
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring}
|
|
||||||
|
|
||||||
var endings: seq[TPosition] = @[]
|
|
||||||
c.gen(n[0])
|
|
||||||
let oldInteresting = c.interestingInstructions
|
|
||||||
let oldLen = c.code.len
|
|
||||||
for i in 1..<n.len:
|
|
||||||
let it = n[i]
|
|
||||||
if it.len == 1 or (i == n.len-1 and isExhaustive):
|
|
||||||
# treat the last branch as 'else' if this is an exhaustive case statement.
|
|
||||||
c.gen(it.lastSon)
|
|
||||||
else:
|
|
||||||
forkT:
|
|
||||||
c.gen(it.lastSon)
|
|
||||||
endings.add c.gotoI()
|
|
||||||
|
|
||||||
if oldInteresting == c.interestingInstructions:
|
|
||||||
setLen c.code, oldLen
|
|
||||||
else:
|
|
||||||
for i in countdown(endings.high, 0):
|
|
||||||
c.patch(endings[i])
|
|
||||||
|
|
||||||
proc genBlock(c: var Con; n: PNode) =
|
|
||||||
withBlock(n[0].sym):
|
|
||||||
c.gen(n[1])
|
|
||||||
|
|
||||||
proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
|
|
||||||
let lab1 = c.gotoI()
|
|
||||||
if c.blocks[i].isTryBlock:
|
|
||||||
c.blocks[i].raiseFixups.add lab1
|
|
||||||
else:
|
|
||||||
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
|
|
||||||
|
|
||||||
c.blocks[i].breakFixups.add (lab1, trailingFinales)
|
|
||||||
|
|
||||||
proc genBreak(c: var Con; n: PNode) =
|
|
||||||
inc c.interestingInstructions
|
|
||||||
if n[0].kind == nkSym:
|
|
||||||
for i in countdown(c.blocks.high, 0):
|
|
||||||
if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
|
|
||||||
genBreakOrRaiseAux(c, i, n)
|
|
||||||
return
|
|
||||||
#globalError(n.info, "VM problem: cannot find 'break' target")
|
|
||||||
else:
|
|
||||||
for i in countdown(c.blocks.high, 0):
|
|
||||||
if not c.blocks[i].isTryBlock:
|
|
||||||
genBreakOrRaiseAux(c, i, n)
|
|
||||||
return
|
|
||||||
|
|
||||||
proc genTry(c: var Con; n: PNode) =
|
|
||||||
var endings: seq[TPosition] = @[]
|
|
||||||
|
|
||||||
let oldLen = c.blocks.len
|
|
||||||
c.blocks.add TBlock(isTryBlock: true, finale: if n[^1].kind == nkFinally: n[^1] else: newNode(nkEmpty))
|
|
||||||
|
|
||||||
inc c.inTryStmt
|
|
||||||
c.gen(n[0])
|
|
||||||
dec c.inTryStmt
|
|
||||||
|
|
||||||
for f in c.blocks[oldLen].raiseFixups:
|
|
||||||
c.patch(f)
|
|
||||||
|
|
||||||
c.blocks.setLen oldLen
|
|
||||||
|
|
||||||
for i in 1..<n.len:
|
|
||||||
let it = n[i]
|
|
||||||
if it.kind != nkFinally:
|
|
||||||
forkT:
|
|
||||||
c.gen(it.lastSon)
|
|
||||||
endings.add c.gotoI()
|
|
||||||
for i in countdown(endings.high, 0):
|
|
||||||
c.patch(endings[i])
|
|
||||||
|
|
||||||
let fin = lastSon(n)
|
|
||||||
if fin.kind == nkFinally:
|
|
||||||
c.gen(fin[0])
|
|
||||||
|
|
||||||
template genNoReturn(c: var Con) =
|
|
||||||
# leave the graph
|
|
||||||
c.code.add Instr(kind: goto, dest: high(int) - c.code.len)
|
|
||||||
|
|
||||||
proc genRaise(c: var Con; n: PNode) =
|
|
||||||
inc c.interestingInstructions
|
|
||||||
gen(c, n[0])
|
|
||||||
if c.inTryStmt > 0:
|
|
||||||
for i in countdown(c.blocks.high, 0):
|
|
||||||
if c.blocks[i].isTryBlock:
|
|
||||||
genBreakOrRaiseAux(c, i, n)
|
|
||||||
return
|
|
||||||
assert false #Unreachable
|
|
||||||
else:
|
|
||||||
genNoReturn(c)
|
|
||||||
|
|
||||||
proc genImplicitReturn(c: var Con) =
|
|
||||||
if c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter} and resultPos < c.owner.ast.len:
|
|
||||||
gen(c, c.owner.ast[resultPos])
|
|
||||||
|
|
||||||
proc genReturn(c: var Con; n: PNode) =
|
|
||||||
inc c.interestingInstructions
|
|
||||||
if n[0].kind != nkEmpty:
|
|
||||||
gen(c, n[0])
|
|
||||||
else:
|
|
||||||
genImplicitReturn(c)
|
|
||||||
genBreakOrRaiseAux(c, 0, n)
|
|
||||||
|
|
||||||
const
|
|
||||||
InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
|
|
||||||
|
|
||||||
proc skipTrivials(c: var Con, n: PNode): PNode =
|
|
||||||
result = n
|
|
||||||
while true:
|
|
||||||
case result.kind
|
|
||||||
of PathKinds0 - {nkBracketExpr}:
|
|
||||||
result = result[0]
|
|
||||||
of nkBracketExpr:
|
|
||||||
gen(c, result[1])
|
|
||||||
result = result[0]
|
|
||||||
of PathKinds1:
|
|
||||||
result = result[1]
|
|
||||||
else: break
|
|
||||||
|
|
||||||
proc genUse(c: var Con; orig: PNode) =
|
|
||||||
let n = c.skipTrivials(orig)
|
|
||||||
|
|
||||||
if n.kind == nkSym:
|
|
||||||
if n.sym.kind in InterestingSyms and n.sym == c.root:
|
|
||||||
c.code.add Instr(kind: use, n: orig)
|
|
||||||
inc c.interestingInstructions
|
|
||||||
else:
|
|
||||||
gen(c, n)
|
|
||||||
|
|
||||||
proc genDef(c: var Con; orig: PNode) =
|
|
||||||
let n = c.skipTrivials(orig)
|
|
||||||
|
|
||||||
if n.kind == nkSym and n.sym.kind in InterestingSyms:
|
|
||||||
if n.sym == c.root:
|
|
||||||
c.code.add Instr(kind: def, n: orig)
|
|
||||||
inc c.interestingInstructions
|
|
||||||
|
|
||||||
proc genCall(c: var Con; n: PNode) =
|
|
||||||
gen(c, n[0])
|
|
||||||
var t = n[0].typ
|
|
||||||
if t != nil: t = t.skipTypes(abstractInst)
|
|
||||||
for i in 1..<n.len:
|
|
||||||
gen(c, n[i])
|
|
||||||
if t != nil and i < t.len and isOutParam(t[i]):
|
|
||||||
# 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]):
|
|
||||||
# we generate the instruction sequence:
|
|
||||||
# fork lab1
|
|
||||||
# goto exceptionHandler (except or finally)
|
|
||||||
# lab1:
|
|
||||||
# join F1
|
|
||||||
forkT:
|
|
||||||
for i in countdown(c.blocks.high, 0):
|
|
||||||
if c.blocks[i].isTryBlock:
|
|
||||||
genBreakOrRaiseAux(c, i, n)
|
|
||||||
break
|
|
||||||
|
|
||||||
proc genMagic(c: var Con; n: PNode; m: TMagic) =
|
|
||||||
case m
|
|
||||||
of mAnd, mOr: c.genAndOr(n)
|
|
||||||
of mNew, mNewFinalize:
|
|
||||||
genDef(c, n[1])
|
|
||||||
for i in 2..<n.len: gen(c, n[i])
|
|
||||||
else:
|
|
||||||
genCall(c, n)
|
|
||||||
|
|
||||||
proc genVarSection(c: var Con; n: PNode) =
|
|
||||||
for a in n:
|
|
||||||
if a.kind == nkCommentStmt:
|
|
||||||
discard
|
|
||||||
elif a.kind == nkVarTuple:
|
|
||||||
gen(c, a.lastSon)
|
|
||||||
for i in 0..<a.len-2: genDef(c, a[i])
|
|
||||||
else:
|
|
||||||
gen(c, a.lastSon)
|
|
||||||
if a.lastSon.kind != nkEmpty:
|
|
||||||
genDef(c, a[0])
|
|
||||||
|
|
||||||
proc gen(c: var Con; n: PNode) =
|
|
||||||
case n.kind
|
|
||||||
of nkSym: genUse(c, n)
|
|
||||||
of nkCallKinds:
|
|
||||||
if n[0].kind == nkSym:
|
|
||||||
let s = n[0].sym
|
|
||||||
if s.magic != mNone:
|
|
||||||
genMagic(c, n, s.magic)
|
|
||||||
else:
|
|
||||||
genCall(c, n)
|
|
||||||
if sfNoReturn in n[0].sym.flags:
|
|
||||||
genNoReturn(c)
|
|
||||||
else:
|
|
||||||
genCall(c, n)
|
|
||||||
of nkCharLit..nkNilLit: discard
|
|
||||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
|
||||||
gen(c, n[1])
|
|
||||||
|
|
||||||
if n[0].kind in PathKinds0:
|
|
||||||
let a = c.skipTrivials(n[0])
|
|
||||||
if a.kind in nkCallKinds:
|
|
||||||
gen(c, a)
|
|
||||||
|
|
||||||
# watch out: 'obj[i].f2 = value' sets 'f2' but
|
|
||||||
# "uses" 'i'. But we are only talking about builtin array indexing so
|
|
||||||
# it doesn't matter and 'x = 34' is NOT a usage of 'x'.
|
|
||||||
genDef(c, n[0])
|
|
||||||
of PathKinds0 - {nkObjDownConv, nkObjUpConv}:
|
|
||||||
genUse(c, n)
|
|
||||||
of nkIfStmt, nkIfExpr: genIf(c, n)
|
|
||||||
of nkWhenStmt:
|
|
||||||
# This is "when nimvm" node. Chose the first branch.
|
|
||||||
gen(c, n[0][1])
|
|
||||||
of nkCaseStmt: genCase(c, n)
|
|
||||||
of nkWhileStmt: genWhile(c, n)
|
|
||||||
of nkBlockExpr, nkBlockStmt: genBlock(c, n)
|
|
||||||
of nkReturnStmt: genReturn(c, n)
|
|
||||||
of nkRaiseStmt: genRaise(c, n)
|
|
||||||
of nkBreakStmt: genBreak(c, n)
|
|
||||||
of nkTryStmt, nkHiddenTryStmt: genTry(c, n)
|
|
||||||
of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
|
|
||||||
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkYieldStmt:
|
|
||||||
for x in n: gen(c, x)
|
|
||||||
of nkPragmaBlock: gen(c, n.lastSon)
|
|
||||||
of nkDiscardStmt, nkObjDownConv, nkObjUpConv, nkStringToCString, nkCStringToString:
|
|
||||||
gen(c, n[0])
|
|
||||||
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'"
|
|
||||||
else: discard
|
|
||||||
|
|
||||||
when false:
|
|
||||||
proc optimizeJumps(c: var ControlFlowGraph) =
|
|
||||||
for i in 0..<c.len:
|
|
||||||
case c[i].kind
|
|
||||||
of goto, fork:
|
|
||||||
var pc = i + c[i].dest
|
|
||||||
if pc < c.len and c[pc].kind == goto:
|
|
||||||
while pc < c.len and c[pc].kind == goto:
|
|
||||||
let newPc = pc + c[pc].dest
|
|
||||||
if newPc > pc:
|
|
||||||
pc = newPc
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
c[i].dest = pc - i
|
|
||||||
of loop, def, use: discard
|
|
||||||
|
|
||||||
proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph =
|
|
||||||
## constructs a control flow graph for ``body``.
|
|
||||||
var c = Con(code: @[], blocks: @[], owner: s, root: root)
|
|
||||||
withBlock(s):
|
|
||||||
gen(c, body)
|
|
||||||
if root.kind == skResult:
|
|
||||||
genImplicitReturn(c)
|
|
||||||
when defined(gcArc) or defined(gcOrc):
|
|
||||||
result = c.code # will move
|
|
||||||
else:
|
|
||||||
shallowCopy(result, c.code)
|
|
||||||
when false:
|
|
||||||
optimizeJumps result
|
|
||||||
2118
compiler/docgen.nim
2118
compiler/docgen.nim
File diff suppressed because it is too large
Load diff
|
|
@ -10,69 +10,40 @@
|
||||||
# This module implements a new documentation generator that runs after
|
# This module implements a new documentation generator that runs after
|
||||||
# semantic checking.
|
# semantic checking.
|
||||||
|
|
||||||
import
|
import
|
||||||
options, ast, msgs, docgen, lineinfos, pathutils, packages
|
os, options, ast, astalgo, msgs, ropes, idents, passes, docgen
|
||||||
|
|
||||||
from modulegraphs import ModuleGraph, PPassContext
|
type
|
||||||
|
TGen = object of TPassContext
|
||||||
type
|
|
||||||
TGen = object of PPassContext
|
|
||||||
doc: PDoc
|
doc: PDoc
|
||||||
module: PSym
|
module: PSym
|
||||||
config: ConfigRef
|
|
||||||
PGen = ref TGen
|
PGen = ref TGen
|
||||||
|
|
||||||
proc shouldProcess(g: PGen): bool =
|
proc close(p: PPassContext, n: PNode): PNode =
|
||||||
(optWholeProject in g.doc.conf.globalOptions and g.doc.conf.belongsToProjectPackage(g.module)) or
|
|
||||||
sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex
|
|
||||||
|
|
||||||
template closeImpl(body: untyped) {.dirty.} =
|
|
||||||
var g = PGen(p)
|
var g = PGen(p)
|
||||||
let useWarning = sfMainModule notin g.module.flags
|
let useWarning = sfMainModule notin g.module.flags
|
||||||
let groupedToc = true
|
if gWholeProject or sfMainModule in g.module.flags:
|
||||||
if shouldProcess(g):
|
writeOutput(g.doc, g.module.filename, HtmlExt, useWarning)
|
||||||
finishGenerateDoc(g.doc)
|
|
||||||
body
|
|
||||||
try:
|
try:
|
||||||
generateIndex(g.doc)
|
generateIndex(g.doc)
|
||||||
except IOError:
|
except IOError:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
proc processNode(c: PPassContext, n: PNode): PNode =
|
||||||
closeImpl:
|
|
||||||
writeOutput(g.doc, useWarning, groupedToc)
|
|
||||||
|
|
||||||
proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
|
||||||
closeImpl:
|
|
||||||
writeOutputJson(g.doc, useWarning)
|
|
||||||
|
|
||||||
proc processNode*(c: PPassContext, n: PNode): PNode =
|
|
||||||
result = n
|
result = n
|
||||||
var g = PGen(c)
|
var g = PGen(c)
|
||||||
if shouldProcess(g):
|
generateDoc(g.doc, n)
|
||||||
generateDoc(g.doc, n, n, g.config)
|
|
||||||
|
|
||||||
proc processNodeJson*(c: PPassContext, n: PNode): PNode =
|
proc myOpen(module: PSym): PPassContext =
|
||||||
result = n
|
|
||||||
var g = PGen(c)
|
|
||||||
if shouldProcess(g):
|
|
||||||
generateJson(g.doc, n, false)
|
|
||||||
|
|
||||||
template myOpenImpl(ext: untyped) {.dirty.} =
|
|
||||||
var g: PGen
|
var g: PGen
|
||||||
new(g)
|
new(g)
|
||||||
g.module = module
|
g.module = module
|
||||||
g.config = graph.config
|
var d = newDocumentor(module.filename, options.gConfigVars)
|
||||||
var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
|
d.hasToc = true
|
||||||
graph.cache, graph.config, ext, module, hasToc = true)
|
|
||||||
g.doc = d
|
g.doc = d
|
||||||
result = g
|
result = g
|
||||||
|
|
||||||
proc openHtml*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
|
||||||
myOpenImpl(HtmlExt)
|
|
||||||
|
|
||||||
proc openTex*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
proc finishDoc2Pass*(project: string) =
|
||||||
myOpenImpl(TexExt)
|
discard
|
||||||
|
|
||||||
proc openJson*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
|
||||||
myOpenImpl(JsonExt)
|
|
||||||
|
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
|
|
||||||
import ast, idents, lineinfos, modulegraphs, magicsys
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
|
|
||||||
proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
|
||||||
result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info)
|
|
||||||
|
|
||||||
let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
|
|
||||||
dest.typ = t
|
|
||||||
|
|
||||||
let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
|
|
||||||
res.typ = getSysType(g, info, tyString)
|
|
||||||
|
|
||||||
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
|
|
||||||
result.typ.n = newNodeI(nkFormalParams, info)
|
|
||||||
rawAddSon(result.typ, res.typ)
|
|
||||||
result.typ.n.add newNodeI(nkEffectList, info)
|
|
||||||
|
|
||||||
result.typ.addParam dest
|
|
||||||
|
|
||||||
var body = newNodeI(nkStmtList, info)
|
|
||||||
var caseStmt = newNodeI(nkCaseStmt, info)
|
|
||||||
caseStmt.add(newSymNode dest)
|
|
||||||
|
|
||||||
# copy the branches over, but replace the fields with the for loop body:
|
|
||||||
for i in 0..<t.n.len:
|
|
||||||
assert(t.n[i].kind == nkSym)
|
|
||||||
var field = t.n[i].sym
|
|
||||||
let val = if field.ast == nil: field.name.s else: field.ast.strVal
|
|
||||||
caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t),
|
|
||||||
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
|
|
||||||
#newIntTypeNode(nkIntLit, field.position, t)
|
|
||||||
|
|
||||||
body.add(caseStmt)
|
|
||||||
|
|
||||||
var n = newNodeI(nkProcDef, info, bodyPos+2)
|
|
||||||
for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
|
|
||||||
n[namePos] = newSymNode(result)
|
|
||||||
n[paramsPos] = result.typ.n
|
|
||||||
n[bodyPos] = body
|
|
||||||
n[resultPos] = newSymNode(res)
|
|
||||||
result.ast = n
|
|
||||||
incl result.flags, sfFromGeneric
|
|
||||||
incl result.flags, sfNeverRaises
|
|
||||||
|
|
||||||
proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
|
|
||||||
case obj.kind
|
|
||||||
of nkSym:
|
|
||||||
result = nil
|
|
||||||
of nkElse, nkOfBranch:
|
|
||||||
result = searchObjCaseImpl(obj.lastSon, field)
|
|
||||||
else:
|
|
||||||
if obj.kind == nkRecCase and obj[0].kind == nkSym and obj[0].sym == field:
|
|
||||||
result = obj
|
|
||||||
else:
|
|
||||||
for x in obj:
|
|
||||||
result = searchObjCaseImpl(x, field)
|
|
||||||
if result != nil: break
|
|
||||||
|
|
||||||
proc searchObjCase(t: PType; field: PSym): PNode =
|
|
||||||
result = searchObjCaseImpl(t.n, field)
|
|
||||||
if result == nil and t.len > 0:
|
|
||||||
result = searchObjCase(t[0].skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
|
|
||||||
doAssert result != nil
|
|
||||||
|
|
||||||
proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
|
||||||
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextSymId idgen, t.owner, info)
|
|
||||||
|
|
||||||
let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
|
|
||||||
dest.typ = field.typ
|
|
||||||
|
|
||||||
let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
|
|
||||||
res.typ = getSysType(g, info, tyUInt8)
|
|
||||||
|
|
||||||
result.typ = newType(tyProc, nextTypeId idgen, t.owner)
|
|
||||||
result.typ.n = newNodeI(nkFormalParams, info)
|
|
||||||
rawAddSon(result.typ, res.typ)
|
|
||||||
result.typ.n.add newNodeI(nkEffectList, info)
|
|
||||||
|
|
||||||
result.typ.addParam dest
|
|
||||||
|
|
||||||
var body = newNodeI(nkStmtList, info)
|
|
||||||
var caseStmt = newNodeI(nkCaseStmt, info)
|
|
||||||
caseStmt.add(newSymNode dest)
|
|
||||||
|
|
||||||
let subObj = searchObjCase(t, field)
|
|
||||||
for i in 1..<subObj.len:
|
|
||||||
let ofBranch = subObj[i]
|
|
||||||
var newBranch = newNodeI(ofBranch.kind, ofBranch.info)
|
|
||||||
for j in 0..<ofBranch.len-1:
|
|
||||||
newBranch.add ofBranch[j]
|
|
||||||
|
|
||||||
newBranch.add newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newIntNode(nkInt8Lit, i)))
|
|
||||||
caseStmt.add newBranch
|
|
||||||
|
|
||||||
body.add(caseStmt)
|
|
||||||
|
|
||||||
var n = newNodeI(nkProcDef, info, bodyPos+2)
|
|
||||||
for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
|
|
||||||
n[namePos] = newSymNode(result)
|
|
||||||
n[paramsPos] = result.typ.n
|
|
||||||
n[bodyPos] = body
|
|
||||||
n[resultPos] = newSymNode(res)
|
|
||||||
result.ast = n
|
|
||||||
incl result.flags, sfFromGeneric
|
|
||||||
incl result.flags, sfNeverRaises
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2021 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## This module contains support code for new-styled error
|
|
||||||
## handling via an `nkError` node kind.
|
|
||||||
|
|
||||||
import ast, renderer, options, strutils, types
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
|
||||||
ErrorKind* = enum ## expand as you need.
|
|
||||||
RawTypeMismatchError
|
|
||||||
ExpressionCannotBeCalled
|
|
||||||
CustomError
|
|
||||||
WrongNumberOfArguments
|
|
||||||
AmbiguousCall
|
|
||||||
|
|
||||||
proc errorSubNode*(n: PNode): PNode =
|
|
||||||
case n.kind
|
|
||||||
of nkEmpty..nkNilLit:
|
|
||||||
result = nil
|
|
||||||
of nkError:
|
|
||||||
result = n
|
|
||||||
else:
|
|
||||||
result = nil
|
|
||||||
for i in 0..<n.len:
|
|
||||||
result = errorSubNode(n[i])
|
|
||||||
if result != nil: break
|
|
||||||
|
|
||||||
proc newError*(wrongNode: PNode; k: ErrorKind; args: varargs[PNode]): PNode =
|
|
||||||
assert wrongNode.kind != nkError
|
|
||||||
let innerError = errorSubNode(wrongNode)
|
|
||||||
if innerError != nil:
|
|
||||||
return innerError
|
|
||||||
result = newNodeIT(nkError, wrongNode.info, newType(tyError, ItemId(module: -1, item: -1), nil))
|
|
||||||
result.add wrongNode
|
|
||||||
result.add newIntNode(nkIntLit, ord(k))
|
|
||||||
for a in args: result.add a
|
|
||||||
|
|
||||||
proc newError*(wrongNode: PNode; msg: string): PNode =
|
|
||||||
assert wrongNode.kind != nkError
|
|
||||||
let innerError = errorSubNode(wrongNode)
|
|
||||||
if innerError != nil:
|
|
||||||
return innerError
|
|
||||||
result = newNodeIT(nkError, wrongNode.info, newType(tyError, ItemId(module: -1, item: -1), nil))
|
|
||||||
result.add wrongNode
|
|
||||||
result.add newIntNode(nkIntLit, ord(CustomError))
|
|
||||||
result.add newStrNode(msg, wrongNode.info)
|
|
||||||
|
|
||||||
proc errorToString*(config: ConfigRef; n: PNode): string =
|
|
||||||
assert n.kind == nkError
|
|
||||||
assert n.len > 1
|
|
||||||
let wrongNode = n[0]
|
|
||||||
case ErrorKind(n[1].intVal)
|
|
||||||
of RawTypeMismatchError:
|
|
||||||
result = "type mismatch"
|
|
||||||
of ExpressionCannotBeCalled:
|
|
||||||
result = "expression '$1' cannot be called" % wrongNode[0].renderTree
|
|
||||||
of CustomError:
|
|
||||||
result = n[2].strVal
|
|
||||||
of WrongNumberOfArguments:
|
|
||||||
result = "wrong number of arguments"
|
|
||||||
of AmbiguousCall:
|
|
||||||
let a = n[2].sym
|
|
||||||
let b = n[3].sym
|
|
||||||
var args = "("
|
|
||||||
for i in 1..<wrongNode.len:
|
|
||||||
if i > 1: args.add(", ")
|
|
||||||
args.add(typeToString(wrongNode[i].typ))
|
|
||||||
args.add(")")
|
|
||||||
result = "ambiguous call; both $1 and $2 match for: $3" % [
|
|
||||||
getProcHeader(config, a),
|
|
||||||
getProcHeader(config, b),
|
|
||||||
args]
|
|
||||||
|
|
@ -1,502 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2015 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## This file implements the FFI part of the evaluator for Nim code.
|
|
||||||
|
|
||||||
import ast, types, options, tables, dynlib, msgs, lineinfos
|
|
||||||
from os import getAppFilename
|
|
||||||
import pkg/libffi
|
|
||||||
|
|
||||||
when defined(windows):
|
|
||||||
const libcDll = "msvcrt.dll"
|
|
||||||
elif defined(linux):
|
|
||||||
const libcDll = "libc.so(.6|.5|)"
|
|
||||||
elif defined(openbsd):
|
|
||||||
const libcDll = "/usr/lib/libc.so(.95.1|)"
|
|
||||||
elif defined(bsd):
|
|
||||||
const libcDll = "/lib/libc.so.7"
|
|
||||||
elif defined(osx):
|
|
||||||
const libcDll = "/usr/lib/libSystem.dylib"
|
|
||||||
else:
|
|
||||||
{.error: "`libcDll` not implemented on this platform".}
|
|
||||||
|
|
||||||
type
|
|
||||||
TDllCache = tables.Table[string, LibHandle]
|
|
||||||
var
|
|
||||||
gDllCache = initTable[string, LibHandle]()
|
|
||||||
|
|
||||||
when defined(windows):
|
|
||||||
var gExeHandle = loadLib(getAppFilename())
|
|
||||||
else:
|
|
||||||
var gExeHandle = loadLib()
|
|
||||||
|
|
||||||
proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo): pointer =
|
|
||||||
if dll in cache:
|
|
||||||
return cache[dll]
|
|
||||||
var libs: seq[string]
|
|
||||||
libCandidates(dll, libs)
|
|
||||||
for c in libs:
|
|
||||||
result = loadLib(c)
|
|
||||||
if not result.isNil: break
|
|
||||||
if result.isNil:
|
|
||||||
globalError(conf, info, "cannot load: " & dll)
|
|
||||||
cache[dll] = result
|
|
||||||
|
|
||||||
const
|
|
||||||
nkPtrLit = nkIntLit # hopefully we can get rid of this hack soon
|
|
||||||
|
|
||||||
proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
|
|
||||||
let name = sym.cname # $sym.loc.r would point to internal name
|
|
||||||
# the AST does not support untyped pointers directly, so we use an nkIntLit
|
|
||||||
# that contains the address instead:
|
|
||||||
result = newNodeIT(nkPtrLit, sym.info, sym.typ)
|
|
||||||
when true:
|
|
||||||
var libPathMsg = ""
|
|
||||||
let lib = sym.annex
|
|
||||||
if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
|
|
||||||
globalError(conf, sym.info, "dynlib needs to be a string lit")
|
|
||||||
var theAddr: pointer
|
|
||||||
if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil:
|
|
||||||
libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll
|
|
||||||
# first try this exe itself:
|
|
||||||
theAddr = gExeHandle.symAddr(name.cstring)
|
|
||||||
# then try libc:
|
|
||||||
if theAddr.isNil:
|
|
||||||
let dllhandle = getDll(conf, gDllCache, libcDll, sym.info)
|
|
||||||
theAddr = dllhandle.symAddr(name.cstring)
|
|
||||||
elif not lib.isNil:
|
|
||||||
let dll = if lib.kind == libHeader: libcDll else: lib.path.strVal
|
|
||||||
libPathMsg = dll
|
|
||||||
let dllhandle = getDll(conf, gDllCache, dll, sym.info)
|
|
||||||
theAddr = dllhandle.symAddr(name.cstring)
|
|
||||||
if theAddr.isNil: globalError(conf, sym.info,
|
|
||||||
"cannot import symbol: " & name & " from " & libPathMsg)
|
|
||||||
result.intVal = cast[int](theAddr)
|
|
||||||
|
|
||||||
proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
|
|
||||||
if t == nil: return addr libffi.type_void
|
|
||||||
|
|
||||||
case t.kind
|
|
||||||
of tyBool, tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tySet:
|
|
||||||
case getSize(conf, t)
|
|
||||||
of 1: result = addr libffi.type_uint8
|
|
||||||
of 2: result = addr libffi.type_sint16
|
|
||||||
of 4: result = addr libffi.type_sint32
|
|
||||||
of 8: result = addr libffi.type_sint64
|
|
||||||
else: result = nil
|
|
||||||
of tyFloat, tyFloat64: result = addr libffi.type_double
|
|
||||||
of tyFloat32: result = addr libffi.type_float
|
|
||||||
of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCstring, tySequence, tyString, tyUntyped,
|
|
||||||
tyTyped, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
|
|
||||||
result = addr libffi.type_pointer
|
|
||||||
of tyDistinct, tyAlias, tySink:
|
|
||||||
result = mapType(conf, t[0])
|
|
||||||
else:
|
|
||||||
result = nil
|
|
||||||
# too risky:
|
|
||||||
#of tyFloat128: result = addr libffi.type_longdouble
|
|
||||||
|
|
||||||
proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI =
|
|
||||||
case cc
|
|
||||||
of ccNimCall: result = DEFAULT_ABI
|
|
||||||
of ccStdCall: result = when defined(windows) and defined(x86): STDCALL else: DEFAULT_ABI
|
|
||||||
of ccCDecl: result = DEFAULT_ABI
|
|
||||||
else:
|
|
||||||
globalError(conf, info, "cannot map calling convention to FFI")
|
|
||||||
|
|
||||||
template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[]
|
|
||||||
template wr(typ, p, v: untyped): untyped = (cast[ptr typ](p))[] = v
|
|
||||||
template `+!`(x, y: untyped): untyped =
|
|
||||||
cast[pointer](cast[int](x) + y)
|
|
||||||
|
|
||||||
proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
|
|
||||||
## computes the size of the blob
|
|
||||||
case typ.kind
|
|
||||||
of tyPtr, tyRef, tyVar, tyLent:
|
|
||||||
if v.kind in {nkNilLit, nkPtrLit}:
|
|
||||||
result = sizeof(pointer)
|
|
||||||
else:
|
|
||||||
result = sizeof(pointer) + packSize(conf, v[0], typ.lastSon)
|
|
||||||
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
|
||||||
result = packSize(conf, v, typ[0])
|
|
||||||
of tyArray:
|
|
||||||
# consider: ptr array[0..1000_000, int] which is common for interfacing;
|
|
||||||
# we use the real length here instead
|
|
||||||
if v.kind in {nkNilLit, nkPtrLit}:
|
|
||||||
result = sizeof(pointer)
|
|
||||||
elif v.len != 0:
|
|
||||||
result = v.len * packSize(conf, v[0], typ[1])
|
|
||||||
else:
|
|
||||||
result = getSize(conf, typ).int
|
|
||||||
|
|
||||||
proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer)
|
|
||||||
|
|
||||||
proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
|
|
||||||
case n.kind
|
|
||||||
of nkRecList:
|
|
||||||
for i in 0..<n.len:
|
|
||||||
result = getField(conf, n[i], position)
|
|
||||||
if result != nil: return
|
|
||||||
of nkRecCase:
|
|
||||||
result = getField(conf, n[0], position)
|
|
||||||
if result != nil: return
|
|
||||||
for i in 1..<n.len:
|
|
||||||
case n[i].kind
|
|
||||||
of nkOfBranch, nkElse:
|
|
||||||
result = getField(conf, lastSon(n[i]), position)
|
|
||||||
if result != nil: return
|
|
||||||
else: internalError(conf, n.info, "getField(record case branch)")
|
|
||||||
of nkSym:
|
|
||||||
if n.sym.position == position: result = n.sym
|
|
||||||
else: discard
|
|
||||||
|
|
||||||
proc packObject(conf: ConfigRef, x: PNode, typ: PType, res: pointer) =
|
|
||||||
internalAssert conf, x.kind in {nkObjConstr, nkPar, nkTupleConstr}
|
|
||||||
# compute the field's offsets:
|
|
||||||
discard getSize(conf, typ)
|
|
||||||
for i in ord(x.kind == nkObjConstr)..<x.len:
|
|
||||||
var it = x[i]
|
|
||||||
if it.kind == nkExprColonExpr:
|
|
||||||
internalAssert conf, it[0].kind == nkSym
|
|
||||||
let field = it[0].sym
|
|
||||||
pack(conf, it[1], field.typ, res +! field.offset)
|
|
||||||
elif typ.n != nil:
|
|
||||||
let field = getField(conf, typ.n, i)
|
|
||||||
pack(conf, it, field.typ, res +! field.offset)
|
|
||||||
else:
|
|
||||||
# XXX: todo
|
|
||||||
globalError(conf, x.info, "cannot pack unnamed tuple")
|
|
||||||
|
|
||||||
const maxPackDepth = 20
|
|
||||||
var packRecCheck = 0
|
|
||||||
|
|
||||||
proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
|
|
||||||
template awr(typ, v: untyped): untyped =
|
|
||||||
wr(typ, res, v)
|
|
||||||
|
|
||||||
case typ.kind
|
|
||||||
of tyBool: awr(bool, v.intVal != 0)
|
|
||||||
of tyChar: awr(char, v.intVal.chr)
|
|
||||||
of tyInt: awr(int, v.intVal.int)
|
|
||||||
of tyInt8: awr(int8, v.intVal.int8)
|
|
||||||
of tyInt16: awr(int16, v.intVal.int16)
|
|
||||||
of tyInt32: awr(int32, v.intVal.int32)
|
|
||||||
of tyInt64: awr(int64, v.intVal.int64)
|
|
||||||
of tyUInt: awr(uint, v.intVal.uint)
|
|
||||||
of tyUInt8: awr(uint8, v.intVal.uint8)
|
|
||||||
of tyUInt16: awr(uint16, v.intVal.uint16)
|
|
||||||
of tyUInt32: awr(uint32, v.intVal.uint32)
|
|
||||||
of tyUInt64: awr(uint64, v.intVal.uint64)
|
|
||||||
of tyEnum, tySet:
|
|
||||||
case getSize(conf, v.typ)
|
|
||||||
of 1: awr(uint8, v.intVal.uint8)
|
|
||||||
of 2: awr(uint16, v.intVal.uint16)
|
|
||||||
of 4: awr(int32, v.intVal.int32)
|
|
||||||
of 8: awr(int64, v.intVal.int64)
|
|
||||||
else:
|
|
||||||
globalError(conf, v.info, "cannot map value to FFI (tyEnum, tySet)")
|
|
||||||
of tyFloat: awr(float, v.floatVal)
|
|
||||||
of tyFloat32: awr(float32, v.floatVal)
|
|
||||||
of tyFloat64: awr(float64, v.floatVal)
|
|
||||||
|
|
||||||
of tyPointer, tyProc, tyCstring, tyString:
|
|
||||||
if v.kind == nkNilLit:
|
|
||||||
# nothing to do since the memory is 0 initialized anyway
|
|
||||||
discard
|
|
||||||
elif v.kind == nkPtrLit:
|
|
||||||
awr(pointer, cast[pointer](v.intVal))
|
|
||||||
elif v.kind in {nkStrLit..nkTripleStrLit}:
|
|
||||||
awr(cstring, cstring(v.strVal))
|
|
||||||
else:
|
|
||||||
globalError(conf, v.info, "cannot map pointer/proc value to FFI")
|
|
||||||
of tyPtr, tyRef, tyVar, tyLent:
|
|
||||||
if v.kind == nkNilLit:
|
|
||||||
# nothing to do since the memory is 0 initialized anyway
|
|
||||||
discard
|
|
||||||
elif v.kind == nkPtrLit:
|
|
||||||
awr(pointer, cast[pointer](v.intVal))
|
|
||||||
else:
|
|
||||||
if packRecCheck > maxPackDepth:
|
|
||||||
packRecCheck = 0
|
|
||||||
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
|
|
||||||
inc packRecCheck
|
|
||||||
pack(conf, v[0], typ.lastSon, res +! sizeof(pointer))
|
|
||||||
dec packRecCheck
|
|
||||||
awr(pointer, res +! sizeof(pointer))
|
|
||||||
of tyArray:
|
|
||||||
let baseSize = getSize(conf, typ[1])
|
|
||||||
for i in 0..<v.len:
|
|
||||||
pack(conf, v[i], typ[1], res +! i * baseSize)
|
|
||||||
of tyObject, tyTuple:
|
|
||||||
packObject(conf, v, typ, res)
|
|
||||||
of tyNil:
|
|
||||||
discard
|
|
||||||
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
|
||||||
pack(conf, v, typ[0], res)
|
|
||||||
else:
|
|
||||||
globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
|
|
||||||
|
|
||||||
proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode
|
|
||||||
|
|
||||||
proc unpackObjectAdd(conf: ConfigRef, x: pointer, n, result: PNode) =
|
|
||||||
case n.kind
|
|
||||||
of nkRecList:
|
|
||||||
for i in 0..<n.len:
|
|
||||||
unpackObjectAdd(conf, x, n[i], result)
|
|
||||||
of nkRecCase:
|
|
||||||
globalError(conf, result.info, "case objects cannot be unpacked")
|
|
||||||
of nkSym:
|
|
||||||
var pair = newNodeI(nkExprColonExpr, result.info, 2)
|
|
||||||
pair[0] = n
|
|
||||||
pair[1] = unpack(conf, x +! n.sym.offset, n.sym.typ, nil)
|
|
||||||
#echo "offset: ", n.sym.name.s, " ", n.sym.offset
|
|
||||||
result.add pair
|
|
||||||
else: discard
|
|
||||||
|
|
||||||
proc unpackObject(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
|
||||||
# compute the field's offsets:
|
|
||||||
discard getSize(conf, typ)
|
|
||||||
|
|
||||||
# iterate over any actual field of 'n' ... if n is nil we need to create
|
|
||||||
# the nkPar node:
|
|
||||||
if n.isNil:
|
|
||||||
result = newNode(nkTupleConstr)
|
|
||||||
result.typ = typ
|
|
||||||
if typ.n.isNil:
|
|
||||||
internalError(conf, "cannot unpack unnamed tuple")
|
|
||||||
unpackObjectAdd(conf, x, typ.n, result)
|
|
||||||
else:
|
|
||||||
result = n
|
|
||||||
if result.kind notin {nkObjConstr, nkPar, nkTupleConstr}:
|
|
||||||
globalError(conf, n.info, "cannot map value from FFI")
|
|
||||||
if typ.n.isNil:
|
|
||||||
globalError(conf, n.info, "cannot unpack unnamed tuple")
|
|
||||||
for i in ord(n.kind == nkObjConstr)..<n.len:
|
|
||||||
var it = n[i]
|
|
||||||
if it.kind == nkExprColonExpr:
|
|
||||||
internalAssert conf, it[0].kind == nkSym
|
|
||||||
let field = it[0].sym
|
|
||||||
it[1] = unpack(conf, x +! field.offset, field.typ, it[1])
|
|
||||||
else:
|
|
||||||
let field = getField(conf, typ.n, i)
|
|
||||||
n[i] = unpack(conf, x +! field.offset, field.typ, it)
|
|
||||||
|
|
||||||
proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
|
||||||
if n.isNil:
|
|
||||||
result = newNode(nkBracket)
|
|
||||||
result.typ = typ
|
|
||||||
newSeq(result.sons, lengthOrd(conf, typ).toInt)
|
|
||||||
else:
|
|
||||||
result = n
|
|
||||||
if result.kind != nkBracket:
|
|
||||||
globalError(conf, n.info, "cannot map value from FFI")
|
|
||||||
let baseSize = getSize(conf, typ[1])
|
|
||||||
for i in 0..<result.len:
|
|
||||||
result[i] = unpack(conf, x +! i * baseSize, typ[1], result[i])
|
|
||||||
|
|
||||||
proc canonNodeKind(k: TNodeKind): TNodeKind =
|
|
||||||
case k
|
|
||||||
of nkCharLit..nkUInt64Lit: result = nkIntLit
|
|
||||||
of nkFloatLit..nkFloat128Lit: result = nkFloatLit
|
|
||||||
of nkStrLit..nkTripleStrLit: result = nkStrLit
|
|
||||||
else: result = k
|
|
||||||
|
|
||||||
proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
|
||||||
template aw(k, v, field: untyped): untyped =
|
|
||||||
if n.isNil:
|
|
||||||
result = newNode(k)
|
|
||||||
result.typ = typ
|
|
||||||
else:
|
|
||||||
# check we have the right field:
|
|
||||||
result = n
|
|
||||||
if result.kind.canonNodeKind != k.canonNodeKind:
|
|
||||||
#echo "expected ", k, " but got ", result.kind
|
|
||||||
#debug result
|
|
||||||
return newNodeI(nkExceptBranch, n.info)
|
|
||||||
#globalError(conf, n.info, "cannot map value from FFI")
|
|
||||||
result.field = v
|
|
||||||
|
|
||||||
template setNil() =
|
|
||||||
if n.isNil:
|
|
||||||
result = newNode(nkNilLit)
|
|
||||||
result.typ = typ
|
|
||||||
else:
|
|
||||||
reset n[]
|
|
||||||
result = n
|
|
||||||
result[] = TNode(kind: nkNilLit)
|
|
||||||
result.typ = typ
|
|
||||||
|
|
||||||
template awi(kind, v: untyped): untyped = aw(kind, v, intVal)
|
|
||||||
template awf(kind, v: untyped): untyped = aw(kind, v, floatVal)
|
|
||||||
template aws(kind, v: untyped): untyped = aw(kind, v, strVal)
|
|
||||||
|
|
||||||
case typ.kind
|
|
||||||
of tyBool: awi(nkIntLit, rd(bool, x).ord)
|
|
||||||
of tyChar: awi(nkCharLit, rd(char, x).ord)
|
|
||||||
of tyInt: awi(nkIntLit, rd(int, x))
|
|
||||||
of tyInt8: awi(nkInt8Lit, rd(int8, x))
|
|
||||||
of tyInt16: awi(nkInt16Lit, rd(int16, x))
|
|
||||||
of tyInt32: awi(nkInt32Lit, rd(int32, x))
|
|
||||||
of tyInt64: awi(nkInt64Lit, rd(int64, x))
|
|
||||||
of tyUInt: awi(nkUIntLit, rd(uint, x).BiggestInt)
|
|
||||||
of tyUInt8: awi(nkUInt8Lit, rd(uint8, x).BiggestInt)
|
|
||||||
of tyUInt16: awi(nkUInt16Lit, rd(uint16, x).BiggestInt)
|
|
||||||
of tyUInt32: awi(nkUInt32Lit, rd(uint32, x).BiggestInt)
|
|
||||||
of tyUInt64: awi(nkUInt64Lit, rd(uint64, x).BiggestInt)
|
|
||||||
of tyEnum:
|
|
||||||
case getSize(conf, typ)
|
|
||||||
of 1: awi(nkIntLit, rd(uint8, x).BiggestInt)
|
|
||||||
of 2: awi(nkIntLit, rd(uint16, x).BiggestInt)
|
|
||||||
of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
|
|
||||||
of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
|
|
||||||
else:
|
|
||||||
globalError(conf, n.info, "cannot map value from FFI (tyEnum, tySet)")
|
|
||||||
of tyFloat: awf(nkFloatLit, rd(float, x))
|
|
||||||
of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
|
|
||||||
of tyFloat64: awf(nkFloat64Lit, rd(float64, x))
|
|
||||||
of tyPointer, tyProc:
|
|
||||||
let p = rd(pointer, x)
|
|
||||||
if p.isNil:
|
|
||||||
setNil()
|
|
||||||
elif n != nil and n.kind == nkStrLit:
|
|
||||||
# we passed a string literal as a pointer; however strings are already
|
|
||||||
# in their unboxed representation so nothing it to be unpacked:
|
|
||||||
result = n
|
|
||||||
else:
|
|
||||||
awi(nkPtrLit, cast[int](p))
|
|
||||||
of tyPtr, tyRef, tyVar, tyLent:
|
|
||||||
let p = rd(pointer, x)
|
|
||||||
if p.isNil:
|
|
||||||
setNil()
|
|
||||||
elif n == nil or n.kind == nkPtrLit:
|
|
||||||
awi(nkPtrLit, cast[int](p))
|
|
||||||
elif n != nil and n.len == 1:
|
|
||||||
internalAssert(conf, n.kind == nkRefTy)
|
|
||||||
n[0] = unpack(conf, p, typ.lastSon, n[0])
|
|
||||||
result = n
|
|
||||||
else:
|
|
||||||
globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
|
|
||||||
of tyObject, tyTuple:
|
|
||||||
result = unpackObject(conf, x, typ, n)
|
|
||||||
of tyArray:
|
|
||||||
result = unpackArray(conf, x, typ, n)
|
|
||||||
of tyCstring, tyString:
|
|
||||||
let p = rd(cstring, x)
|
|
||||||
if p.isNil:
|
|
||||||
setNil()
|
|
||||||
else:
|
|
||||||
aws(nkStrLit, $p)
|
|
||||||
of tyNil:
|
|
||||||
setNil()
|
|
||||||
of tyDistinct, tyGenericInst, tyAlias, tySink:
|
|
||||||
result = unpack(conf, x, typ.lastSon, n)
|
|
||||||
else:
|
|
||||||
# XXX what to do with 'array' here?
|
|
||||||
globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
|
|
||||||
|
|
||||||
proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
|
||||||
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyLent, tyPointer,
|
|
||||||
tyProc, tyCstring, tyString,
|
|
||||||
tySequence}:
|
|
||||||
result = newNodeIT(x.kind, x.info, destTyp)
|
|
||||||
result.intVal = x.intVal
|
|
||||||
elif x.kind == nkNilLit:
|
|
||||||
result = newNodeIT(x.kind, x.info, destTyp)
|
|
||||||
else:
|
|
||||||
# we play safe here and allocate the max possible size:
|
|
||||||
let size = max(packSize(conf, x, x.typ), packSize(conf, x, destTyp))
|
|
||||||
var a = alloc0(size)
|
|
||||||
pack(conf, x, x.typ, a)
|
|
||||||
# cast through a pointer needs a new inner object:
|
|
||||||
let y = if x.kind == nkRefTy: newNodeI(nkRefTy, x.info, 1)
|
|
||||||
else: x.copyTree
|
|
||||||
y.typ = x.typ
|
|
||||||
result = unpack(conf, a, destTyp, y)
|
|
||||||
dealloc a
|
|
||||||
|
|
||||||
proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
|
|
||||||
internalAssert conf, call[0].kind == nkPtrLit
|
|
||||||
|
|
||||||
var cif: TCif
|
|
||||||
var sig: ParamList
|
|
||||||
# use the arguments' types for varargs support:
|
|
||||||
for i in 1..<call.len:
|
|
||||||
sig[i-1] = mapType(conf, call[i].typ)
|
|
||||||
if sig[i-1].isNil:
|
|
||||||
globalError(conf, call.info, "cannot map FFI type")
|
|
||||||
|
|
||||||
let typ = call[0].typ
|
|
||||||
if prep_cif(cif, mapCallConv(conf, typ.callConv, call.info), cuint(call.len-1),
|
|
||||||
mapType(conf, typ[0]), sig) != OK:
|
|
||||||
globalError(conf, call.info, "error in FFI call")
|
|
||||||
|
|
||||||
var args: ArgList
|
|
||||||
let fn = cast[pointer](call[0].intVal)
|
|
||||||
for i in 1..<call.len:
|
|
||||||
var t = call[i].typ
|
|
||||||
args[i-1] = alloc0(packSize(conf, call[i], t))
|
|
||||||
pack(conf, call[i], t, args[i-1])
|
|
||||||
let retVal = if isEmptyType(typ[0]): pointer(nil)
|
|
||||||
else: alloc(getSize(conf, typ[0]).int)
|
|
||||||
|
|
||||||
libffi.call(cif, fn, retVal, args)
|
|
||||||
|
|
||||||
if retVal.isNil:
|
|
||||||
result = newNode(nkEmpty)
|
|
||||||
else:
|
|
||||||
result = unpack(conf, retVal, typ[0], nil)
|
|
||||||
result.info = call.info
|
|
||||||
|
|
||||||
if retVal != nil: dealloc retVal
|
|
||||||
for i in 1..<call.len:
|
|
||||||
call[i] = unpack(conf, args[i-1], typ[i], call[i])
|
|
||||||
dealloc args[i-1]
|
|
||||||
|
|
||||||
proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
|
|
||||||
args: var TNodeSeq, start, len: int,
|
|
||||||
info: TLineInfo): PNode =
|
|
||||||
internalAssert conf, fn.kind == nkPtrLit
|
|
||||||
|
|
||||||
var cif: TCif
|
|
||||||
var sig: ParamList
|
|
||||||
for i in 0..len-1:
|
|
||||||
var aTyp = args[i+start].typ
|
|
||||||
if aTyp.isNil:
|
|
||||||
internalAssert conf, i+1 < fntyp.len
|
|
||||||
aTyp = fntyp[i+1]
|
|
||||||
args[i+start].typ = aTyp
|
|
||||||
sig[i] = mapType(conf, aTyp)
|
|
||||||
if sig[i].isNil: globalError(conf, info, "cannot map FFI type")
|
|
||||||
|
|
||||||
if prep_cif(cif, mapCallConv(conf, fntyp.callConv, info), cuint(len),
|
|
||||||
mapType(conf, fntyp[0]), sig) != OK:
|
|
||||||
globalError(conf, info, "error in FFI call")
|
|
||||||
|
|
||||||
var cargs: ArgList
|
|
||||||
let fn = cast[pointer](fn.intVal)
|
|
||||||
for i in 0..len-1:
|
|
||||||
let t = args[i+start].typ
|
|
||||||
cargs[i] = alloc0(packSize(conf, args[i+start], t))
|
|
||||||
pack(conf, args[i+start], t, cargs[i])
|
|
||||||
let retVal = if isEmptyType(fntyp[0]): pointer(nil)
|
|
||||||
else: alloc(getSize(conf, fntyp[0]).int)
|
|
||||||
|
|
||||||
libffi.call(cif, fn, retVal, cargs)
|
|
||||||
|
|
||||||
if retVal.isNil:
|
|
||||||
result = newNode(nkEmpty)
|
|
||||||
else:
|
|
||||||
result = unpack(conf, retVal, fntyp[0], nil)
|
|
||||||
result.info = info
|
|
||||||
|
|
||||||
if retVal != nil: dealloc retVal
|
|
||||||
for i in 0..len-1:
|
|
||||||
let t = args[i+start].typ
|
|
||||||
args[i+start] = unpack(conf, cargs[i], t, args[i+start])
|
|
||||||
dealloc cargs[i]
|
|
||||||
|
|
@ -10,207 +10,97 @@
|
||||||
## Template evaluation engine. Now hygienic.
|
## Template evaluation engine. Now hygienic.
|
||||||
|
|
||||||
import
|
import
|
||||||
strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents
|
strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer,
|
||||||
|
rodread
|
||||||
|
|
||||||
type
|
type
|
||||||
TemplCtx = object
|
TemplCtx {.pure, final.} = object
|
||||||
owner, genSymOwner: PSym
|
owner, genSymOwner: PSym
|
||||||
instLines: bool # use the instantiation lines numbers
|
instLines: bool # use the instantiation lines numbers
|
||||||
isDeclarative: bool
|
|
||||||
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
|
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
|
||||||
# new symbol
|
# new symbol
|
||||||
config: ConfigRef
|
|
||||||
ic: IdentCache
|
|
||||||
instID: int
|
|
||||||
idgen: IdGenerator
|
|
||||||
|
|
||||||
proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
|
proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
|
||||||
result = copyNode(a)
|
result = copyNode(a)
|
||||||
if ctx.instLines: setInfoRecursive(result, b.info)
|
if ctx.instLines: result.info = b.info
|
||||||
|
|
||||||
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||||
template handleParam(param) =
|
|
||||||
let x = param
|
|
||||||
if x.kind == nkArgList:
|
|
||||||
for y in items(x): result.add(y)
|
|
||||||
else:
|
|
||||||
result.add copyTree(x)
|
|
||||||
|
|
||||||
case templ.kind
|
case templ.kind
|
||||||
of nkSym:
|
of nkSym:
|
||||||
var s = templ.sym
|
var s = templ.sym
|
||||||
if (s.owner == nil and s.kind == skParam) or s.owner == c.owner:
|
if s.owner.id == c.owner.id:
|
||||||
if s.kind == skParam and {sfGenSym, sfTemplateParam} * s.flags == {sfTemplateParam}:
|
if s.kind == skParam and sfGenSym notin s.flags:
|
||||||
handleParam actual[s.position]
|
let x = actual.sons[s.position]
|
||||||
elif (s.owner != nil) and (s.kind == skGenericParam or
|
if x.kind == nkArgList:
|
||||||
s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam):
|
for y in items(x): result.add(y)
|
||||||
handleParam actual[s.owner.typ.len + s.position - 1]
|
else:
|
||||||
|
result.add copyTree(x)
|
||||||
else:
|
else:
|
||||||
internalAssert c.config, sfGenSym in s.flags or s.kind == skType
|
internalAssert sfGenSym in s.flags
|
||||||
var x = PSym(idTableGet(c.mapping, s))
|
var x = PSym(idTableGet(c.mapping, s))
|
||||||
if x == nil:
|
if x == nil:
|
||||||
x = copySym(s, nextSymId(c.idgen))
|
x = copySym(s, false)
|
||||||
# sem'check needs to set the owner properly later, see bug #9476
|
x.owner = c.genSymOwner
|
||||||
x.owner = nil # c.genSymOwner
|
|
||||||
#if x.kind == skParam and x.owner.kind == skModule:
|
|
||||||
# internalAssert c.config, false
|
|
||||||
idTablePut(c.mapping, s, x)
|
idTablePut(c.mapping, s, x)
|
||||||
if sfGenSym in s.flags:
|
result.add newSymNode(x, if c.instLines: actual.info else: templ.info)
|
||||||
result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID),
|
|
||||||
if c.instLines: actual.info else: templ.info)
|
|
||||||
else:
|
|
||||||
result.add newSymNode(x, if c.instLines: actual.info else: templ.info)
|
|
||||||
else:
|
else:
|
||||||
result.add copyNode(c, templ, actual)
|
result.add copyNode(c, templ, actual)
|
||||||
of nkNone..nkIdent, nkType..nkNilLit: # atom
|
of nkNone..nkIdent, nkType..nkNilLit: # atom
|
||||||
result.add copyNode(c, templ, actual)
|
result.add copyNode(c, templ, actual)
|
||||||
of nkCommentStmt:
|
|
||||||
# for the documentation generator we don't keep documentation comments
|
|
||||||
# in the AST that would confuse it (bug #9432), but only if we are not in a
|
|
||||||
# "declarative" context (bug #9235).
|
|
||||||
if c.isDeclarative:
|
|
||||||
var res = copyNode(c, templ, actual)
|
|
||||||
for i in 0..<templ.len:
|
|
||||||
evalTemplateAux(templ[i], actual, c, res)
|
|
||||||
result.add res
|
|
||||||
else:
|
|
||||||
result.add newNodeI(nkEmpty, templ.info)
|
|
||||||
else:
|
else:
|
||||||
var isDeclarative = false
|
var res = copyNode(c, templ, actual)
|
||||||
if templ.kind in {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef,
|
for i in countup(0, sonsLen(templ) - 1):
|
||||||
nkMacroDef, nkTemplateDef, nkConverterDef, nkTypeSection,
|
evalTemplateAux(templ.sons[i], actual, c, res)
|
||||||
nkVarSection, nkLetSection, nkConstSection} and
|
result.add res
|
||||||
not c.isDeclarative:
|
|
||||||
c.isDeclarative = true
|
|
||||||
isDeclarative = true
|
|
||||||
if (not c.isDeclarative) and templ.kind in nkCallKinds and isRunnableExamples(templ[0]):
|
|
||||||
# fixes bug #16993, bug #18054
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
var res = copyNode(c, templ, actual)
|
|
||||||
for i in 0..<templ.len:
|
|
||||||
evalTemplateAux(templ[i], actual, c, res)
|
|
||||||
result.add res
|
|
||||||
if isDeclarative: c.isDeclarative = false
|
|
||||||
|
|
||||||
const
|
proc evalTemplateArgs(n: PNode, s: PSym): PNode =
|
||||||
errWrongNumberOfArguments = "wrong number of arguments"
|
|
||||||
errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
|
|
||||||
errTemplateInstantiationTooNested = "template instantiation too nested"
|
|
||||||
|
|
||||||
proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode =
|
|
||||||
# if the template has zero arguments, it can be called without ``()``
|
# if the template has zero arguments, it can be called without ``()``
|
||||||
# `n` is then a nkSym or something similar
|
# `n` is then a nkSym or something similar
|
||||||
var totalParams = case n.kind
|
var a: int
|
||||||
of nkCallKinds: n.len-1
|
case n.kind
|
||||||
else: 0
|
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
|
||||||
|
a = sonsLen(n)
|
||||||
var
|
else: a = 0
|
||||||
# XXX: Since immediate templates are not subject to the
|
var f = s.typ.sonsLen
|
||||||
# standard sigmatching algorithm, they will have a number
|
if a > f: globalError(n.info, errWrongNumberOfArguments)
|
||||||
# of deficiencies when it comes to generic params:
|
|
||||||
# Type dependencies between the parameters won't be honoured
|
|
||||||
# and the bound generic symbols won't be resolvable within
|
|
||||||
# their bodies. We could try to fix this, but it may be
|
|
||||||
# wiser to just deprecate immediate templates and macros
|
|
||||||
# now that we have working untyped parameters.
|
|
||||||
genericParams = if fromHlo: 0
|
|
||||||
else: s.ast[genericParamsPos].len
|
|
||||||
expectedRegularParams = s.typ.len-1
|
|
||||||
givenRegularParams = totalParams - genericParams
|
|
||||||
if givenRegularParams < 0: givenRegularParams = 0
|
|
||||||
|
|
||||||
if totalParams > expectedRegularParams + genericParams:
|
|
||||||
globalError(conf, n.info, errWrongNumberOfArguments)
|
|
||||||
|
|
||||||
if totalParams < genericParams:
|
|
||||||
globalError(conf, n.info, errMissingGenericParamsForTemplate %
|
|
||||||
n.renderTree)
|
|
||||||
|
|
||||||
result = newNodeI(nkArgList, n.info)
|
result = newNodeI(nkArgList, n.info)
|
||||||
for i in 1..givenRegularParams:
|
for i in countup(1, f - 1):
|
||||||
result.add n[i]
|
var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast)
|
||||||
|
if arg == nil or arg.kind == nkEmpty:
|
||||||
|
localError(n.info, errWrongNumberOfArguments)
|
||||||
|
addSon(result, arg)
|
||||||
|
|
||||||
# handle parameters with default values, which were
|
var evalTemplateCounter* = 0
|
||||||
# not supplied by the user
|
# to prevent endless recursion in templates instantiation
|
||||||
for i in givenRegularParams+1..expectedRegularParams:
|
|
||||||
let default = s.typ.n[i].sym.ast
|
|
||||||
if default.isNil or default.kind == nkEmpty:
|
|
||||||
localError(conf, n.info, errWrongNumberOfArguments)
|
|
||||||
result.add newNodeI(nkEmpty, n.info)
|
|
||||||
else:
|
|
||||||
result.add default.copyTree
|
|
||||||
|
|
||||||
# add any generic parameters
|
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym): PNode =
|
||||||
for i in 1..genericParams:
|
inc(evalTemplateCounter)
|
||||||
result.add n[givenRegularParams + i]
|
if evalTemplateCounter > 100:
|
||||||
|
globalError(n.info, errTemplateInstantiationTooNested)
|
||||||
# to prevent endless recursion in template instantiation
|
|
||||||
const evalTemplateLimit* = 1000
|
|
||||||
|
|
||||||
proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
|
|
||||||
when true:
|
|
||||||
result = res
|
|
||||||
result.info = info
|
|
||||||
if result.kind in {nkStmtList, nkStmtListExpr} and result.len > 0:
|
|
||||||
result.lastSon.info = info
|
|
||||||
when false:
|
|
||||||
# this hack is required to
|
|
||||||
var x = result
|
|
||||||
while x.kind == nkStmtListExpr: x = x.lastSon
|
|
||||||
if x.kind in nkCallKinds:
|
|
||||||
for i in 1..<x.len:
|
|
||||||
if x[i].kind in nkCallKinds:
|
|
||||||
x[i].info = info
|
|
||||||
else:
|
|
||||||
result = newNodeI(nkStmtListExpr, info)
|
|
||||||
var d = newNodeI(nkComesFrom, info)
|
|
||||||
d.add newSymNode(sym, info)
|
|
||||||
result.add d
|
|
||||||
result.add res
|
|
||||||
result.typ = res.typ
|
|
||||||
|
|
||||||
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
|
||||||
conf: ConfigRef;
|
|
||||||
ic: IdentCache; instID: ref int;
|
|
||||||
idgen: IdGenerator;
|
|
||||||
fromHlo=false): PNode =
|
|
||||||
inc(conf.evalTemplateCounter)
|
|
||||||
if conf.evalTemplateCounter > evalTemplateLimit:
|
|
||||||
globalError(conf, n.info, errTemplateInstantiationTooNested)
|
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
# replace each param by the corresponding node:
|
# replace each param by the corresponding node:
|
||||||
var args = evalTemplateArgs(n, tmpl, conf, fromHlo)
|
var args = evalTemplateArgs(n, tmpl)
|
||||||
var ctx: TemplCtx
|
var ctx: TemplCtx
|
||||||
ctx.owner = tmpl
|
ctx.owner = tmpl
|
||||||
ctx.genSymOwner = genSymOwner
|
ctx.genSymOwner = genSymOwner
|
||||||
ctx.config = conf
|
|
||||||
ctx.ic = ic
|
|
||||||
initIdTable(ctx.mapping)
|
initIdTable(ctx.mapping)
|
||||||
ctx.instID = instID[]
|
|
||||||
ctx.idgen = idgen
|
|
||||||
|
|
||||||
let body = tmpl.ast[bodyPos]
|
let body = tmpl.getBody
|
||||||
#echo "instantion of ", renderTree(body, {renderIds})
|
|
||||||
if isAtom(body):
|
if isAtom(body):
|
||||||
result = newNodeI(nkPar, body.info)
|
result = newNodeI(nkPar, body.info)
|
||||||
evalTemplateAux(body, args, ctx, result)
|
evalTemplateAux(body, args, ctx, result)
|
||||||
if result.len == 1: result = result[0]
|
if result.len == 1: result = result.sons[0]
|
||||||
else:
|
else:
|
||||||
localError(conf, result.info, "illformed AST: " &
|
localError(result.info, errIllFormedAstX,
|
||||||
renderTree(result, {renderNoComments}))
|
renderTree(result, {renderNoComments}))
|
||||||
else:
|
else:
|
||||||
result = copyNode(body)
|
result = copyNode(body)
|
||||||
ctx.instLines = sfCallsite in tmpl.flags
|
ctx.instLines = body.kind notin {nkStmtList, nkStmtListExpr,
|
||||||
if ctx.instLines:
|
nkBlockStmt, nkBlockExpr}
|
||||||
setInfoRecursive(result, n.info)
|
if ctx.instLines: result.info = n.info
|
||||||
for i in 0..<body.safeLen:
|
for i in countup(0, safeLen(body) - 1):
|
||||||
evalTemplateAux(body[i], args, ctx, result)
|
evalTemplateAux(body.sons[i], args, ctx, result)
|
||||||
result.flags.incl nfFromTemplate
|
|
||||||
result = wrapInComesFrom(n.info, tmpl, result)
|
dec(evalTemplateCounter)
|
||||||
#if ctx.debugActive:
|
|
||||||
# echo "instantion of ", renderTree(result, {renderIds})
|
|
||||||
dec(conf.evalTemplateCounter)
|
|
||||||
# The instID must be unique for every template instantiation, so we increment it here
|
|
||||||
inc instID[]
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -9,41 +9,46 @@
|
||||||
|
|
||||||
# This module implements Nim's standard template filter.
|
# This module implements Nim's standard template filter.
|
||||||
|
|
||||||
import
|
import
|
||||||
llstream, strutils, ast, msgs, options,
|
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
||||||
filters, lineinfos, pathutils
|
renderer, filters
|
||||||
|
|
||||||
type
|
proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
|
||||||
TParseState = enum
|
# #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
|
||||||
|
# implementation
|
||||||
|
|
||||||
|
type
|
||||||
|
TParseState = enum
|
||||||
psDirective, psTempl
|
psDirective, psTempl
|
||||||
TTmplParser = object
|
TTmplParser{.final.} = object
|
||||||
inp: PLLStream
|
inp: PLLStream
|
||||||
state: TParseState
|
state: TParseState
|
||||||
info: TLineInfo
|
info: TLineInfo
|
||||||
indent, emitPar: int
|
indent, emitPar: int
|
||||||
x: string # the current input line
|
x: string # the current input line
|
||||||
outp: PLLStream # the output will be parsed by parser
|
outp: PLLStream # the ouput will be parsed by pnimsyn
|
||||||
subsChar, nimDirective: char
|
subsChar, nimDirective: char
|
||||||
emit, conc, toStr: string
|
emit, conc, toStr: string
|
||||||
curly, bracket, par: int
|
curly, bracket, par: int
|
||||||
pendingExprLine: bool
|
pendingExprLine: bool
|
||||||
config: ConfigRef
|
|
||||||
|
|
||||||
const
|
|
||||||
|
const
|
||||||
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
|
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
|
||||||
|
|
||||||
proc newLine(p: var TTmplParser) =
|
proc newLine(p: var TTmplParser) =
|
||||||
llStreamWrite(p.outp, repeat(')', p.emitPar))
|
llStreamWrite(p.outp, repeat(')', p.emitPar))
|
||||||
p.emitPar = 0
|
p.emitPar = 0
|
||||||
if p.info.line > uint16(1): llStreamWrite(p.outp, "\n")
|
if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
|
||||||
if p.pendingExprLine:
|
if p.pendingExprLine:
|
||||||
llStreamWrite(p.outp, spaces(2))
|
llStreamWrite(p.outp, spaces(2))
|
||||||
p.pendingExprLine = false
|
p.pendingExprLine = false
|
||||||
|
|
||||||
proc scanPar(p: var TTmplParser, d: int) =
|
proc scanPar(p: var TTmplParser, d: int) =
|
||||||
var i = d
|
var i = d
|
||||||
while i < p.x.len:
|
while true:
|
||||||
case p.x[i]
|
case p.x[i]
|
||||||
|
of '\0': break
|
||||||
of '(': inc(p.par)
|
of '(': inc(p.par)
|
||||||
of ')': dec(p.par)
|
of ')': dec(p.par)
|
||||||
of '[': inc(p.bracket)
|
of '[': inc(p.bracket)
|
||||||
|
|
@ -53,51 +58,47 @@ proc scanPar(p: var TTmplParser, d: int) =
|
||||||
else: discard
|
else: discard
|
||||||
inc(i)
|
inc(i)
|
||||||
|
|
||||||
proc withInExpr(p: TTmplParser): bool {.inline.} =
|
proc withInExpr(p: TTmplParser): bool {.inline.} =
|
||||||
result = p.par > 0 or p.bracket > 0 or p.curly > 0
|
result = p.par > 0 or p.bracket > 0 or p.curly > 0
|
||||||
|
|
||||||
const
|
proc parseLine(p: var TTmplParser) =
|
||||||
LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '^',
|
var
|
||||||
'|', '%', '&', '$', '@', '~', ','}
|
d, j, curly: int
|
||||||
|
keyw: string
|
||||||
proc parseLine(p: var TTmplParser) =
|
j = 0
|
||||||
var j = 0
|
while p.x[j] == ' ': inc(j)
|
||||||
let len = p.x.len
|
if (p.x[0] == p.nimDirective) and (p.x[0 + 1] == '!'):
|
||||||
|
|
||||||
while j < len and p.x[j] == ' ': inc(j)
|
|
||||||
|
|
||||||
if len >= 2 and p.x[0] == p.nimDirective and p.x[1] == '?':
|
|
||||||
newLine(p)
|
newLine(p)
|
||||||
elif j < len and p.x[j] == p.nimDirective:
|
elif (p.x[j] == p.nimDirective):
|
||||||
newLine(p)
|
newLine(p)
|
||||||
inc(j)
|
inc(j)
|
||||||
while j < len and p.x[j] == ' ': inc(j)
|
while p.x[j] == ' ': inc(j)
|
||||||
let d = j
|
d = j
|
||||||
var keyw = ""
|
keyw = ""
|
||||||
while j < len and p.x[j] in PatternChars:
|
while p.x[j] in PatternChars:
|
||||||
keyw.add(p.x[j])
|
add(keyw, p.x[j])
|
||||||
inc(j)
|
inc(j)
|
||||||
|
|
||||||
scanPar(p, j)
|
scanPar(p, j)
|
||||||
p.pendingExprLine = withInExpr(p) or p.x.endsWith(LineContinuationOprs)
|
p.pendingExprLine = withInExpr(p) or llstream.endsWithOpr(p.x)
|
||||||
case keyw
|
case whichKeyword(keyw)
|
||||||
of "end":
|
of wEnd:
|
||||||
if p.indent >= 2:
|
if p.indent >= 2:
|
||||||
dec(p.indent, 2)
|
dec(p.indent, 2)
|
||||||
else:
|
else:
|
||||||
p.info.col = int16(j)
|
p.info.col = int16(j)
|
||||||
localError(p.config, p.info, "'end' does not close a control flow construct")
|
localError(p.info, errXNotAllowedHere, "end")
|
||||||
llStreamWrite(p.outp, spaces(p.indent))
|
llStreamWrite(p.outp, spaces(p.indent))
|
||||||
llStreamWrite(p.outp, "#end")
|
llStreamWrite(p.outp, "#end")
|
||||||
of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator",
|
of wIf, wWhen, wTry, wWhile, wFor, wBlock, wCase, wProc, wIterator,
|
||||||
"converter", "macro", "template", "method", "func":
|
wConverter, wMacro, wTemplate, wMethod:
|
||||||
llStreamWrite(p.outp, spaces(p.indent))
|
llStreamWrite(p.outp, spaces(p.indent))
|
||||||
llStreamWrite(p.outp, substr(p.x, d))
|
llStreamWrite(p.outp, substr(p.x, d))
|
||||||
inc(p.indent, 2)
|
inc(p.indent, 2)
|
||||||
of "elif", "of", "else", "except", "finally":
|
of wElif, wOf, wElse, wExcept, wFinally:
|
||||||
llStreamWrite(p.outp, spaces(p.indent - 2))
|
llStreamWrite(p.outp, spaces(p.indent - 2))
|
||||||
llStreamWrite(p.outp, substr(p.x, d))
|
llStreamWrite(p.outp, substr(p.x, d))
|
||||||
of "let", "var", "const", "type":
|
of wLet, wVar, wConst, wType:
|
||||||
llStreamWrite(p.outp, spaces(p.indent))
|
llStreamWrite(p.outp, spaces(p.indent))
|
||||||
llStreamWrite(p.outp, substr(p.x, d))
|
llStreamWrite(p.outp, substr(p.x, d))
|
||||||
if not p.x.contains({':', '='}):
|
if not p.x.contains({':', '='}):
|
||||||
|
|
@ -107,7 +108,7 @@ proc parseLine(p: var TTmplParser) =
|
||||||
llStreamWrite(p.outp, spaces(p.indent))
|
llStreamWrite(p.outp, spaces(p.indent))
|
||||||
llStreamWrite(p.outp, substr(p.x, d))
|
llStreamWrite(p.outp, substr(p.x, d))
|
||||||
p.state = psDirective
|
p.state = psDirective
|
||||||
else:
|
else:
|
||||||
# data line
|
# data line
|
||||||
# reset counters
|
# reset counters
|
||||||
p.par = 0
|
p.par = 0
|
||||||
|
|
@ -115,108 +116,105 @@ proc parseLine(p: var TTmplParser) =
|
||||||
p.bracket = 0
|
p.bracket = 0
|
||||||
j = 0
|
j = 0
|
||||||
case p.state
|
case p.state
|
||||||
of psTempl:
|
of psTempl:
|
||||||
# next line of string literal:
|
# next line of string literal:
|
||||||
llStreamWrite(p.outp, p.conc)
|
llStreamWrite(p.outp, p.conc)
|
||||||
llStreamWrite(p.outp, "\n")
|
llStreamWrite(p.outp, "\n")
|
||||||
llStreamWrite(p.outp, spaces(p.indent + 2))
|
llStreamWrite(p.outp, spaces(p.indent + 2))
|
||||||
llStreamWrite(p.outp, "\"")
|
llStreamWrite(p.outp, "\"")
|
||||||
of psDirective:
|
of psDirective:
|
||||||
newLine(p)
|
newLine(p)
|
||||||
llStreamWrite(p.outp, spaces(p.indent))
|
llStreamWrite(p.outp, spaces(p.indent))
|
||||||
llStreamWrite(p.outp, p.emit)
|
llStreamWrite(p.outp, p.emit)
|
||||||
llStreamWrite(p.outp, "(\"")
|
llStreamWrite(p.outp, "(\"")
|
||||||
inc(p.emitPar)
|
inc(p.emitPar)
|
||||||
p.state = psTempl
|
p.state = psTempl
|
||||||
while j < len:
|
while true:
|
||||||
case p.x[j]
|
case p.x[j]
|
||||||
of '\x01'..'\x1F', '\x80'..'\xFF':
|
of '\0':
|
||||||
|
break
|
||||||
|
of '\x01'..'\x1F', '\x80'..'\xFF':
|
||||||
llStreamWrite(p.outp, "\\x")
|
llStreamWrite(p.outp, "\\x")
|
||||||
llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
|
llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
|
||||||
inc(j)
|
inc(j)
|
||||||
of '\\':
|
of '\\':
|
||||||
llStreamWrite(p.outp, "\\\\")
|
llStreamWrite(p.outp, "\\\\")
|
||||||
inc(j)
|
inc(j)
|
||||||
of '\'':
|
of '\'':
|
||||||
llStreamWrite(p.outp, "\\\'")
|
llStreamWrite(p.outp, "\\\'")
|
||||||
inc(j)
|
inc(j)
|
||||||
of '\"':
|
of '\"':
|
||||||
llStreamWrite(p.outp, "\\\"")
|
llStreamWrite(p.outp, "\\\"")
|
||||||
inc(j)
|
inc(j)
|
||||||
else:
|
else:
|
||||||
if p.x[j] == p.subsChar:
|
if p.x[j] == p.subsChar:
|
||||||
# parse Nim expression:
|
# parse Nim expression:
|
||||||
inc(j)
|
inc(j)
|
||||||
case p.x[j]
|
case p.x[j]
|
||||||
of '{':
|
of '{':
|
||||||
p.info.col = int16(j)
|
p.info.col = int16(j)
|
||||||
llStreamWrite(p.outp, '\"')
|
llStreamWrite(p.outp, '\"')
|
||||||
llStreamWrite(p.outp, p.conc)
|
llStreamWrite(p.outp, p.conc)
|
||||||
llStreamWrite(p.outp, p.toStr)
|
llStreamWrite(p.outp, p.toStr)
|
||||||
llStreamWrite(p.outp, '(')
|
llStreamWrite(p.outp, '(')
|
||||||
inc(j)
|
inc(j)
|
||||||
var curly = 0
|
curly = 0
|
||||||
while j < len:
|
while true:
|
||||||
case p.x[j]
|
case p.x[j]
|
||||||
of '{':
|
of '\0':
|
||||||
|
localError(p.info, errXExpected, "}")
|
||||||
|
break
|
||||||
|
of '{':
|
||||||
inc(j)
|
inc(j)
|
||||||
inc(curly)
|
inc(curly)
|
||||||
llStreamWrite(p.outp, '{')
|
llStreamWrite(p.outp, '{')
|
||||||
of '}':
|
of '}':
|
||||||
inc(j)
|
inc(j)
|
||||||
if curly == 0: break
|
if curly == 0: break
|
||||||
if curly > 0: dec(curly)
|
if curly > 0: dec(curly)
|
||||||
llStreamWrite(p.outp, '}')
|
llStreamWrite(p.outp, '}')
|
||||||
else:
|
else:
|
||||||
llStreamWrite(p.outp, p.x[j])
|
llStreamWrite(p.outp, p.x[j])
|
||||||
inc(j)
|
inc(j)
|
||||||
if curly > 0:
|
|
||||||
localError(p.config, p.info, "expected closing '}'")
|
|
||||||
break
|
|
||||||
llStreamWrite(p.outp, ')')
|
llStreamWrite(p.outp, ')')
|
||||||
llStreamWrite(p.outp, p.conc)
|
llStreamWrite(p.outp, p.conc)
|
||||||
llStreamWrite(p.outp, '\"')
|
llStreamWrite(p.outp, '\"')
|
||||||
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
|
of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
|
||||||
llStreamWrite(p.outp, '\"')
|
llStreamWrite(p.outp, '\"')
|
||||||
llStreamWrite(p.outp, p.conc)
|
llStreamWrite(p.outp, p.conc)
|
||||||
llStreamWrite(p.outp, p.toStr)
|
llStreamWrite(p.outp, p.toStr)
|
||||||
llStreamWrite(p.outp, '(')
|
llStreamWrite(p.outp, '(')
|
||||||
while j < len and p.x[j] in PatternChars:
|
while p.x[j] in PatternChars:
|
||||||
llStreamWrite(p.outp, p.x[j])
|
llStreamWrite(p.outp, p.x[j])
|
||||||
inc(j)
|
inc(j)
|
||||||
llStreamWrite(p.outp, ')')
|
llStreamWrite(p.outp, ')')
|
||||||
llStreamWrite(p.outp, p.conc)
|
llStreamWrite(p.outp, p.conc)
|
||||||
llStreamWrite(p.outp, '\"')
|
llStreamWrite(p.outp, '\"')
|
||||||
else:
|
else:
|
||||||
if p.x[j] == p.subsChar:
|
if p.x[j] == p.subsChar:
|
||||||
llStreamWrite(p.outp, p.subsChar)
|
llStreamWrite(p.outp, p.subsChar)
|
||||||
inc(j)
|
inc(j)
|
||||||
else:
|
else:
|
||||||
p.info.col = int16(j)
|
p.info.col = int16(j)
|
||||||
localError(p.config, p.info, "invalid expression")
|
localError(p.info, errInvalidExpression, "$")
|
||||||
else:
|
else:
|
||||||
llStreamWrite(p.outp, p.x[j])
|
llStreamWrite(p.outp, p.x[j])
|
||||||
inc(j)
|
inc(j)
|
||||||
llStreamWrite(p.outp, "\\n\"")
|
llStreamWrite(p.outp, "\\n\"")
|
||||||
|
|
||||||
proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
|
proc filterTmpl(stdin: PLLStream, filename: string, call: PNode): PLLStream =
|
||||||
call: PNode): PLLStream =
|
|
||||||
var p: TTmplParser
|
var p: TTmplParser
|
||||||
p.config = conf
|
p.info = newLineInfo(filename, 0, 0)
|
||||||
p.info = newLineInfo(conf, filename, 0, 0)
|
|
||||||
p.outp = llStreamOpen("")
|
p.outp = llStreamOpen("")
|
||||||
p.inp = stdin
|
p.inp = stdin
|
||||||
p.subsChar = charArg(conf, call, "subschar", 1, '$')
|
p.subsChar = charArg(call, "subschar", 1, '$')
|
||||||
p.nimDirective = charArg(conf, call, "metachar", 2, '#')
|
p.nimDirective = charArg(call, "metachar", 2, '#')
|
||||||
p.emit = strArg(conf, call, "emit", 3, "result.add")
|
p.emit = strArg(call, "emit", 3, "result.add")
|
||||||
p.conc = strArg(conf, call, "conc", 4, " & ")
|
p.conc = strArg(call, "conc", 4, " & ")
|
||||||
p.toStr = strArg(conf, call, "tostring", 5, "$")
|
p.toStr = strArg(call, "tostring", 5, "$")
|
||||||
p.x = newStringOfCap(120)
|
p.x = newStringOfCap(120)
|
||||||
# do not process the first line which contains the directive:
|
|
||||||
if llStreamReadLine(p.inp, p.x):
|
|
||||||
inc p.info.line
|
|
||||||
while llStreamReadLine(p.inp, p.x):
|
while llStreamReadLine(p.inp, p.x):
|
||||||
inc p.info.line
|
p.info.line = p.info.line + int16(1)
|
||||||
parseLine(p)
|
parseLine(p)
|
||||||
newLine(p)
|
newLine(p)
|
||||||
result = p.outp
|
result = p.outp
|
||||||
|
|
|
||||||
|
|
@ -10,61 +10,68 @@
|
||||||
# This module implements Nim's simple filters and helpers for filters.
|
# This module implements Nim's simple filters and helpers for filters.
|
||||||
|
|
||||||
import
|
import
|
||||||
llstream, idents, strutils, ast, msgs, options,
|
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
|
||||||
renderer, pathutils
|
renderer
|
||||||
|
|
||||||
proc invalidPragma(conf: ConfigRef; n: PNode) =
|
proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream
|
||||||
localError(conf, n.info,
|
proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream
|
||||||
"'$1' not allowed here" % renderTree(n, {renderNoComments}))
|
# helpers to retrieve arguments:
|
||||||
|
proc charArg*(n: PNode, name: string, pos: int, default: char): char
|
||||||
|
proc strArg*(n: PNode, name: string, pos: int, default: string): string
|
||||||
|
proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool
|
||||||
|
# implementation
|
||||||
|
|
||||||
proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode =
|
proc invalidPragma(n: PNode) =
|
||||||
|
localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
|
||||||
|
|
||||||
|
proc getArg(n: PNode, name: string, pos: int): PNode =
|
||||||
result = nil
|
result = nil
|
||||||
if n.kind in {nkEmpty..nkNilLit}: return
|
if n.kind in {nkEmpty..nkNilLit}: return
|
||||||
for i in 1..<n.len:
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
if n[i].kind == nkExprEqExpr:
|
if n.sons[i].kind == nkExprEqExpr:
|
||||||
if n[i][0].kind != nkIdent: invalidPragma(conf, n)
|
if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n)
|
||||||
if cmpIgnoreStyle(n[i][0].ident.s, name) == 0:
|
if identEq(n.sons[i].sons[0].ident, name):
|
||||||
return n[i][1]
|
return n.sons[i].sons[1]
|
||||||
elif i == pos:
|
elif i == pos:
|
||||||
return n[i]
|
return n.sons[i]
|
||||||
|
|
||||||
proc charArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: char): char =
|
proc charArg(n: PNode, name: string, pos: int, default: char): char =
|
||||||
var x = getArg(conf, n, name, pos)
|
var x = getArg(n, name, pos)
|
||||||
if x == nil: result = default
|
if x == nil: result = default
|
||||||
elif x.kind == nkCharLit: result = chr(int(x.intVal))
|
elif x.kind == nkCharLit: result = chr(int(x.intVal))
|
||||||
else: invalidPragma(conf, n)
|
else: invalidPragma(n)
|
||||||
|
|
||||||
proc strArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: string): string =
|
proc strArg(n: PNode, name: string, pos: int, default: string): string =
|
||||||
var x = getArg(conf, n, name, pos)
|
var x = getArg(n, name, pos)
|
||||||
if x == nil: result = default
|
if x == nil: result = default
|
||||||
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
|
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
|
||||||
else: invalidPragma(conf, n)
|
else: invalidPragma(n)
|
||||||
|
|
||||||
proc boolArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: bool): bool =
|
proc boolArg(n: PNode, name: string, pos: int, default: bool): bool =
|
||||||
var x = getArg(conf, n, name, pos)
|
var x = getArg(n, name, pos)
|
||||||
if x == nil: result = default
|
if x == nil: result = default
|
||||||
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true
|
elif (x.kind == nkIdent) and identEq(x.ident, "true"): result = true
|
||||||
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false
|
elif (x.kind == nkIdent) and identEq(x.ident, "false"): result = false
|
||||||
else: invalidPragma(conf, n)
|
else: invalidPragma(n)
|
||||||
|
|
||||||
proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream =
|
proc filterStrip(stdin: PLLStream, filename: string, call: PNode): PLLStream =
|
||||||
var pattern = strArg(conf, call, "startswith", 1, "")
|
var pattern = strArg(call, "startswith", 1, "")
|
||||||
var leading = boolArg(conf, call, "leading", 2, true)
|
var leading = boolArg(call, "leading", 2, true)
|
||||||
var trailing = boolArg(conf, call, "trailing", 3, true)
|
var trailing = boolArg(call, "trailing", 3, true)
|
||||||
result = llStreamOpen("")
|
result = llStreamOpen("")
|
||||||
var line = newStringOfCap(80)
|
var line = newStringOfCap(80)
|
||||||
while llStreamReadLine(stdin, line):
|
while llStreamReadLine(stdin, line):
|
||||||
var stripped = strip(line, leading, trailing)
|
var stripped = strip(line, leading, trailing)
|
||||||
if pattern.len == 0 or startsWith(stripped, pattern):
|
if (len(pattern) == 0) or startsWith(stripped, pattern):
|
||||||
llStreamWriteln(result, stripped)
|
llStreamWriteln(result, stripped)
|
||||||
else:
|
else:
|
||||||
llStreamWriteln(result, line)
|
llStreamWriteln(result, line)
|
||||||
llStreamClose(stdin)
|
llStreamClose(stdin)
|
||||||
|
|
||||||
proc filterReplace*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream =
|
proc filterReplace(stdin: PLLStream, filename: string, call: PNode): PLLStream =
|
||||||
var sub = strArg(conf, call, "sub", 1, "")
|
var sub = strArg(call, "sub", 1, "")
|
||||||
if sub.len == 0: invalidPragma(conf, call)
|
if len(sub) == 0: invalidPragma(call)
|
||||||
var by = strArg(conf, call, "by", 2, "")
|
var by = strArg(call, "by", 2, "")
|
||||||
result = llStreamOpen("")
|
result = llStreamOpen("")
|
||||||
var line = newStringOfCap(80)
|
var line = newStringOfCap(80)
|
||||||
while llStreamReadLine(stdin, line):
|
while llStreamReadLine(stdin, line):
|
||||||
|
|
|
||||||
89
compiler/forloops.nim
Normal file
89
compiler/forloops.nim
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2015 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module implements for loop detection for better C code generation.
|
||||||
|
|
||||||
|
import ast, astalgo
|
||||||
|
|
||||||
|
const
|
||||||
|
someCmp = {mEqI, mEqI64, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||||
|
mEqUntracedRef, mLeI, mLeI64, mLeF64, mLeU, mLeU64, mLeEnum,
|
||||||
|
mLeCh, mLeB, mLePtr, mLtI, mLtI64, mLtF64, mLtU, mLtU64, mLtEnum,
|
||||||
|
mLtCh, mLtB, mLtPtr}
|
||||||
|
|
||||||
|
proc isCounter(s: PSym): bool {.inline.} =
|
||||||
|
s.kind in {skResult, skVar, skLet, skTemp} and
|
||||||
|
{sfGlobal, sfAddrTaken} * s.flags == {}
|
||||||
|
|
||||||
|
proc isCall(n: PNode): bool {.inline.} =
|
||||||
|
n.kind in nkCallKinds and n[0].kind == nkSym
|
||||||
|
|
||||||
|
proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
|
||||||
|
|
||||||
|
proc getCounter(lastStmt: PNode): PSym =
|
||||||
|
if lastStmt.isCall:
|
||||||
|
let op = lastStmt.sym
|
||||||
|
if op.magic in {mDec, mInc} or
|
||||||
|
((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem):
|
||||||
|
if op[1].kind == nkSym and isCounter(op[1].sym):
|
||||||
|
result = op[1].sym
|
||||||
|
|
||||||
|
proc counterInTree(n, loop: PNode; counter: PSym): bool =
|
||||||
|
# prune the search tree: within the loop the counter may be used:
|
||||||
|
if n == loop: return
|
||||||
|
case n.kind
|
||||||
|
of nkSym:
|
||||||
|
if n.sym == counter: return true
|
||||||
|
of nkVarSection, nkLetSection:
|
||||||
|
# definitions are fine!
|
||||||
|
for it in n:
|
||||||
|
if counterInTree(it.lastSon): return true
|
||||||
|
else:
|
||||||
|
for i in 0 .. <safeLen(n):
|
||||||
|
if counterInTree(n[i], loop, counter): return true
|
||||||
|
|
||||||
|
proc copyExcept(n: PNode, x, dest: PNode) =
|
||||||
|
if x == n: return
|
||||||
|
if n.kind in {nkStmtList, nkStmtListExpr}:
|
||||||
|
for i in 0 .. <n.len: copyExcept(n[i], x, dest)
|
||||||
|
else:
|
||||||
|
dest.add n
|
||||||
|
|
||||||
|
type
|
||||||
|
ForLoop* = object
|
||||||
|
counter*: PSym
|
||||||
|
init*, cond*, increment*, body*: PNode
|
||||||
|
|
||||||
|
proc extractForLoop*(loop, fullTree: PNode): ForLoop =
|
||||||
|
## returns 'counter == nil' if the while loop 'n' is not a for loop:
|
||||||
|
assert loop.kind == nkWhileStmt
|
||||||
|
let cond == loop[0]
|
||||||
|
|
||||||
|
if not cond.isCall: return
|
||||||
|
if cond[0].sym.magic notin someCmp: return
|
||||||
|
|
||||||
|
var lastStmt = loop[1]
|
||||||
|
while lastStmt.kind in {nkStmtList, nkStmtListExpr}:
|
||||||
|
lastStmt = lastStmt.lastSon
|
||||||
|
|
||||||
|
let counter = getCounter(lastStmt)
|
||||||
|
if counter.isNil or counter.ast.isNil: return
|
||||||
|
|
||||||
|
template `=~`(a, b): expr = a.kind == nkSym and a.sym == b
|
||||||
|
|
||||||
|
if cond[1] =~ counter or cond[2] =~ counter:
|
||||||
|
# ok, now check 'counter' is not used *after* the loop
|
||||||
|
if counterInTree(fullTree, loop, counter): return
|
||||||
|
# ok, success, fill in the fields:
|
||||||
|
result.counter = counter
|
||||||
|
result.init = counter.ast
|
||||||
|
result.cond = cond
|
||||||
|
result.increment = lastStmt
|
||||||
|
result.body = newNodeI(nkStmtList, loop[1].info)
|
||||||
|
copyExcept(loop[1], lastStmt, result.body)
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2017 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Module that implements ``gorge`` for the compiler.
|
|
||||||
|
|
||||||
import msgs, std / sha1, os, osproc, streams, options,
|
|
||||||
lineinfos, pathutils
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/syncio
|
|
||||||
|
|
||||||
proc readOutput(p: Process): (string, int) =
|
|
||||||
result[0] = ""
|
|
||||||
var output = p.outputStream
|
|
||||||
while not output.atEnd:
|
|
||||||
result[0].add(output.readLine)
|
|
||||||
result[0].add("\n")
|
|
||||||
if result[0].len > 0:
|
|
||||||
result[0].setLen(result[0].len - "\n".len)
|
|
||||||
result[1] = p.waitForExit
|
|
||||||
|
|
||||||
proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
|
|
||||||
let workingDir = parentDir(toFullPath(conf, info))
|
|
||||||
if cache.len > 0:
|
|
||||||
let h = secureHash(cmd & "\t" & input & "\t" & cache)
|
|
||||||
let filename = toGeneratedFile(conf, AbsoluteFile("gorge_" & $h), "txt").string
|
|
||||||
var f: File
|
|
||||||
if optForceFullMake notin conf.globalOptions and open(f, filename):
|
|
||||||
result = (f.readAll, 0)
|
|
||||||
f.close
|
|
||||||
return
|
|
||||||
var readSuccessful = false
|
|
||||||
try:
|
|
||||||
var p = startProcess(cmd, workingDir,
|
|
||||||
options={poEvalCommand, poStdErrToStdOut})
|
|
||||||
if input.len != 0:
|
|
||||||
p.inputStream.write(input)
|
|
||||||
p.inputStream.close()
|
|
||||||
result = p.readOutput
|
|
||||||
p.close()
|
|
||||||
readSuccessful = true
|
|
||||||
# only cache successful runs:
|
|
||||||
if result[1] == 0:
|
|
||||||
writeFile(filename, result[0])
|
|
||||||
except IOError, OSError:
|
|
||||||
if not readSuccessful:
|
|
||||||
when defined(nimLegacyGorgeErrors):
|
|
||||||
result = ("", -1)
|
|
||||||
else:
|
|
||||||
result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
var p = startProcess(cmd, workingDir,
|
|
||||||
options={poEvalCommand, poStdErrToStdOut})
|
|
||||||
if input.len != 0:
|
|
||||||
p.inputStream.write(input)
|
|
||||||
p.inputStream.close()
|
|
||||||
result = p.readOutput
|
|
||||||
p.close()
|
|
||||||
except IOError, OSError:
|
|
||||||
when defined(nimLegacyGorgeErrors):
|
|
||||||
result = ("", -1)
|
|
||||||
else:
|
|
||||||
result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -8,31 +8,27 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# This include implements the high level optimization pass.
|
# This include implements the high level optimization pass.
|
||||||
# included from sem.nim
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
proc hlo(c: PContext, n: PNode): PNode
|
proc hlo(c: PContext, n: PNode): PNode
|
||||||
|
|
||||||
proc evalPattern(c: PContext, n, orig: PNode): PNode =
|
proc evalPattern(c: PContext, n, orig: PNode): PNode =
|
||||||
internalAssert c.config, n.kind == nkCall and n[0].kind == nkSym
|
internalAssert n.kind == nkCall and n.sons[0].kind == nkSym
|
||||||
# we need to ensure that the resulting AST is semchecked. However, it's
|
# 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
|
# aweful to semcheck before macro invocation, so we don't and treat
|
||||||
# templates and macros as immediate in this context.
|
# templates and macros as immediate in this context.
|
||||||
var rule: string
|
var rule: string
|
||||||
if c.config.hasHint(hintPattern):
|
if optHints in gOptions and hintPattern in gNotes:
|
||||||
rule = renderTree(n, {renderNoComments})
|
rule = renderTree(n, {renderNoComments})
|
||||||
let s = n[0].sym
|
let s = n.sons[0].sym
|
||||||
case s.kind
|
case s.kind
|
||||||
of skMacro:
|
of skMacro:
|
||||||
result = semMacroExpr(c, n, orig, s)
|
result = semMacroExpr(c, n, orig, s)
|
||||||
of skTemplate:
|
of skTemplate:
|
||||||
result = semTemplateExpr(c, n, s, {efFromHlo})
|
result = semTemplateExpr(c, n, s)
|
||||||
else:
|
else:
|
||||||
result = semDirectOp(c, n, {})
|
result = semDirectOp(c, n, {})
|
||||||
if c.config.hasHint(hintPattern):
|
if optHints in gOptions and hintPattern in gNotes:
|
||||||
message(c.config, orig.info, hintPattern, rule & " --> '" &
|
message(orig.info, hintPattern, rule & " --> '" &
|
||||||
renderTree(result, {renderNoComments}) & "'")
|
renderTree(result, {renderNoComments}) & "'")
|
||||||
|
|
||||||
proc applyPatterns(c: PContext, n: PNode): PNode =
|
proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||||
|
|
@ -40,25 +36,25 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
|
||||||
# we apply the last pattern first, so that pattern overriding is possible;
|
# we apply the last pattern first, so that pattern overriding is possible;
|
||||||
# however the resulting AST would better not trigger the old rule then
|
# however the resulting AST would better not trigger the old rule then
|
||||||
# anymore ;-)
|
# anymore ;-)
|
||||||
for i in countdown(c.patterns.len-1, 0):
|
for i in countdown(<c.patterns.len, 0):
|
||||||
let pattern = c.patterns[i]
|
let pattern = c.patterns[i]
|
||||||
if not isNil(pattern):
|
if not isNil(pattern):
|
||||||
let x = applyRule(c, pattern, result)
|
let x = applyRule(c, pattern, result)
|
||||||
if not isNil(x):
|
if not isNil(x):
|
||||||
assert x.kind in {nkStmtList, nkCall}
|
assert x.kind in {nkStmtList, nkCall}
|
||||||
# better be safe than sorry, so check evalTemplateCounter too:
|
# better be safe than sorry, so check evalTemplateCounter too:
|
||||||
inc(c.config.evalTemplateCounter)
|
inc(evalTemplateCounter)
|
||||||
if c.config.evalTemplateCounter > evalTemplateLimit:
|
if evalTemplateCounter > 100:
|
||||||
globalError(c.config, n.info, "template instantiation too nested")
|
globalError(n.info, errTemplateInstantiationTooNested)
|
||||||
# deactivate this pattern:
|
# deactivate this pattern:
|
||||||
c.patterns[i] = nil
|
c.patterns[i] = nil
|
||||||
if x.kind == nkStmtList:
|
if x.kind == nkStmtList:
|
||||||
assert x.len == 3
|
assert x.len == 3
|
||||||
x[1] = evalPattern(c, x[1], result)
|
x.sons[1] = evalPattern(c, x.sons[1], result)
|
||||||
result = flattenStmts(x)
|
result = flattenStmts(x)
|
||||||
else:
|
else:
|
||||||
result = evalPattern(c, x, result)
|
result = evalPattern(c, x, result)
|
||||||
dec(c.config.evalTemplateCounter)
|
dec(evalTemplateCounter)
|
||||||
# activate this pattern again:
|
# activate this pattern again:
|
||||||
c.patterns[i] = pattern
|
c.patterns[i] = pattern
|
||||||
|
|
||||||
|
|
@ -71,37 +67,37 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||||
# already processed (special cases in semstmts.nim)
|
# already processed (special cases in semstmts.nim)
|
||||||
result = n
|
result = n
|
||||||
else:
|
else:
|
||||||
if n.kind in {nkFastAsgn, nkAsgn, nkSinkAsgn, nkIdentDefs, nkVarTuple} and
|
if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
|
||||||
n[0].kind == nkSym and
|
n.sons[0].kind == nkSym and
|
||||||
{sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
|
{sfGlobal, sfPure} * n.sons[0].sym.flags == {sfGlobal, sfPure}:
|
||||||
# do not optimize 'var g {.global} = re(...)' again!
|
# do not optimize 'var g {.global} = re(...)' again!
|
||||||
return n
|
return n
|
||||||
result = applyPatterns(c, n)
|
result = applyPatterns(c, n)
|
||||||
if result == n:
|
if result == n:
|
||||||
# no optimization applied, try subtrees:
|
# no optimization applied, try subtrees:
|
||||||
for i in 0..<result.safeLen:
|
for i in 0 .. < safeLen(result):
|
||||||
let a = result[i]
|
let a = result.sons[i]
|
||||||
let h = hlo(c, a)
|
let h = hlo(c, a)
|
||||||
if h != a: result[i] = h
|
if h != a: result.sons[i] = h
|
||||||
else:
|
else:
|
||||||
# perform type checking, so that the replacement still fits:
|
# perform type checking, so that the replacement still fits:
|
||||||
if isEmptyType(n.typ) and isEmptyType(result.typ):
|
if isEmptyType(n.typ) and isEmptyType(result.typ):
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
result = fitNode(c, n.typ, result, n.info)
|
result = fitNode(c, n.typ, result)
|
||||||
# optimization has been applied so check again:
|
# optimization has been applied so check again:
|
||||||
result = commonOptimizations(c.graph, c.idgen, c.module, result)
|
result = commonOptimizations(c.module, result)
|
||||||
result = hlo(c, result)
|
result = hlo(c, result)
|
||||||
result = commonOptimizations(c.graph, c.idgen, c.module, result)
|
result = commonOptimizations(c.module, result)
|
||||||
|
|
||||||
proc hloBody(c: PContext, n: PNode): PNode =
|
proc hloBody(c: PContext, n: PNode): PNode =
|
||||||
# fast exit:
|
# fast exit:
|
||||||
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
|
if c.patterns.len == 0 or optPatterns notin gOptions: return n
|
||||||
c.hloLoopDetector = 0
|
c.hloLoopDetector = 0
|
||||||
result = hlo(c, n)
|
result = hlo(c, n)
|
||||||
|
|
||||||
proc hloStmt(c: PContext, n: PNode): PNode =
|
proc hloStmt(c: PContext, n: PNode): PNode =
|
||||||
# fast exit:
|
# fast exit:
|
||||||
if c.patterns.len == 0 or optTrMacros notin c.config.options: return n
|
if c.patterns.len == 0 or optPatterns notin gOptions: return n
|
||||||
c.hloLoopDetector = 0
|
c.hloLoopDetector = 0
|
||||||
result = hlo(c, n)
|
result = hlo(c, n)
|
||||||
|
|
|
||||||
|
|
@ -1,171 +0,0 @@
|
||||||
## A BiTable is a table that can be seen as an optimized pair
|
|
||||||
## of `(Table[LitId, Val], Table[Val, LitId])`.
|
|
||||||
|
|
||||||
import hashes, rodfiles
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
|
||||||
LitId* = distinct uint32
|
|
||||||
|
|
||||||
BiTable*[T] = object
|
|
||||||
vals: seq[T] # indexed by LitId
|
|
||||||
keys: seq[LitId] # indexed by hash(val)
|
|
||||||
|
|
||||||
proc nextTry(h, maxHash: Hash): Hash {.inline.} =
|
|
||||||
result = (h + 1) and maxHash
|
|
||||||
|
|
||||||
template maxHash(t): untyped = high(t.keys)
|
|
||||||
template isFilled(x: LitId): bool = x.uint32 > 0'u32
|
|
||||||
|
|
||||||
proc `$`*(x: LitId): string {.borrow.}
|
|
||||||
proc `<`*(x, y: LitId): bool {.borrow.}
|
|
||||||
proc `<=`*(x, y: LitId): bool {.borrow.}
|
|
||||||
proc `==`*(x, y: LitId): bool {.borrow.}
|
|
||||||
proc hash*(x: LitId): Hash {.borrow.}
|
|
||||||
|
|
||||||
|
|
||||||
proc len*[T](t: BiTable[T]): int = t.vals.len
|
|
||||||
|
|
||||||
proc mustRehash(length, counter: int): bool {.inline.} =
|
|
||||||
assert(length > counter)
|
|
||||||
result = (length * 2 < counter * 3) or (length - counter < 4)
|
|
||||||
|
|
||||||
const
|
|
||||||
idStart = 256 ##
|
|
||||||
## Ids do not start with 0 but with this value. The IR needs it.
|
|
||||||
## TODO: explain why
|
|
||||||
|
|
||||||
template idToIdx(x: LitId): int = x.int - idStart
|
|
||||||
|
|
||||||
proc hasLitId*[T](t: BiTable[T]; x: LitId): bool =
|
|
||||||
let idx = idToIdx(x)
|
|
||||||
result = idx >= 0 and idx < t.vals.len
|
|
||||||
|
|
||||||
proc enlarge[T](t: var BiTable[T]) =
|
|
||||||
var n: seq[LitId]
|
|
||||||
newSeq(n, len(t.keys) * 2)
|
|
||||||
swap(t.keys, n)
|
|
||||||
for i in 0..high(n):
|
|
||||||
let eh = n[i]
|
|
||||||
if isFilled(eh):
|
|
||||||
var j = hash(t.vals[idToIdx eh]) and maxHash(t)
|
|
||||||
while isFilled(t.keys[j]):
|
|
||||||
j = nextTry(j, maxHash(t))
|
|
||||||
t.keys[j] = move n[i]
|
|
||||||
|
|
||||||
proc getKeyId*[T](t: BiTable[T]; v: T): LitId =
|
|
||||||
let origH = hash(v)
|
|
||||||
var h = origH and maxHash(t)
|
|
||||||
if t.keys.len != 0:
|
|
||||||
while true:
|
|
||||||
let litId = t.keys[h]
|
|
||||||
if not isFilled(litId): break
|
|
||||||
if t.vals[idToIdx t.keys[h]] == v: return litId
|
|
||||||
h = nextTry(h, maxHash(t))
|
|
||||||
return LitId(0)
|
|
||||||
|
|
||||||
proc getOrIncl*[T](t: var BiTable[T]; v: T): LitId =
|
|
||||||
let origH = hash(v)
|
|
||||||
var h = origH and maxHash(t)
|
|
||||||
if t.keys.len != 0:
|
|
||||||
while true:
|
|
||||||
let litId = t.keys[h]
|
|
||||||
if not isFilled(litId): break
|
|
||||||
if t.vals[idToIdx t.keys[h]] == v: return litId
|
|
||||||
h = nextTry(h, maxHash(t))
|
|
||||||
# not found, we need to insert it:
|
|
||||||
if mustRehash(t.keys.len, t.vals.len):
|
|
||||||
enlarge(t)
|
|
||||||
# recompute where to insert:
|
|
||||||
h = origH and maxHash(t)
|
|
||||||
while true:
|
|
||||||
let litId = t.keys[h]
|
|
||||||
if not isFilled(litId): break
|
|
||||||
h = nextTry(h, maxHash(t))
|
|
||||||
else:
|
|
||||||
setLen(t.keys, 16)
|
|
||||||
h = origH and maxHash(t)
|
|
||||||
|
|
||||||
result = LitId(t.vals.len + idStart)
|
|
||||||
t.keys[h] = result
|
|
||||||
t.vals.add v
|
|
||||||
|
|
||||||
|
|
||||||
proc `[]`*[T](t: var BiTable[T]; litId: LitId): var T {.inline.} =
|
|
||||||
let idx = idToIdx litId
|
|
||||||
assert idx < t.vals.len
|
|
||||||
result = t.vals[idx]
|
|
||||||
|
|
||||||
proc `[]`*[T](t: BiTable[T]; litId: LitId): lent T {.inline.} =
|
|
||||||
let idx = idToIdx litId
|
|
||||||
assert idx < t.vals.len
|
|
||||||
result = t.vals[idx]
|
|
||||||
|
|
||||||
proc hash*[T](t: BiTable[T]): Hash =
|
|
||||||
## as the keys are hashes of the values, we simply use them instead
|
|
||||||
var h: Hash = 0
|
|
||||||
for i, n in pairs t.keys:
|
|
||||||
h = h !& hash((i, n))
|
|
||||||
result = !$h
|
|
||||||
|
|
||||||
proc store*[T](f: var RodFile; t: BiTable[T]) =
|
|
||||||
storeSeq(f, t.vals)
|
|
||||||
storeSeq(f, t.keys)
|
|
||||||
|
|
||||||
proc load*[T](f: var RodFile; t: var BiTable[T]) =
|
|
||||||
loadSeq(f, t.vals)
|
|
||||||
loadSeq(f, t.keys)
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
|
|
||||||
var t: BiTable[string]
|
|
||||||
|
|
||||||
echo getOrIncl(t, "hello")
|
|
||||||
|
|
||||||
echo getOrIncl(t, "hello")
|
|
||||||
echo getOrIncl(t, "hello3")
|
|
||||||
echo getOrIncl(t, "hello4")
|
|
||||||
echo getOrIncl(t, "helloasfasdfdsa")
|
|
||||||
echo getOrIncl(t, "hello")
|
|
||||||
echo getKeyId(t, "hello")
|
|
||||||
echo getKeyId(t, "none")
|
|
||||||
|
|
||||||
for i in 0 ..< 100_000:
|
|
||||||
discard t.getOrIncl($i & "___" & $i)
|
|
||||||
|
|
||||||
for i in 0 ..< 100_000:
|
|
||||||
assert t.getOrIncl($i & "___" & $i).idToIdx == i + 4
|
|
||||||
echo "begin"
|
|
||||||
echo t.vals.len
|
|
||||||
|
|
||||||
echo t.vals[0]
|
|
||||||
echo t.vals[1004]
|
|
||||||
|
|
||||||
echo "middle"
|
|
||||||
|
|
||||||
var tf: BiTable[float]
|
|
||||||
|
|
||||||
discard tf.getOrIncl(0.4)
|
|
||||||
discard tf.getOrIncl(16.4)
|
|
||||||
discard tf.getOrIncl(32.4)
|
|
||||||
echo getKeyId(tf, 32.4)
|
|
||||||
|
|
||||||
var f2 = open("testblah.bin", fmWrite)
|
|
||||||
echo store(f2, tf)
|
|
||||||
f2.close
|
|
||||||
|
|
||||||
var f1 = open("testblah.bin", fmRead)
|
|
||||||
|
|
||||||
var t2: BiTable[float]
|
|
||||||
|
|
||||||
echo f1.load(t2)
|
|
||||||
echo t2.vals.len
|
|
||||||
|
|
||||||
echo getKeyId(t2, 32.4)
|
|
||||||
|
|
||||||
echo "end"
|
|
||||||
|
|
||||||
|
|
||||||
f1.close
|
|
||||||
|
|
@ -1,178 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2021 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## New entry point into our C/C++ code generator. Ideally
|
|
||||||
## somebody would rewrite the old backend (which is 8000 lines of crufty Nim code)
|
|
||||||
## to work on packed trees directly and produce the C code as an AST which can
|
|
||||||
## then be rendered to text in a very simple manner. Unfortunately nobody wrote
|
|
||||||
## this code. So instead we wrap the existing cgen.nim and its friends so that
|
|
||||||
## we call directly into the existing code generation logic but avoiding the
|
|
||||||
## naive, outdated `passes` design. Thus you will see some
|
|
||||||
## `useAliveDataFromDce in flags` checks in the old code -- the old code is
|
|
||||||
## also doing cross-module dependency tracking and DCE that we don't need
|
|
||||||
## anymore. DCE is now done as prepass over the entire packed module graph.
|
|
||||||
|
|
||||||
import std/packedsets, algorithm, tables
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
|
|
||||||
pathutils, extccomp, msgs, modulepaths]
|
|
||||||
|
|
||||||
import packed_ast, ic, dce, rodfiles
|
|
||||||
|
|
||||||
proc unpackTree(g: ModuleGraph; thisModule: int;
|
|
||||||
tree: PackedTree; n: NodePos): PNode =
|
|
||||||
var decoder = initPackedDecoder(g.config, g.cache)
|
|
||||||
result = loadNodes(decoder, g.packed, thisModule, tree, n)
|
|
||||||
|
|
||||||
proc setupBackendModule(g: ModuleGraph; m: var LoadedModule) =
|
|
||||||
if g.backend == nil:
|
|
||||||
g.backend = cgendata.newModuleList(g)
|
|
||||||
assert g.backend != nil
|
|
||||||
var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config)
|
|
||||||
bmod.idgen = idgenFromLoadedModule(m)
|
|
||||||
|
|
||||||
proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) =
|
|
||||||
var bmod = BModuleList(g.backend).modules[m.module.position]
|
|
||||||
assert bmod != nil
|
|
||||||
bmod.flags.incl useAliveDataFromDce
|
|
||||||
bmod.alive = move alive[m.module.position]
|
|
||||||
|
|
||||||
for p in allNodes(m.fromDisk.topLevel):
|
|
||||||
let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
|
|
||||||
cgen.genTopLevelStmt(bmod, n)
|
|
||||||
|
|
||||||
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
|
|
||||||
m.fromDisk.backendFlags = cgen.whichInitProcs(bmod)
|
|
||||||
|
|
||||||
proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
|
|
||||||
for x in mitems(m.fromDisk.emittedTypeInfo):
|
|
||||||
#echo "found type ", x, " for file ", int(origin)
|
|
||||||
g.emittedTypeInfo[x] = origin
|
|
||||||
|
|
||||||
proc addFileToLink(config: ConfigRef; m: PSym) =
|
|
||||||
let filename = AbsoluteFile toFullPath(config, m.position.FileIndex)
|
|
||||||
let ext =
|
|
||||||
if config.backend == backendCpp: ".nim.cpp"
|
|
||||||
elif config.backend == backendObjc: ".nim.m"
|
|
||||||
else: ".nim.c"
|
|
||||||
let cfile = changeFileExt(completeCfilePath(config,
|
|
||||||
mangleModuleName(config, filename).AbsoluteFile), ext)
|
|
||||||
let objFile = completeCfilePath(config, toObjFile(config, cfile))
|
|
||||||
if fileExists(objFile):
|
|
||||||
var cf = Cfile(nimname: m.name.s, cname: cfile,
|
|
||||||
obj: objFile,
|
|
||||||
flags: {CfileFlag.Cached})
|
|
||||||
addFileToCompile(config, cf)
|
|
||||||
|
|
||||||
when defined(debugDce):
|
|
||||||
import os, std/packedsets
|
|
||||||
|
|
||||||
proc storeAliveSymsImpl(asymFile: AbsoluteFile; s: seq[int32]) =
|
|
||||||
var f = rodfiles.create(asymFile.string)
|
|
||||||
f.storeHeader()
|
|
||||||
f.storeSection aliveSymsSection
|
|
||||||
f.storeSeq(s)
|
|
||||||
close f
|
|
||||||
|
|
||||||
template prepare {.dirty.} =
|
|
||||||
let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms")
|
|
||||||
var s = newSeqOfCap[int32](alive[position].len)
|
|
||||||
for a in items(alive[position]): s.add int32(a)
|
|
||||||
sort(s)
|
|
||||||
|
|
||||||
proc storeAliveSyms(config: ConfigRef; position: int; alive: AliveSyms) =
|
|
||||||
prepare()
|
|
||||||
storeAliveSymsImpl(asymFile, s)
|
|
||||||
|
|
||||||
proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool =
|
|
||||||
prepare()
|
|
||||||
var f2 = rodfiles.open(asymFile.string)
|
|
||||||
f2.loadHeader()
|
|
||||||
f2.loadSection aliveSymsSection
|
|
||||||
var oldData: seq[int32]
|
|
||||||
f2.loadSeq(oldData)
|
|
||||||
f2.close
|
|
||||||
if f2.err == ok and oldData == s:
|
|
||||||
result = false
|
|
||||||
else:
|
|
||||||
when defined(debugDce):
|
|
||||||
let oldAsSet = toPackedSet[int32](oldData)
|
|
||||||
let newAsSet = toPackedSet[int32](s)
|
|
||||||
echo "set of live symbols changed ", asymFile.changeFileExt("rod"), " ", position, " ", f2.err
|
|
||||||
echo "in old but not in new ", oldAsSet.difference(newAsSet), " number of entries in old ", oldAsSet.len
|
|
||||||
echo "in new but not in old ", newAsSet.difference(oldAsSet), " number of entries in new ", newAsSet.len
|
|
||||||
#if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0:
|
|
||||||
# echo "command failed"
|
|
||||||
result = true
|
|
||||||
storeAliveSymsImpl(asymFile, s)
|
|
||||||
|
|
||||||
proc genPackedModule(g: ModuleGraph, i: int; alive: var AliveSyms) =
|
|
||||||
# case statement here to enforce exhaustive checks.
|
|
||||||
case g.packed[i].status
|
|
||||||
of undefined:
|
|
||||||
discard "nothing to do"
|
|
||||||
of loading, stored:
|
|
||||||
assert false
|
|
||||||
of storing, outdated:
|
|
||||||
storeAliveSyms(g.config, g.packed[i].module.position, alive)
|
|
||||||
generateCodeForModule(g, g.packed[i], alive)
|
|
||||||
closeRodFile(g, g.packed[i].module)
|
|
||||||
of loaded:
|
|
||||||
if g.packed[i].loadedButAliveSetChanged:
|
|
||||||
generateCodeForModule(g, g.packed[i], alive)
|
|
||||||
else:
|
|
||||||
addFileToLink(g.config, g.packed[i].module)
|
|
||||||
replayTypeInfo(g, g.packed[i], FileIndex(i))
|
|
||||||
|
|
||||||
if g.backend == nil:
|
|
||||||
g.backend = cgendata.newModuleList(g)
|
|
||||||
registerInitProcs(BModuleList(g.backend), g.packed[i].module, g.packed[i].fromDisk.backendFlags)
|
|
||||||
|
|
||||||
proc generateCode*(g: ModuleGraph) =
|
|
||||||
## The single entry point, generate C(++) code for the entire
|
|
||||||
## Nim program aka `ModuleGraph`.
|
|
||||||
resetForBackend(g)
|
|
||||||
var alive = computeAliveSyms(g.packed, g.config)
|
|
||||||
|
|
||||||
when false:
|
|
||||||
for i in 0..high(g.packed):
|
|
||||||
echo i, " is of status ", g.packed[i].status, " ", toFullPath(g.config, FileIndex(i))
|
|
||||||
|
|
||||||
# First pass: Setup all the backend modules for all the modules that have
|
|
||||||
# changed:
|
|
||||||
for i in 0..high(g.packed):
|
|
||||||
# case statement here to enforce exhaustive checks.
|
|
||||||
case g.packed[i].status
|
|
||||||
of undefined:
|
|
||||||
discard "nothing to do"
|
|
||||||
of loading, stored:
|
|
||||||
assert false
|
|
||||||
of storing, outdated:
|
|
||||||
setupBackendModule(g, g.packed[i])
|
|
||||||
of loaded:
|
|
||||||
# Even though this module didn't change, DCE might trigger a change.
|
|
||||||
# Consider this case: Module A uses symbol S from B and B does not use
|
|
||||||
# S itself. A is then edited not to use S either. Thus we have to
|
|
||||||
# recompile B in order to remove S from the final result.
|
|
||||||
if aliveSymsChanged(g.config, g.packed[i].module.position, alive):
|
|
||||||
g.packed[i].loadedButAliveSetChanged = true
|
|
||||||
setupBackendModule(g, g.packed[i])
|
|
||||||
|
|
||||||
# Second pass: Code generation.
|
|
||||||
let mainModuleIdx = g.config.projectMainIdx2.int
|
|
||||||
# We need to generate the main module last, because only then
|
|
||||||
# all init procs have been registered:
|
|
||||||
for i in 0..high(g.packed):
|
|
||||||
if i != mainModuleIdx:
|
|
||||||
genPackedModule(g, i, alive)
|
|
||||||
if mainModuleIdx >= 0:
|
|
||||||
genPackedModule(g, mainModuleIdx, alive)
|
|
||||||
|
|
@ -1,165 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2021 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Dead code elimination (=DCE) for IC.
|
|
||||||
|
|
||||||
import std/[intsets, tables]
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
import ".." / [ast, options, lineinfos, types]
|
|
||||||
|
|
||||||
import packed_ast, ic, bitabs
|
|
||||||
|
|
||||||
type
|
|
||||||
AliveSyms* = seq[IntSet]
|
|
||||||
AliveContext* = object ## Purpose is to fill the 'alive' field.
|
|
||||||
stack: seq[(int, TOptions, NodePos)] ## A stack for marking symbols as alive.
|
|
||||||
decoder: PackedDecoder ## We need a PackedDecoder for module ID address translations.
|
|
||||||
thisModule: int ## The module we're currently analysing for DCE.
|
|
||||||
alive: AliveSyms ## The final result of our computation.
|
|
||||||
options: TOptions
|
|
||||||
compilerProcs: Table[string, (int, int32)]
|
|
||||||
|
|
||||||
proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): bool =
|
|
||||||
## "Exported to C" procs are special (these are marked with '.exportc') because these
|
|
||||||
## must not be optimized away!
|
|
||||||
let symPtr = unsafeAddr g[c.thisModule].fromDisk.syms[symId]
|
|
||||||
let flags = symPtr.flags
|
|
||||||
# due to a bug/limitation in the lambda lifting, unused inner procs
|
|
||||||
# are not transformed correctly; issue (#411). However, the whole purpose here
|
|
||||||
# is to eliminate unused procs. So there is no special logic required for this case.
|
|
||||||
if sfCompileTime notin flags:
|
|
||||||
if ({sfExportc, sfCompilerProc} * flags != {}) or
|
|
||||||
(symPtr.kind == skMethod):
|
|
||||||
result = true
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty
|
|
||||||
|
|
||||||
proc followLater(c: var AliveContext; g: PackedModuleGraph; module: int; item: int32) =
|
|
||||||
## Marks a symbol 'item' as used and later in 'followNow' the symbol's body will
|
|
||||||
## be analysed.
|
|
||||||
if not c.alive[module].containsOrIncl(item):
|
|
||||||
var body = g[module].fromDisk.syms[item].ast
|
|
||||||
if body != emptyNodeId:
|
|
||||||
let opt = g[module].fromDisk.syms[item].options
|
|
||||||
if g[module].fromDisk.syms[item].kind in routineKinds:
|
|
||||||
body = NodeId ithSon(g[module].fromDisk.bodies, NodePos body, bodyPos)
|
|
||||||
c.stack.add((module, opt, NodePos(body)))
|
|
||||||
|
|
||||||
when false:
|
|
||||||
let nid = g[module].fromDisk.syms[item].name
|
|
||||||
if nid != LitId(0):
|
|
||||||
let name = g[module].fromDisk.strings[nid]
|
|
||||||
if name in ["nimFrame", "callDepthLimitReached"]:
|
|
||||||
echo "I was called! ", name, " body exists: ", body != emptyNodeId, " ", module, " ", item
|
|
||||||
|
|
||||||
proc requestCompilerProc(c: var AliveContext; g: PackedModuleGraph; name: string) =
|
|
||||||
let (module, item) = c.compilerProcs[name]
|
|
||||||
followLater(c, g, module, item)
|
|
||||||
|
|
||||||
proc loadTypeKind(t: PackedItemId; c: AliveContext; g: PackedModuleGraph; toSkip: set[TTypeKind]): TTypeKind =
|
|
||||||
template kind(t: ItemId): TTypeKind = g[t.module].fromDisk.types[t.item].kind
|
|
||||||
|
|
||||||
var t2 = translateId(t, g, c.thisModule, c.decoder.config)
|
|
||||||
result = t2.kind
|
|
||||||
while result in toSkip:
|
|
||||||
t2 = translateId(g[t2.module].fromDisk.types[t2.item].types[^1], g, t2.module, c.decoder.config)
|
|
||||||
result = t2.kind
|
|
||||||
|
|
||||||
proc rangeCheckAnalysis(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
|
|
||||||
## Replicates the logic of `ccgexprs.genRangeChck`.
|
|
||||||
## XXX Refactor so that the duplicated logic is avoided. However, for now it's not clear
|
|
||||||
## the approach has enough merit.
|
|
||||||
var dest = loadTypeKind(n.typ, c, g, abstractVar)
|
|
||||||
if optRangeCheck notin c.options or dest in {tyUInt..tyUInt64}:
|
|
||||||
discard "no need to generate a check because it was disabled"
|
|
||||||
else:
|
|
||||||
let n0t = loadTypeKind(n.firstSon.typ, c, g, {})
|
|
||||||
if n0t in {tyUInt, tyUInt64}:
|
|
||||||
c.requestCompilerProc(g, "raiseRangeErrorNoArgs")
|
|
||||||
else:
|
|
||||||
let raiser =
|
|
||||||
case loadTypeKind(n.typ, c, g, abstractVarRange)
|
|
||||||
of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU"
|
|
||||||
of tyFloat..tyFloat128: "raiseRangeErrorF"
|
|
||||||
else: "raiseRangeErrorI"
|
|
||||||
c.requestCompilerProc(g, raiser)
|
|
||||||
|
|
||||||
proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) =
|
|
||||||
## Marks the symbols we encounter when we traverse the AST at `tree[n]` as alive, unless
|
|
||||||
## it is purely in a declarative context (type section etc.).
|
|
||||||
case n.kind
|
|
||||||
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
|
|
||||||
discard "ignore non-sym atoms"
|
|
||||||
of nkSym:
|
|
||||||
# This symbol is alive and everything its body references.
|
|
||||||
followLater(c, g, c.thisModule, n.operand)
|
|
||||||
of nkModuleRef:
|
|
||||||
let (n1, n2) = sons2(tree, n)
|
|
||||||
assert n1.kind == nkInt32Lit
|
|
||||||
assert n2.kind == nkInt32Lit
|
|
||||||
let m = n1.litId
|
|
||||||
let item = n2.operand
|
|
||||||
let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int
|
|
||||||
followLater(c, g, otherModule, item)
|
|
||||||
of nkMacroDef, nkTemplateDef, nkTypeSection, nkTypeOfExpr,
|
|
||||||
nkCommentStmt, nkIncludeStmt,
|
|
||||||
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
|
|
||||||
nkFromStmt, nkStaticStmt:
|
|
||||||
discard
|
|
||||||
of nkVarSection, nkLetSection, nkConstSection:
|
|
||||||
# XXX ignore the defining local variable name?
|
|
||||||
for son in sonsReadonly(tree, n):
|
|
||||||
aliveCode(c, g, tree, son)
|
|
||||||
of nkChckRangeF, nkChckRange64, nkChckRange:
|
|
||||||
rangeCheckAnalysis(c, g, tree, n)
|
|
||||||
of nkProcDef, nkConverterDef, nkMethodDef, nkFuncDef, nkIteratorDef:
|
|
||||||
if n.firstSon.kind == nkSym and isNotGeneric(n):
|
|
||||||
let item = n.firstSon.operand
|
|
||||||
if isExportedToC(c, g, item):
|
|
||||||
# This symbol is alive and everything its body references.
|
|
||||||
followLater(c, g, c.thisModule, item)
|
|
||||||
else:
|
|
||||||
for son in sonsReadonly(tree, n):
|
|
||||||
aliveCode(c, g, tree, son)
|
|
||||||
|
|
||||||
proc followNow(c: var AliveContext; g: PackedModuleGraph) =
|
|
||||||
## Mark all entries in the stack. Marking can add more entries
|
|
||||||
## to the stack but eventually we have looked at every alive symbol.
|
|
||||||
while c.stack.len > 0:
|
|
||||||
let (modId, opt, ast) = c.stack.pop()
|
|
||||||
c.thisModule = modId
|
|
||||||
c.options = opt
|
|
||||||
aliveCode(c, g, g[modId].fromDisk.bodies, ast)
|
|
||||||
|
|
||||||
proc computeAliveSyms*(g: PackedModuleGraph; conf: ConfigRef): AliveSyms =
|
|
||||||
## Entry point for our DCE algorithm.
|
|
||||||
var c = AliveContext(stack: @[], decoder: PackedDecoder(config: conf),
|
|
||||||
thisModule: -1, alive: newSeq[IntSet](g.len),
|
|
||||||
options: conf.options)
|
|
||||||
for i in countdown(high(g), 0):
|
|
||||||
if g[i].status != undefined:
|
|
||||||
c.thisModule = i
|
|
||||||
for p in allNodes(g[i].fromDisk.topLevel):
|
|
||||||
aliveCode(c, g, g[i].fromDisk.topLevel, p)
|
|
||||||
|
|
||||||
followNow(c, g)
|
|
||||||
result = move(c.alive)
|
|
||||||
|
|
||||||
proc isAlive*(a: AliveSyms; module: int, item: int32): bool =
|
|
||||||
## Backends use this to query if a symbol is `alive` which means
|
|
||||||
## we need to produce (C/C++/etc) code for it.
|
|
||||||
result = a[module].contains(item)
|
|
||||||
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
====================================
|
|
||||||
Incremental Recompilations
|
|
||||||
====================================
|
|
||||||
|
|
||||||
We split the Nim compiler into a frontend and a backend.
|
|
||||||
The frontend produces a set of `.rod` files. Every `.nim` module
|
|
||||||
produces its own `.rod` file.
|
|
||||||
|
|
||||||
- The IR must be a faithful representation of the AST in memory.
|
|
||||||
- The backend can do its own caching but doesn't have to. In the
|
|
||||||
current implementation the backend also caches its results.
|
|
||||||
|
|
||||||
Advantage of the "set of files" vs the previous global database:
|
|
||||||
- By construction, we either read from the `.rod` file or from the
|
|
||||||
`.nim` file, there can be no inconsistency. There can also be no
|
|
||||||
partial updates.
|
|
||||||
- No dependency to external packages (SQLite). SQLite simply is too
|
|
||||||
slow and the old way of serialization was too slow too. We use a
|
|
||||||
format designed for Nim and expect to base further tools on this
|
|
||||||
file format.
|
|
||||||
|
|
||||||
References to external modules must be (moduleId, symId) pairs.
|
|
||||||
The symbol IDs are module specific. This way no global ID increment
|
|
||||||
mechanism needs to be implemented that we could get wrong. ModuleIds
|
|
||||||
are rod-file specific too.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Global state
|
|
||||||
------------
|
|
||||||
|
|
||||||
There is no global state.
|
|
||||||
|
|
||||||
Rod File Format
|
|
||||||
---------------
|
|
||||||
|
|
||||||
It's a simple binary file format. `rodfiles.nim` contains some details.
|
|
||||||
|
|
||||||
|
|
||||||
Backend
|
|
||||||
-------
|
|
||||||
|
|
||||||
Nim programmers have to come to enjoy whole-program dead code elimination,
|
|
||||||
by default. Since this is a "whole program" optimization, it does break
|
|
||||||
modularity. However, thanks to the packed AST representation we can perform
|
|
||||||
this global analysis without having to unpack anything. This is basically
|
|
||||||
a mark&sweep GC algorithm:
|
|
||||||
|
|
||||||
- Start with the top level statements. Every symbol that is referenced
|
|
||||||
from a top level statement is not "dead" and needs to be compiled by
|
|
||||||
the backend.
|
|
||||||
- Every symbol referenced from a referenced symbol also has to be
|
|
||||||
compiled.
|
|
||||||
|
|
||||||
Caching logic: Only if the set of alive symbols is different from the
|
|
||||||
last run, the module has to be regenerated.
|
|
||||||
1231
compiler/ic/ic.nim
1231
compiler/ic/ic.nim
File diff suppressed because it is too large
Load diff
|
|
@ -1,153 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2021 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Integrity checking for a set of .rod files.
|
|
||||||
## The set must cover a complete Nim project.
|
|
||||||
|
|
||||||
import sets
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
import ".." / [ast, modulegraphs]
|
|
||||||
import packed_ast, bitabs, ic
|
|
||||||
|
|
||||||
type
|
|
||||||
CheckedContext = object
|
|
||||||
g: ModuleGraph
|
|
||||||
thisModule: int32
|
|
||||||
checkedSyms: HashSet[ItemId]
|
|
||||||
checkedTypes: HashSet[ItemId]
|
|
||||||
|
|
||||||
proc checkType(c: var CheckedContext; typeId: PackedItemId)
|
|
||||||
proc checkForeignSym(c: var CheckedContext; symId: PackedItemId)
|
|
||||||
proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos)
|
|
||||||
|
|
||||||
proc checkTypeObj(c: var CheckedContext; typ: PackedType) =
|
|
||||||
for child in typ.types:
|
|
||||||
checkType(c, child)
|
|
||||||
if typ.n != emptyNodeId:
|
|
||||||
checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos typ.n)
|
|
||||||
if typ.sym != nilItemId:
|
|
||||||
checkForeignSym(c, typ.sym)
|
|
||||||
if typ.owner != nilItemId:
|
|
||||||
checkForeignSym(c, typ.owner)
|
|
||||||
checkType(c, typ.typeInst)
|
|
||||||
|
|
||||||
proc checkType(c: var CheckedContext; typeId: PackedItemId) =
|
|
||||||
if typeId == nilItemId: return
|
|
||||||
let itemId = translateId(typeId, c.g.packed, c.thisModule, c.g.config)
|
|
||||||
if not c.checkedTypes.containsOrIncl(itemId):
|
|
||||||
let oldThisModule = c.thisModule
|
|
||||||
c.thisModule = itemId.module
|
|
||||||
checkTypeObj c, c.g.packed[itemId.module].fromDisk.types[itemId.item]
|
|
||||||
c.thisModule = oldThisModule
|
|
||||||
|
|
||||||
proc checkSym(c: var CheckedContext; s: PackedSym) =
|
|
||||||
if s.name != LitId(0):
|
|
||||||
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId s.name
|
|
||||||
checkType c, s.typ
|
|
||||||
if s.ast != emptyNodeId:
|
|
||||||
checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos s.ast)
|
|
||||||
if s.owner != nilItemId:
|
|
||||||
checkForeignSym(c, s.owner)
|
|
||||||
|
|
||||||
proc checkLocalSym(c: var CheckedContext; item: int32) =
|
|
||||||
let itemId = ItemId(module: c.thisModule, item: item)
|
|
||||||
if not c.checkedSyms.containsOrIncl(itemId):
|
|
||||||
checkSym c, c.g.packed[c.thisModule].fromDisk.syms[item]
|
|
||||||
|
|
||||||
proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) =
|
|
||||||
let itemId = translateId(symId, c.g.packed, c.thisModule, c.g.config)
|
|
||||||
if not c.checkedSyms.containsOrIncl(itemId):
|
|
||||||
let oldThisModule = c.thisModule
|
|
||||||
c.thisModule = itemId.module
|
|
||||||
checkSym c, c.g.packed[itemId.module].fromDisk.syms[itemId.item]
|
|
||||||
c.thisModule = oldThisModule
|
|
||||||
|
|
||||||
proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
|
|
||||||
if tree[n.int].typeId != nilItemId:
|
|
||||||
checkType(c, tree[n.int].typeId)
|
|
||||||
case n.kind
|
|
||||||
of nkEmpty, nkNilLit, nkType, nkNilRodNode:
|
|
||||||
discard
|
|
||||||
of nkIdent:
|
|
||||||
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
|
|
||||||
of nkSym:
|
|
||||||
checkLocalSym(c, tree.nodes[n.int].operand)
|
|
||||||
of directIntLit:
|
|
||||||
discard
|
|
||||||
of externIntLit, nkFloatLit..nkFloat128Lit:
|
|
||||||
assert c.g.packed[c.thisModule].fromDisk.numbers.hasLitId n.litId
|
|
||||||
of nkStrLit..nkTripleStrLit:
|
|
||||||
assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
|
|
||||||
of nkModuleRef:
|
|
||||||
let (n1, n2) = sons2(tree, n)
|
|
||||||
assert n1.kind == nkInt32Lit
|
|
||||||
assert n2.kind == nkInt32Lit
|
|
||||||
checkForeignSym(c, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
|
|
||||||
else:
|
|
||||||
for n0 in sonsReadonly(tree, n):
|
|
||||||
checkNode(c, tree, n0)
|
|
||||||
|
|
||||||
proc checkTree(c: var CheckedContext; t: PackedTree) =
|
|
||||||
for p in allNodes(t): checkNode(c, t, p)
|
|
||||||
|
|
||||||
proc checkLocalSymIds(c: var CheckedContext; m: PackedModule; symIds: seq[int32]) =
|
|
||||||
for symId in symIds:
|
|
||||||
assert symId >= 0 and symId < m.syms.len, $symId & " " & $m.syms.len
|
|
||||||
|
|
||||||
proc checkModule(c: var CheckedContext; m: PackedModule) =
|
|
||||||
# We check that:
|
|
||||||
# - Every symbol references existing types and symbols.
|
|
||||||
# - Every tree node references existing types and symbols.
|
|
||||||
for i in 0..high(m.syms):
|
|
||||||
checkLocalSym c, int32(i)
|
|
||||||
|
|
||||||
checkTree c, m.toReplay
|
|
||||||
checkTree c, m.topLevel
|
|
||||||
|
|
||||||
for e in m.exports:
|
|
||||||
assert e[1] >= 0 and e[1] < m.syms.len
|
|
||||||
assert e[0] == m.syms[e[1]].name
|
|
||||||
|
|
||||||
for e in m.compilerProcs:
|
|
||||||
assert e[1] >= 0 and e[1] < m.syms.len
|
|
||||||
assert e[0] == m.syms[e[1]].name
|
|
||||||
|
|
||||||
checkLocalSymIds c, m, m.converters
|
|
||||||
checkLocalSymIds c, m, m.methods
|
|
||||||
checkLocalSymIds c, m, m.trmacros
|
|
||||||
checkLocalSymIds c, m, m.pureEnums
|
|
||||||
#[
|
|
||||||
To do: Check all these fields:
|
|
||||||
|
|
||||||
reexports*: seq[(LitId, PackedItemId)]
|
|
||||||
macroUsages*: seq[(PackedItemId, PackedLineInfo)]
|
|
||||||
|
|
||||||
typeInstCache*: seq[(PackedItemId, PackedItemId)]
|
|
||||||
procInstCache*: seq[PackedInstantiation]
|
|
||||||
attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)]
|
|
||||||
methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
|
|
||||||
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
|
|
||||||
]#
|
|
||||||
|
|
||||||
proc checkIntegrity*(g: ModuleGraph) =
|
|
||||||
var c = CheckedContext(g: g)
|
|
||||||
for i in 0..high(g.packed):
|
|
||||||
# case statement here to enforce exhaustive checks.
|
|
||||||
case g.packed[i].status
|
|
||||||
of undefined:
|
|
||||||
discard "nothing to do"
|
|
||||||
of loading:
|
|
||||||
assert false, "cannot check integrity: Module still loading"
|
|
||||||
of stored, storing, outdated, loaded:
|
|
||||||
c.thisModule = int32 i
|
|
||||||
checkModule(c, g.packed[i].fromDisk)
|
|
||||||
|
|
||||||
|
|
@ -1,170 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2021 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Supports the "nim check --ic:on --defusages:FILE,LINE,COL"
|
|
||||||
## IDE-like features. It uses the set of .rod files to accomplish
|
|
||||||
## its task. The set must cover a complete Nim project.
|
|
||||||
|
|
||||||
import sets
|
|
||||||
|
|
||||||
from os import nil
|
|
||||||
from std/private/miscdollars import toLocation
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
import ".." / [ast, modulegraphs, msgs, options]
|
|
||||||
import packed_ast, bitabs, ic
|
|
||||||
|
|
||||||
type
|
|
||||||
NavContext = object
|
|
||||||
g: ModuleGraph
|
|
||||||
thisModule: int32
|
|
||||||
trackPos: PackedLineInfo
|
|
||||||
alreadyEmitted: HashSet[string]
|
|
||||||
outputSep: char # for easier testing, use short filenames and spaces instead of tabs.
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool =
|
|
||||||
result = s.name != LitId(0) and
|
|
||||||
isTracked(info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len)
|
|
||||||
|
|
||||||
proc searchForeignSym(c: var NavContext; s: ItemId; info: PackedLineInfo): bool =
|
|
||||||
let name = c.g.packed[s.module].fromDisk.syms[s.item].name
|
|
||||||
result = name != LitId(0) and
|
|
||||||
isTracked(info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len)
|
|
||||||
|
|
||||||
const
|
|
||||||
EmptyItemId = ItemId(module: -1'i32, item: -1'i32)
|
|
||||||
|
|
||||||
proc search(c: var NavContext; tree: PackedTree): ItemId =
|
|
||||||
# We use the linear representation here directly:
|
|
||||||
for i in 0..high(tree.nodes):
|
|
||||||
case tree.nodes[i].kind
|
|
||||||
of nkSym:
|
|
||||||
let item = tree.nodes[i].operand
|
|
||||||
if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree.nodes[i].info):
|
|
||||||
return ItemId(module: c.thisModule, item: item)
|
|
||||||
of nkModuleRef:
|
|
||||||
if tree.nodes[i].info.line == c.trackPos.line and tree.nodes[i].info.file == c.trackPos.file:
|
|
||||||
let (n1, n2) = sons2(tree, NodePos i)
|
|
||||||
assert n1.kind == nkInt32Lit
|
|
||||||
assert n2.kind == nkInt32Lit
|
|
||||||
let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)
|
|
||||||
let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
|
|
||||||
if searchForeignSym(c, itemId, tree.nodes[i].info):
|
|
||||||
return itemId
|
|
||||||
else: discard
|
|
||||||
return EmptyItemId
|
|
||||||
|
|
||||||
proc isDecl(tree: PackedTree; n: NodePos): bool =
|
|
||||||
# XXX This is not correct yet.
|
|
||||||
const declarativeNodes = procDefs + {nkMacroDef, nkTemplateDef,
|
|
||||||
nkLetSection, nkVarSection, nkUsingStmt, nkConstSection, nkTypeSection,
|
|
||||||
nkIdentDefs, nkEnumTy, nkVarTuple}
|
|
||||||
result = n.int >= 0 and tree[n.int].kind in declarativeNodes
|
|
||||||
|
|
||||||
proc usage(c: var NavContext; info: PackedLineInfo; isDecl: bool) =
|
|
||||||
var m = ""
|
|
||||||
var file = c.g.packed[c.thisModule].fromDisk.strings[info.file]
|
|
||||||
if c.outputSep == ' ':
|
|
||||||
file = os.extractFilename file
|
|
||||||
toLocation(m, file, info.line.int, info.col.int + ColOffset)
|
|
||||||
if not c.alreadyEmitted.containsOrIncl(m):
|
|
||||||
msgWriteln c.g.config, (if isDecl: "def" else: "usage") & c.outputSep & m
|
|
||||||
|
|
||||||
proc list(c: var NavContext; tree: PackedTree; sym: ItemId) =
|
|
||||||
for i in 0..high(tree.nodes):
|
|
||||||
case tree.nodes[i].kind
|
|
||||||
of nkSym:
|
|
||||||
let item = tree.nodes[i].operand
|
|
||||||
if sym.item == item and sym.module == c.thisModule:
|
|
||||||
usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i)))
|
|
||||||
of nkModuleRef:
|
|
||||||
let (n1, n2) = sons2(tree, NodePos i)
|
|
||||||
assert n1.kind == nkInt32Lit
|
|
||||||
assert n2.kind == nkInt32Lit
|
|
||||||
let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)
|
|
||||||
let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
|
|
||||||
if itemId.item == sym.item and sym.module == itemId.module:
|
|
||||||
usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i)))
|
|
||||||
else: discard
|
|
||||||
|
|
||||||
proc searchForIncludeFile(g: ModuleGraph; fullPath: string): int =
|
|
||||||
for i in 0..high(g.packed):
|
|
||||||
for k in 1..high(g.packed[i].fromDisk.includes):
|
|
||||||
# we start from 1 because the first "include" file is
|
|
||||||
# the module's filename.
|
|
||||||
if os.cmpPaths(g.packed[i].fromDisk.strings[g.packed[i].fromDisk.includes[k][0]], fullPath) == 0:
|
|
||||||
return i
|
|
||||||
return -1
|
|
||||||
|
|
||||||
proc nav(g: ModuleGraph) =
|
|
||||||
# translate the track position to a packed position:
|
|
||||||
let unpacked = g.config.m.trackPos
|
|
||||||
var mid = unpacked.fileIndex.int
|
|
||||||
|
|
||||||
let fullPath = toFullPath(g.config, unpacked.fileIndex)
|
|
||||||
|
|
||||||
if g.packed[mid].status == undefined:
|
|
||||||
# check if 'mid' is an include file of some other module:
|
|
||||||
mid = searchForIncludeFile(g, fullPath)
|
|
||||||
|
|
||||||
if mid < 0:
|
|
||||||
localError(g.config, unpacked, "unknown file name: " & fullPath)
|
|
||||||
return
|
|
||||||
|
|
||||||
let fileId = g.packed[mid].fromDisk.strings.getKeyId(fullPath)
|
|
||||||
|
|
||||||
if fileId == LitId(0):
|
|
||||||
internalError(g.config, unpacked, "cannot find a valid file ID")
|
|
||||||
return
|
|
||||||
|
|
||||||
var c = NavContext(
|
|
||||||
g: g,
|
|
||||||
thisModule: int32 mid,
|
|
||||||
trackPos: PackedLineInfo(line: unpacked.line, col: unpacked.col, file: fileId),
|
|
||||||
outputSep: if isDefined(g.config, "nimIcNavigatorTests"): ' ' else: '\t'
|
|
||||||
)
|
|
||||||
var symId = search(c, g.packed[mid].fromDisk.topLevel)
|
|
||||||
if symId == EmptyItemId:
|
|
||||||
symId = search(c, g.packed[mid].fromDisk.bodies)
|
|
||||||
|
|
||||||
if symId == EmptyItemId:
|
|
||||||
localError(g.config, unpacked, "no symbol at this position")
|
|
||||||
return
|
|
||||||
|
|
||||||
for i in 0..high(g.packed):
|
|
||||||
# case statement here to enforce exhaustive checks.
|
|
||||||
case g.packed[i].status
|
|
||||||
of undefined:
|
|
||||||
discard "nothing to do"
|
|
||||||
of loading:
|
|
||||||
assert false, "cannot check integrity: Module still loading"
|
|
||||||
of stored, storing, outdated, loaded:
|
|
||||||
c.thisModule = int32 i
|
|
||||||
list(c, g.packed[i].fromDisk.topLevel, symId)
|
|
||||||
list(c, g.packed[i].fromDisk.bodies, symId)
|
|
||||||
|
|
||||||
proc navDefinition*(g: ModuleGraph) = nav(g)
|
|
||||||
proc navUsages*(g: ModuleGraph) = nav(g)
|
|
||||||
proc navDefusages*(g: ModuleGraph) = nav(g)
|
|
||||||
|
|
||||||
proc writeRodFiles*(g: ModuleGraph) =
|
|
||||||
for i in 0..high(g.packed):
|
|
||||||
case g.packed[i].status
|
|
||||||
of undefined, loading, stored, loaded:
|
|
||||||
discard "nothing to do"
|
|
||||||
of storing, outdated:
|
|
||||||
closeRodFile(g, g.packed[i].module)
|
|
||||||
|
|
@ -1,419 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2020 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Packed AST representation, mostly based on a seq of nodes.
|
|
||||||
## For IC support. Far future: Rewrite the compiler passes to
|
|
||||||
## use this representation directly in all the transformations,
|
|
||||||
## it is superior.
|
|
||||||
|
|
||||||
import hashes, tables, strtabs
|
|
||||||
import bitabs
|
|
||||||
import ".." / [ast, options]
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
|
||||||
SymId* = distinct int32
|
|
||||||
ModuleId* = distinct int32
|
|
||||||
NodePos* = distinct int
|
|
||||||
|
|
||||||
NodeId* = distinct int32
|
|
||||||
|
|
||||||
PackedItemId* = object
|
|
||||||
module*: LitId # 0 if it's this module
|
|
||||||
item*: int32 # same as the in-memory representation
|
|
||||||
|
|
||||||
const
|
|
||||||
nilItemId* = PackedItemId(module: LitId(0), item: -1.int32)
|
|
||||||
|
|
||||||
const
|
|
||||||
emptyNodeId* = NodeId(-1)
|
|
||||||
|
|
||||||
type
|
|
||||||
PackedLineInfo* = object
|
|
||||||
line*: uint16
|
|
||||||
col*: int16
|
|
||||||
file*: LitId
|
|
||||||
|
|
||||||
PackedLib* = object
|
|
||||||
kind*: TLibKind
|
|
||||||
generated*: bool
|
|
||||||
isOverriden*: bool
|
|
||||||
name*: LitId
|
|
||||||
path*: NodeId
|
|
||||||
|
|
||||||
PackedSym* = object
|
|
||||||
kind*: TSymKind
|
|
||||||
name*: LitId
|
|
||||||
typ*: PackedItemId
|
|
||||||
flags*: TSymFlags
|
|
||||||
magic*: TMagic
|
|
||||||
info*: PackedLineInfo
|
|
||||||
ast*: NodeId
|
|
||||||
owner*: PackedItemId
|
|
||||||
guard*: PackedItemId
|
|
||||||
bitsize*: int
|
|
||||||
alignment*: int # for alignment
|
|
||||||
options*: TOptions
|
|
||||||
position*: int
|
|
||||||
offset*: int
|
|
||||||
externalName*: LitId # instead of TLoc
|
|
||||||
locFlags*: TLocFlags
|
|
||||||
annex*: PackedLib
|
|
||||||
when hasFFI:
|
|
||||||
cname*: LitId
|
|
||||||
constraint*: NodeId
|
|
||||||
|
|
||||||
PackedType* = object
|
|
||||||
kind*: TTypeKind
|
|
||||||
callConv*: TCallingConvention
|
|
||||||
#nodekind*: TNodeKind
|
|
||||||
flags*: TTypeFlags
|
|
||||||
types*: seq[PackedItemId]
|
|
||||||
n*: NodeId
|
|
||||||
#nodeflags*: TNodeFlags
|
|
||||||
sym*: PackedItemId
|
|
||||||
owner*: PackedItemId
|
|
||||||
size*: BiggestInt
|
|
||||||
align*: int16
|
|
||||||
paddingAtEnd*: int16
|
|
||||||
# not serialized: loc*: TLoc because it is backend-specific
|
|
||||||
typeInst*: PackedItemId
|
|
||||||
nonUniqueId*: int32
|
|
||||||
|
|
||||||
PackedNode* = object # 28 bytes
|
|
||||||
kind*: TNodeKind
|
|
||||||
flags*: TNodeFlags
|
|
||||||
operand*: int32 # for kind in {nkSym, nkSymDef}: SymId
|
|
||||||
# for kind in {nkStrLit, nkIdent, nkNumberLit}: LitId
|
|
||||||
# for kind in nkInt32Lit: direct value
|
|
||||||
# for non-atom kinds: the number of nodes (for easy skipping)
|
|
||||||
typeId*: PackedItemId
|
|
||||||
info*: PackedLineInfo
|
|
||||||
|
|
||||||
PackedTree* = object ## usually represents a full Nim module
|
|
||||||
nodes*: seq[PackedNode]
|
|
||||||
|
|
||||||
PackedInstantiation* = object
|
|
||||||
key*, sym*: PackedItemId
|
|
||||||
concreteTypes*: seq[PackedItemId]
|
|
||||||
|
|
||||||
proc `==`*(a, b: SymId): bool {.borrow.}
|
|
||||||
proc hash*(a: SymId): Hash {.borrow.}
|
|
||||||
|
|
||||||
proc `==`*(a, b: NodePos): bool {.borrow.}
|
|
||||||
#proc `==`*(a, b: PackedItemId): bool {.borrow.}
|
|
||||||
proc `==`*(a, b: NodeId): bool {.borrow.}
|
|
||||||
|
|
||||||
proc newTreeFrom*(old: PackedTree): PackedTree =
|
|
||||||
result.nodes = @[]
|
|
||||||
when false: result.sh = old.sh
|
|
||||||
|
|
||||||
when false:
|
|
||||||
proc declareSym*(tree: var PackedTree; kind: TSymKind;
|
|
||||||
name: LitId; info: PackedLineInfo): SymId =
|
|
||||||
result = SymId(tree.sh.syms.len)
|
|
||||||
tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info)
|
|
||||||
|
|
||||||
proc litIdFromName*(tree: PackedTree; name: string): LitId =
|
|
||||||
result = tree.sh.strings.getOrIncl(name)
|
|
||||||
|
|
||||||
proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) =
|
|
||||||
tree.nodes.add PackedNode(kind: kind, info: info,
|
|
||||||
operand: int32 getOrIncl(tree.sh.strings, token))
|
|
||||||
|
|
||||||
proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) =
|
|
||||||
tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
|
|
||||||
|
|
||||||
proc throwAwayLastNode*(tree: var PackedTree) =
|
|
||||||
tree.nodes.setLen(tree.nodes.len-1)
|
|
||||||
|
|
||||||
proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) =
|
|
||||||
tree.nodes.add PackedNode(kind: nkIdent, operand: int32(s), info: info)
|
|
||||||
|
|
||||||
proc addSym*(tree: var PackedTree; s: int32; info: PackedLineInfo) =
|
|
||||||
tree.nodes.add PackedNode(kind: nkSym, operand: s, info: info)
|
|
||||||
|
|
||||||
proc addModuleId*(tree: var PackedTree; s: ModuleId; info: PackedLineInfo) =
|
|
||||||
tree.nodes.add PackedNode(kind: nkInt32Lit, operand: int32(s), info: info)
|
|
||||||
|
|
||||||
proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
|
|
||||||
tree.nodes.add PackedNode(kind: nkSym, operand: int32(s), info: info)
|
|
||||||
|
|
||||||
proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit
|
|
||||||
|
|
||||||
proc copyTree*(dest: var PackedTree; tree: PackedTree; n: NodePos) =
|
|
||||||
# and this is why the IR is superior. We can copy subtrees
|
|
||||||
# via a linear scan.
|
|
||||||
let pos = n.int
|
|
||||||
let L = if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
|
|
||||||
let d = dest.nodes.len
|
|
||||||
dest.nodes.setLen(d + L)
|
|
||||||
for i in 0..<L:
|
|
||||||
dest.nodes[d+i] = tree.nodes[pos+i]
|
|
||||||
|
|
||||||
when false:
|
|
||||||
proc copySym*(dest: var PackedTree; tree: PackedTree; s: SymId): SymId =
|
|
||||||
result = SymId(dest.sh.syms.len)
|
|
||||||
assert int(s) < tree.sh.syms.len
|
|
||||||
let oldSym = tree.sh.syms[s.int]
|
|
||||||
dest.sh.syms.add oldSym
|
|
||||||
|
|
||||||
type
|
|
||||||
PatchPos = distinct int
|
|
||||||
|
|
||||||
when false:
|
|
||||||
proc prepare*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo): PatchPos =
|
|
||||||
result = PatchPos tree.nodes.len
|
|
||||||
tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
|
|
||||||
|
|
||||||
proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: PackedItemId; info: PackedLineInfo): PatchPos =
|
|
||||||
result = PatchPos tree.nodes.len
|
|
||||||
tree.nodes.add PackedNode(kind: kind, flags: flags, operand: 0, info: info,
|
|
||||||
typeId: typeId)
|
|
||||||
|
|
||||||
proc prepare*(dest: var PackedTree; source: PackedTree; sourcePos: NodePos): PatchPos =
|
|
||||||
result = PatchPos dest.nodes.len
|
|
||||||
dest.nodes.add source.nodes[sourcePos.int]
|
|
||||||
|
|
||||||
proc patch*(tree: var PackedTree; pos: PatchPos) =
|
|
||||||
let pos = pos.int
|
|
||||||
assert tree.nodes[pos].kind > nkNilLit
|
|
||||||
let distance = int32(tree.nodes.len - pos)
|
|
||||||
tree.nodes[pos].operand = distance
|
|
||||||
|
|
||||||
proc len*(tree: PackedTree): int {.inline.} = tree.nodes.len
|
|
||||||
|
|
||||||
proc `[]`*(tree: PackedTree; i: int): lent PackedNode {.inline.} =
|
|
||||||
tree.nodes[i]
|
|
||||||
|
|
||||||
proc nextChild(tree: PackedTree; pos: var int) {.inline.} =
|
|
||||||
if tree.nodes[pos].kind > nkNilLit:
|
|
||||||
assert tree.nodes[pos].operand > 0
|
|
||||||
inc pos, tree.nodes[pos].operand
|
|
||||||
else:
|
|
||||||
inc pos
|
|
||||||
|
|
||||||
iterator sonsReadonly*(tree: PackedTree; n: NodePos): NodePos =
|
|
||||||
var pos = n.int
|
|
||||||
assert tree.nodes[pos].kind > nkNilLit
|
|
||||||
let last = pos + tree.nodes[pos].operand
|
|
||||||
inc pos
|
|
||||||
while pos < last:
|
|
||||||
yield NodePos pos
|
|
||||||
nextChild tree, pos
|
|
||||||
|
|
||||||
iterator sons*(dest: var PackedTree; tree: PackedTree; n: NodePos): NodePos =
|
|
||||||
let patchPos = prepare(dest, tree, n)
|
|
||||||
for x in sonsReadonly(tree, n): yield x
|
|
||||||
patch dest, patchPos
|
|
||||||
|
|
||||||
iterator isons*(dest: var PackedTree; tree: PackedTree;
|
|
||||||
n: NodePos): (int, NodePos) =
|
|
||||||
var i = 0
|
|
||||||
for ch0 in sons(dest, tree, n):
|
|
||||||
yield (i, ch0)
|
|
||||||
inc i
|
|
||||||
|
|
||||||
iterator sonsFrom1*(tree: PackedTree; n: NodePos): NodePos =
|
|
||||||
var pos = n.int
|
|
||||||
assert tree.nodes[pos].kind > nkNilLit
|
|
||||||
let last = pos + tree.nodes[pos].operand
|
|
||||||
inc pos
|
|
||||||
if pos < last:
|
|
||||||
nextChild tree, pos
|
|
||||||
while pos < last:
|
|
||||||
yield NodePos pos
|
|
||||||
nextChild tree, pos
|
|
||||||
|
|
||||||
iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos =
|
|
||||||
var count = 0
|
|
||||||
for child in sonsReadonly(tree, n):
|
|
||||||
inc count
|
|
||||||
var pos = n.int
|
|
||||||
assert tree.nodes[pos].kind > nkNilLit
|
|
||||||
let last = pos + tree.nodes[pos].operand
|
|
||||||
inc pos
|
|
||||||
while pos < last and count > 2:
|
|
||||||
yield NodePos pos
|
|
||||||
dec count
|
|
||||||
nextChild tree, pos
|
|
||||||
|
|
||||||
proc parentImpl(tree: PackedTree; n: NodePos): NodePos =
|
|
||||||
# finding the parent of a node is rather easy:
|
|
||||||
var pos = n.int - 1
|
|
||||||
while pos >= 0 and (isAtom(tree, pos) or (pos + tree.nodes[pos].operand - 1 < n.int)):
|
|
||||||
dec pos
|
|
||||||
#assert pos >= 0, "node has no parent"
|
|
||||||
result = NodePos(pos)
|
|
||||||
|
|
||||||
template parent*(n: NodePos): NodePos = parentImpl(tree, n)
|
|
||||||
|
|
||||||
proc hasXsons*(tree: PackedTree; n: NodePos; x: int): bool =
|
|
||||||
var count = 0
|
|
||||||
if tree.nodes[n.int].kind > nkNilLit:
|
|
||||||
for child in sonsReadonly(tree, n): inc count
|
|
||||||
result = count == x
|
|
||||||
|
|
||||||
proc hasAtLeastXsons*(tree: PackedTree; n: NodePos; x: int): bool =
|
|
||||||
if tree.nodes[n.int].kind > nkNilLit:
|
|
||||||
var count = 0
|
|
||||||
for child in sonsReadonly(tree, n):
|
|
||||||
inc count
|
|
||||||
if count >= x: return true
|
|
||||||
return false
|
|
||||||
|
|
||||||
proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} =
|
|
||||||
NodePos(n.int+1)
|
|
||||||
proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} =
|
|
||||||
tree.nodes[n.int].kind
|
|
||||||
proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} =
|
|
||||||
LitId tree.nodes[n.int].operand
|
|
||||||
proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} =
|
|
||||||
tree.nodes[n.int].info
|
|
||||||
|
|
||||||
template typ*(n: NodePos): PackedItemId =
|
|
||||||
tree.nodes[n.int].typeId
|
|
||||||
template flags*(n: NodePos): TNodeFlags =
|
|
||||||
tree.nodes[n.int].flags
|
|
||||||
|
|
||||||
template operand*(n: NodePos): int32 =
|
|
||||||
tree.nodes[n.int].operand
|
|
||||||
|
|
||||||
proc span*(tree: PackedTree; pos: int): int {.inline.} =
|
|
||||||
if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
|
|
||||||
|
|
||||||
proc sons2*(tree: PackedTree; n: NodePos): (NodePos, NodePos) =
|
|
||||||
assert(not isAtom(tree, n.int))
|
|
||||||
let a = n.int+1
|
|
||||||
let b = a + span(tree, a)
|
|
||||||
result = (NodePos a, NodePos b)
|
|
||||||
|
|
||||||
proc sons3*(tree: PackedTree; n: NodePos): (NodePos, NodePos, NodePos) =
|
|
||||||
assert(not isAtom(tree, n.int))
|
|
||||||
let a = n.int+1
|
|
||||||
let b = a + span(tree, a)
|
|
||||||
let c = b + span(tree, b)
|
|
||||||
result = (NodePos a, NodePos b, NodePos c)
|
|
||||||
|
|
||||||
proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos =
|
|
||||||
if tree.nodes[n.int].kind > nkNilLit:
|
|
||||||
var count = 0
|
|
||||||
for child in sonsReadonly(tree, n):
|
|
||||||
if count == i: return child
|
|
||||||
inc count
|
|
||||||
assert false, "node has no i-th child"
|
|
||||||
|
|
||||||
when false:
|
|
||||||
proc `@`*(tree: PackedTree; lit: LitId): lent string {.inline.} =
|
|
||||||
tree.sh.strings[lit]
|
|
||||||
|
|
||||||
template kind*(n: NodePos): TNodeKind = tree.nodes[n.int].kind
|
|
||||||
template info*(n: NodePos): PackedLineInfo = tree.nodes[n.int].info
|
|
||||||
template litId*(n: NodePos): LitId = LitId tree.nodes[n.int].operand
|
|
||||||
|
|
||||||
template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].operand
|
|
||||||
|
|
||||||
proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1)
|
|
||||||
|
|
||||||
when false:
|
|
||||||
# xxx `nkStrLit` or `nkStrLit..nkTripleStrLit:` below?
|
|
||||||
proc strLit*(tree: PackedTree; n: NodePos): lent string =
|
|
||||||
assert n.kind == nkStrLit
|
|
||||||
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
|
|
||||||
|
|
||||||
proc strVal*(tree: PackedTree; n: NodePos): string =
|
|
||||||
assert n.kind == nkStrLit
|
|
||||||
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
|
|
||||||
#result = cookedStrLit(raw)
|
|
||||||
|
|
||||||
proc filenameVal*(tree: PackedTree; n: NodePos): string =
|
|
||||||
case n.kind
|
|
||||||
of nkStrLit:
|
|
||||||
result = strVal(tree, n)
|
|
||||||
of nkIdent:
|
|
||||||
result = tree.sh.strings[n.litId]
|
|
||||||
of nkSym:
|
|
||||||
result = tree.sh.strings[tree.sh.syms[int n.symId].name]
|
|
||||||
else:
|
|
||||||
result = ""
|
|
||||||
|
|
||||||
proc identAsStr*(tree: PackedTree; n: NodePos): lent string =
|
|
||||||
assert n.kind == nkIdent
|
|
||||||
result = tree.sh.strings[LitId tree.nodes[n.int].operand]
|
|
||||||
|
|
||||||
const
|
|
||||||
externIntLit* = {nkCharLit,
|
|
||||||
nkIntLit,
|
|
||||||
nkInt8Lit,
|
|
||||||
nkInt16Lit,
|
|
||||||
nkInt64Lit,
|
|
||||||
nkUIntLit,
|
|
||||||
nkUInt8Lit,
|
|
||||||
nkUInt16Lit,
|
|
||||||
nkUInt32Lit,
|
|
||||||
nkUInt64Lit} # nkInt32Lit is missing by design!
|
|
||||||
|
|
||||||
externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt64Lit}
|
|
||||||
externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit}
|
|
||||||
directIntLit* = nkInt32Lit
|
|
||||||
|
|
||||||
when false:
|
|
||||||
proc identIdImpl(tree: PackedTree; n: NodePos): LitId =
|
|
||||||
if n.kind == nkIdent:
|
|
||||||
result = n.litId
|
|
||||||
elif n.kind == nkSym:
|
|
||||||
result = tree.sh.syms[int n.symId].name
|
|
||||||
else:
|
|
||||||
result = LitId(0)
|
|
||||||
|
|
||||||
template identId*(n: NodePos): LitId = identIdImpl(tree, n)
|
|
||||||
|
|
||||||
template copyInto*(dest, n, body) =
|
|
||||||
let patchPos = prepare(dest, tree, n)
|
|
||||||
body
|
|
||||||
patch dest, patchPos
|
|
||||||
|
|
||||||
template copyIntoKind*(dest, kind, info, body) =
|
|
||||||
let patchPos = prepare(dest, kind, info)
|
|
||||||
body
|
|
||||||
patch dest, patchPos
|
|
||||||
|
|
||||||
when false:
|
|
||||||
proc hasPragma*(tree: PackedTree; n: NodePos; pragma: string): bool =
|
|
||||||
let litId = tree.sh.strings.getKeyId(pragma)
|
|
||||||
if litId == LitId(0):
|
|
||||||
return false
|
|
||||||
assert n.kind == nkPragma
|
|
||||||
for ch0 in sonsReadonly(tree, n):
|
|
||||||
if ch0.kind == nkExprColonExpr:
|
|
||||||
if ch0.firstSon.identId == litId:
|
|
||||||
return true
|
|
||||||
elif ch0.identId == litId:
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc getNodeId*(tree: PackedTree): NodeId {.inline.} = NodeId tree.nodes.len
|
|
||||||
|
|
||||||
when false:
|
|
||||||
proc produceError*(dest: var PackedTree; tree: PackedTree; n: NodePos; msg: string) =
|
|
||||||
let patchPos = prepare(dest, nkError, n.info)
|
|
||||||
dest.add nkStrLit, msg, n.info
|
|
||||||
copyTree(dest, tree, n)
|
|
||||||
patch dest, patchPos
|
|
||||||
|
|
||||||
iterator allNodes*(tree: PackedTree): NodePos =
|
|
||||||
var p = 0
|
|
||||||
while p < tree.len:
|
|
||||||
yield NodePos(p)
|
|
||||||
let s = span(tree, p)
|
|
||||||
inc p, s
|
|
||||||
|
|
||||||
proc toPackedItemId*(item: int32): PackedItemId {.inline.} =
|
|
||||||
PackedItemId(module: LitId(0), item: item)
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2020 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Module that contains code to replay global VM state changes and pragma
|
|
||||||
## state like ``{.compile: "foo.c".}``. For IC (= Incremental compilation)
|
|
||||||
## support.
|
|
||||||
|
|
||||||
import ".." / [ast, modulegraphs, trees, extccomp, btrees,
|
|
||||||
msgs, lineinfos, pathutils, options, cgmeth]
|
|
||||||
|
|
||||||
import tables
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
import packed_ast, ic, bitabs
|
|
||||||
|
|
||||||
proc replayStateChanges*(module: PSym; g: ModuleGraph) =
|
|
||||||
let list = module.ast
|
|
||||||
assert list != nil
|
|
||||||
assert list.kind == nkStmtList
|
|
||||||
for n in list:
|
|
||||||
assert n.kind == nkReplayAction
|
|
||||||
# Fortunately only a tiny subset of the available pragmas need to
|
|
||||||
# be replayed here. This is always a subset of ``pragmas.stmtPragmas``.
|
|
||||||
if n.len >= 2:
|
|
||||||
internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit
|
|
||||||
case n[0].strVal
|
|
||||||
of "hint": message(g.config, n.info, hintUser, n[1].strVal)
|
|
||||||
of "warning": message(g.config, n.info, warnUser, n[1].strVal)
|
|
||||||
of "error": localError(g.config, n.info, errUser, n[1].strVal)
|
|
||||||
of "compile":
|
|
||||||
internalAssert g.config, n.len == 4 and n[2].kind == nkStrLit
|
|
||||||
let cname = AbsoluteFile n[1].strVal
|
|
||||||
var cf = Cfile(nimname: splitFile(cname).name, cname: cname,
|
|
||||||
obj: AbsoluteFile n[2].strVal,
|
|
||||||
flags: {CfileFlag.External},
|
|
||||||
customArgs: n[3].strVal)
|
|
||||||
extccomp.addExternalFileToCompile(g.config, cf)
|
|
||||||
of "link":
|
|
||||||
extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal)
|
|
||||||
of "passl":
|
|
||||||
extccomp.addLinkOption(g.config, n[1].strVal)
|
|
||||||
of "passc":
|
|
||||||
extccomp.addCompileOption(g.config, n[1].strVal)
|
|
||||||
of "localpassc":
|
|
||||||
extccomp.addLocalCompileOption(g.config, n[1].strVal, toFullPathConsiderDirty(g.config, module.info.fileIndex))
|
|
||||||
of "cppdefine":
|
|
||||||
options.cppDefine(g.config, n[1].strVal)
|
|
||||||
of "inc":
|
|
||||||
let destKey = n[1].strVal
|
|
||||||
let by = n[2].intVal
|
|
||||||
let v = getOrDefault(g.cacheCounters, destKey)
|
|
||||||
g.cacheCounters[destKey] = v+by
|
|
||||||
of "put":
|
|
||||||
let destKey = n[1].strVal
|
|
||||||
let key = n[2].strVal
|
|
||||||
let val = n[3]
|
|
||||||
if not contains(g.cacheTables, destKey):
|
|
||||||
g.cacheTables[destKey] = initBTree[string, PNode]()
|
|
||||||
if not contains(g.cacheTables[destKey], key):
|
|
||||||
g.cacheTables[destKey].add(key, val)
|
|
||||||
else:
|
|
||||||
internalError(g.config, n.info, "key already exists: " & key)
|
|
||||||
of "incl":
|
|
||||||
let destKey = n[1].strVal
|
|
||||||
let val = n[2]
|
|
||||||
if not contains(g.cacheSeqs, destKey):
|
|
||||||
g.cacheSeqs[destKey] = newTree(nkStmtList, val)
|
|
||||||
else:
|
|
||||||
block search:
|
|
||||||
for existing in g.cacheSeqs[destKey]:
|
|
||||||
if exprStructuralEquivalent(existing, val, strictSymEquality=true):
|
|
||||||
break search
|
|
||||||
g.cacheSeqs[destKey].add val
|
|
||||||
of "add":
|
|
||||||
let destKey = n[1].strVal
|
|
||||||
let val = n[2]
|
|
||||||
if not contains(g.cacheSeqs, destKey):
|
|
||||||
g.cacheSeqs[destKey] = newTree(nkStmtList, val)
|
|
||||||
else:
|
|
||||||
g.cacheSeqs[destKey].add val
|
|
||||||
else:
|
|
||||||
internalAssert g.config, false
|
|
||||||
|
|
||||||
proc replayBackendProcs*(g: ModuleGraph; module: int) =
|
|
||||||
for it in mitems(g.packed[module].fromDisk.attachedOps):
|
|
||||||
let key = translateId(it[0], g.packed, module, g.config)
|
|
||||||
let op = it[1]
|
|
||||||
let tmp = translateId(it[2], g.packed, module, g.config)
|
|
||||||
let symId = FullId(module: tmp.module, packed: it[2])
|
|
||||||
g.attachedOps[op][key] = LazySym(id: symId, sym: nil)
|
|
||||||
|
|
||||||
for it in mitems(g.packed[module].fromDisk.enumToStringProcs):
|
|
||||||
let key = translateId(it[0], g.packed, module, g.config)
|
|
||||||
let tmp = translateId(it[1], g.packed, module, g.config)
|
|
||||||
let symId = FullId(module: tmp.module, packed: it[1])
|
|
||||||
g.enumToStringProcs[key] = LazySym(id: symId, sym: nil)
|
|
||||||
|
|
||||||
proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
|
|
||||||
## We remember the generic instantiations a module performed
|
|
||||||
## in order to to avoid the code bloat that generic code tends
|
|
||||||
## to imply. This is cheaper than deduplication of identical
|
|
||||||
## generic instantiations. However, deduplication is more
|
|
||||||
## powerful and general and I hope to implement it soon too
|
|
||||||
## (famous last words).
|
|
||||||
assert g.packed[module].status == loaded
|
|
||||||
for it in g.packed[module].fromDisk.typeInstCache:
|
|
||||||
let key = translateId(it[0], g.packed, module, g.config)
|
|
||||||
g.typeInstCache.mgetOrPut(key, @[]).add LazyType(id: FullId(module: module, packed: it[1]), typ: nil)
|
|
||||||
|
|
||||||
for it in mitems(g.packed[module].fromDisk.procInstCache):
|
|
||||||
let key = translateId(it.key, g.packed, module, g.config)
|
|
||||||
let sym = translateId(it.sym, g.packed, module, g.config)
|
|
||||||
var concreteTypes = newSeq[FullId](it.concreteTypes.len)
|
|
||||||
for i in 0..high(it.concreteTypes):
|
|
||||||
let tmp = translateId(it.concreteTypes[i], g.packed, module, g.config)
|
|
||||||
concreteTypes[i] = FullId(module: tmp.module, packed: it.concreteTypes[i])
|
|
||||||
|
|
||||||
g.procInstCache.mgetOrPut(key, @[]).add LazyInstantiation(
|
|
||||||
module: module, sym: FullId(module: sym.module, packed: it.sym),
|
|
||||||
concreteTypes: concreteTypes, inst: nil)
|
|
||||||
|
|
||||||
for it in mitems(g.packed[module].fromDisk.methodsPerType):
|
|
||||||
let key = translateId(it[0], g.packed, module, g.config)
|
|
||||||
let col = it[1]
|
|
||||||
let tmp = translateId(it[2], g.packed, module, g.config)
|
|
||||||
let symId = FullId(module: tmp.module, packed: it[2])
|
|
||||||
g.methodsPerType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
|
|
||||||
|
|
||||||
replayBackendProcs(g, module)
|
|
||||||
|
|
||||||
for it in mitems(g.packed[module].fromDisk.methods):
|
|
||||||
let sym = loadSymFromId(g.config, g.cache, g.packed, module,
|
|
||||||
PackedItemId(module: LitId(0), item: it))
|
|
||||||
methodDef(g, g.idgen, sym)
|
|
||||||
|
|
||||||
when false:
|
|
||||||
# not used anymore:
|
|
||||||
for it in mitems(g.packed[module].fromDisk.compilerProcs):
|
|
||||||
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it[1]))
|
|
||||||
g.lazyCompilerprocs[g.packed[module].fromDisk.sh.strings[it[0]]] = symId
|
|
||||||
|
|
||||||
for it in mitems(g.packed[module].fromDisk.converters):
|
|
||||||
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
|
|
||||||
g.ifaces[module].converters.add LazySym(id: symId, sym: nil)
|
|
||||||
|
|
||||||
for it in mitems(g.packed[module].fromDisk.trmacros):
|
|
||||||
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
|
|
||||||
g.ifaces[module].patterns.add LazySym(id: symId, sym: nil)
|
|
||||||
|
|
||||||
for it in mitems(g.packed[module].fromDisk.pureEnums):
|
|
||||||
let symId = FullId(module: module, packed: PackedItemId(module: LitId(0), item: it))
|
|
||||||
g.ifaces[module].pureEnums.add LazySym(id: symId, sym: nil)
|
|
||||||
|
|
@ -1,249 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2020 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Low level binary format used by the compiler to store and load various AST
|
|
||||||
## and related data.
|
|
||||||
##
|
|
||||||
## NB: this is incredibly low level and if you're interested in how the
|
|
||||||
## compiler works and less a storage format, you're probably looking for
|
|
||||||
## the `ic` or `packed_ast` modules to understand the logical format.
|
|
||||||
|
|
||||||
from typetraits import supportsCopyMem
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/[syncio, assertions]
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
## ========
|
|
||||||
## `RodFile` represents a Rod File (versioned binary format), and the
|
|
||||||
## associated data for common interactions such as IO and error tracking
|
|
||||||
## (`RodFileError`). The file format broken up into sections (`RodSection`)
|
|
||||||
## and preceded by a header (see: `cookie`). The precise layout, section
|
|
||||||
## ordering and data following the section are determined by the user. See
|
|
||||||
## `ic.loadRodFile`.
|
|
||||||
##
|
|
||||||
## A basic but "wrong" example of the lifecycle:
|
|
||||||
## ---------------------------------------------
|
|
||||||
## 1. `create` or `open` - create a new one or open an existing
|
|
||||||
## 2. `storeHeader` - header info
|
|
||||||
## 3. `storePrim` or `storeSeq` - save your stuff
|
|
||||||
## 4. `close` - and we're done
|
|
||||||
##
|
|
||||||
## Now read the bits below to understand what's missing.
|
|
||||||
##
|
|
||||||
## ### Issues with the Example
|
|
||||||
## Missing Sections:
|
|
||||||
## This is a low level API, so headers and sections need to be stored and
|
|
||||||
## loaded by the user, see `storeHeader` & `loadHeader` and `storeSection` &
|
|
||||||
## `loadSection`, respectively.
|
|
||||||
##
|
|
||||||
## No Error Handling:
|
|
||||||
## The API is centered around IO and prone to error, each operation checks or
|
|
||||||
## sets the `RodFile.err` field. A user of this API needs to handle these
|
|
||||||
## appropriately.
|
|
||||||
##
|
|
||||||
## API Notes
|
|
||||||
## =========
|
|
||||||
##
|
|
||||||
## Valid inputs for Rod files
|
|
||||||
## --------------------------
|
|
||||||
## ASTs, hopes, dreams, and anything as long as it and any children it may have
|
|
||||||
## support `copyMem`. This means anything that is not a pointer and that does not contain a pointer. At a glance these are:
|
|
||||||
## * string
|
|
||||||
## * objects & tuples (fields are recursed)
|
|
||||||
## * sequences AKA `seq[T]`
|
|
||||||
##
|
|
||||||
## Note on error handling style
|
|
||||||
## ----------------------------
|
|
||||||
## A flag based approach is used where operations no-op in case of a
|
|
||||||
## preexisting error and set the flag if they encounter one.
|
|
||||||
##
|
|
||||||
## Misc
|
|
||||||
## ----
|
|
||||||
## * 'Prim' is short for 'primitive', as in a non-sequence type
|
|
||||||
|
|
||||||
type
|
|
||||||
RodSection* = enum
|
|
||||||
versionSection
|
|
||||||
configSection
|
|
||||||
stringsSection
|
|
||||||
checkSumsSection
|
|
||||||
depsSection
|
|
||||||
numbersSection
|
|
||||||
exportsSection
|
|
||||||
hiddenSection
|
|
||||||
reexportsSection
|
|
||||||
compilerProcsSection
|
|
||||||
trmacrosSection
|
|
||||||
convertersSection
|
|
||||||
methodsSection
|
|
||||||
pureEnumsSection
|
|
||||||
toReplaySection
|
|
||||||
topLevelSection
|
|
||||||
bodiesSection
|
|
||||||
symsSection
|
|
||||||
typesSection
|
|
||||||
typeInstCacheSection
|
|
||||||
procInstCacheSection
|
|
||||||
attachedOpsSection
|
|
||||||
methodsPerTypeSection
|
|
||||||
enumToStringProcsSection
|
|
||||||
typeInfoSection # required by the backend
|
|
||||||
backendFlagsSection
|
|
||||||
aliveSymsSection # beware, this is stored in a `.alivesyms` file.
|
|
||||||
|
|
||||||
RodFileError* = enum
|
|
||||||
ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
|
|
||||||
includeFileChanged
|
|
||||||
|
|
||||||
RodFile* = object
|
|
||||||
f*: File
|
|
||||||
currentSection*: RodSection # for error checking
|
|
||||||
err*: RodFileError # little experiment to see if this works
|
|
||||||
# better than exceptions.
|
|
||||||
|
|
||||||
const
|
|
||||||
RodVersion = 1
|
|
||||||
cookie = [byte(0), byte('R'), byte('O'), byte('D'),
|
|
||||||
byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
|
|
||||||
|
|
||||||
proc setError(f: var RodFile; err: RodFileError) {.inline.} =
|
|
||||||
f.err = err
|
|
||||||
#raise newException(IOError, "IO error")
|
|
||||||
|
|
||||||
proc storePrim*(f: var RodFile; s: string) =
|
|
||||||
## Stores a string.
|
|
||||||
## The len is prefixed to allow for later retreival.
|
|
||||||
if f.err != ok: return
|
|
||||||
if s.len >= high(int32):
|
|
||||||
setError f, tooBig
|
|
||||||
return
|
|
||||||
var lenPrefix = int32(s.len)
|
|
||||||
if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
|
|
||||||
setError f, ioFailure
|
|
||||||
else:
|
|
||||||
if s.len != 0:
|
|
||||||
if writeBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
|
|
||||||
setError f, ioFailure
|
|
||||||
|
|
||||||
proc storePrim*[T](f: var RodFile; x: T) =
|
|
||||||
## Stores a non-sequence/string `T`.
|
|
||||||
## If `T` doesn't support `copyMem` and is an object or tuple then the fields
|
|
||||||
## are written -- the user from context will need to know which `T` to load.
|
|
||||||
if f.err != ok: return
|
|
||||||
when supportsCopyMem(T):
|
|
||||||
if writeBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
|
|
||||||
setError f, ioFailure
|
|
||||||
elif T is tuple:
|
|
||||||
for y in fields(x):
|
|
||||||
storePrim(f, y)
|
|
||||||
elif T is object:
|
|
||||||
for y in fields(x):
|
|
||||||
when y is seq:
|
|
||||||
storeSeq(f, y)
|
|
||||||
else:
|
|
||||||
storePrim(f, y)
|
|
||||||
else:
|
|
||||||
{.error: "unsupported type for 'storePrim'".}
|
|
||||||
|
|
||||||
proc storeSeq*[T](f: var RodFile; s: seq[T]) =
|
|
||||||
## Stores a sequence of `T`s, with the len as a prefix for later retrieval.
|
|
||||||
if f.err != ok: return
|
|
||||||
if s.len >= high(int32):
|
|
||||||
setError f, tooBig
|
|
||||||
return
|
|
||||||
var lenPrefix = int32(s.len)
|
|
||||||
if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
|
|
||||||
setError f, ioFailure
|
|
||||||
else:
|
|
||||||
for i in 0..<s.len:
|
|
||||||
storePrim(f, s[i])
|
|
||||||
|
|
||||||
proc loadPrim*(f: var RodFile; s: var string) =
|
|
||||||
## Read a string, the length was stored as a prefix
|
|
||||||
if f.err != ok: return
|
|
||||||
var lenPrefix = int32(0)
|
|
||||||
if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
|
|
||||||
setError f, ioFailure
|
|
||||||
else:
|
|
||||||
s = newString(lenPrefix)
|
|
||||||
if lenPrefix > 0:
|
|
||||||
if readBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len:
|
|
||||||
setError f, ioFailure
|
|
||||||
|
|
||||||
proc loadPrim*[T](f: var RodFile; x: var T) =
|
|
||||||
## Load a non-sequence/string `T`.
|
|
||||||
if f.err != ok: return
|
|
||||||
when supportsCopyMem(T):
|
|
||||||
if readBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x):
|
|
||||||
setError f, ioFailure
|
|
||||||
elif T is tuple:
|
|
||||||
for y in fields(x):
|
|
||||||
loadPrim(f, y)
|
|
||||||
elif T is object:
|
|
||||||
for y in fields(x):
|
|
||||||
when y is seq:
|
|
||||||
loadSeq(f, y)
|
|
||||||
else:
|
|
||||||
loadPrim(f, y)
|
|
||||||
else:
|
|
||||||
{.error: "unsupported type for 'loadPrim'".}
|
|
||||||
|
|
||||||
proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
|
|
||||||
## `T` must be compatible with `copyMem`, see `loadPrim`
|
|
||||||
if f.err != ok: return
|
|
||||||
var lenPrefix = int32(0)
|
|
||||||
if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
|
|
||||||
setError f, ioFailure
|
|
||||||
else:
|
|
||||||
s = newSeq[T](lenPrefix)
|
|
||||||
for i in 0..<lenPrefix:
|
|
||||||
loadPrim(f, s[i])
|
|
||||||
|
|
||||||
proc storeHeader*(f: var RodFile) =
|
|
||||||
## stores the header which is described by `cookie`.
|
|
||||||
if f.err != ok: return
|
|
||||||
if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len:
|
|
||||||
setError f, ioFailure
|
|
||||||
|
|
||||||
proc loadHeader*(f: var RodFile) =
|
|
||||||
## Loads the header which is described by `cookie`.
|
|
||||||
if f.err != ok: return
|
|
||||||
var thisCookie: array[cookie.len, byte]
|
|
||||||
if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len:
|
|
||||||
setError f, ioFailure
|
|
||||||
elif thisCookie != cookie:
|
|
||||||
setError f, wrongHeader
|
|
||||||
|
|
||||||
proc storeSection*(f: var RodFile; s: RodSection) =
|
|
||||||
## update `currentSection` and writes the bytes value of s.
|
|
||||||
if f.err != ok: return
|
|
||||||
assert f.currentSection < s
|
|
||||||
f.currentSection = s
|
|
||||||
storePrim(f, s)
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
if not open(result.f, filename, fmWrite):
|
|
||||||
setError result, cannotOpen
|
|
||||||
|
|
||||||
proc close*(f: var RodFile) = close(f.f)
|
|
||||||
|
|
||||||
proc open*(filename: string): RodFile =
|
|
||||||
## open the file for reading
|
|
||||||
if not open(result.f, filename, fmRead):
|
|
||||||
setError result, cannotOpen
|
|
||||||
|
|
@ -11,29 +11,26 @@
|
||||||
# An identifier is a shared immutable string that can be compared by its
|
# An identifier is a shared immutable string that can be compared by its
|
||||||
# id. This module is essential for the compiler's performance.
|
# id. This module is essential for the compiler's performance.
|
||||||
|
|
||||||
import
|
import
|
||||||
hashes, wordrecg
|
hashes, strutils
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
type
|
||||||
import std/assertions
|
TIdObj* = object of RootObj
|
||||||
|
|
||||||
type
|
|
||||||
PIdent* = ref TIdent
|
|
||||||
TIdent*{.acyclic.} = object
|
|
||||||
id*: int # unique id; use this for comparisons and not the pointers
|
id*: int # unique id; use this for comparisons and not the pointers
|
||||||
|
|
||||||
|
PIdObj* = ref TIdObj
|
||||||
|
PIdent* = ref TIdent
|
||||||
|
TIdent*{.acyclic.} = object of TIdObj
|
||||||
s*: string
|
s*: string
|
||||||
next*: PIdent # for hash-table chaining
|
next*: PIdent # for hash-table chaining
|
||||||
h*: Hash # hash value of s
|
h*: THash # hash value of s
|
||||||
|
|
||||||
IdentCache* = ref object
|
var firstCharIsCS*: bool = true
|
||||||
buckets: array[0..4096 * 2 - 1, PIdent]
|
var buckets*: array[0..4096 * 2 - 1, PIdent]
|
||||||
wordCounter: int
|
|
||||||
idAnon*, idDelegator*, emptyIdent*: PIdent
|
|
||||||
|
|
||||||
proc resetIdentCache*() = discard
|
proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
|
||||||
|
if firstCharIsCS:
|
||||||
proc cmpIgnoreStyle*(a, b: cstring, blen: int): int =
|
if a[0] != b[0]: return 1
|
||||||
if a[0] != b[0]: return 1
|
|
||||||
var i = 0
|
var i = 0
|
||||||
var j = 0
|
var j = 0
|
||||||
result = 1
|
result = 1
|
||||||
|
|
@ -46,12 +43,12 @@ proc cmpIgnoreStyle*(a, b: cstring, blen: int): int =
|
||||||
if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A')))
|
if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A')))
|
||||||
if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A')))
|
if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A')))
|
||||||
result = ord(aa) - ord(bb)
|
result = ord(aa) - ord(bb)
|
||||||
if (result != 0) or (aa == '\0'): break
|
if (result != 0) or (aa == '\0'): break
|
||||||
inc(i)
|
inc(i)
|
||||||
inc(j)
|
inc(j)
|
||||||
if result == 0:
|
if result == 0:
|
||||||
if a[i] != '\0': result = 1
|
if a[i] != '\0': result = 1
|
||||||
|
|
||||||
proc cmpExact(a, b: cstring, blen: int): int =
|
proc cmpExact(a, b: cstring, blen: int): int =
|
||||||
var i = 0
|
var i = 0
|
||||||
var j = 0
|
var j = 0
|
||||||
|
|
@ -60,25 +57,27 @@ proc cmpExact(a, b: cstring, blen: int): int =
|
||||||
var aa = a[i]
|
var aa = a[i]
|
||||||
var bb = b[j]
|
var bb = b[j]
|
||||||
result = ord(aa) - ord(bb)
|
result = ord(aa) - ord(bb)
|
||||||
if (result != 0) or (aa == '\0'): break
|
if (result != 0) or (aa == '\0'): break
|
||||||
inc(i)
|
inc(i)
|
||||||
inc(j)
|
inc(j)
|
||||||
if result == 0:
|
if result == 0:
|
||||||
if a[i] != '\0': result = 1
|
if a[i] != '\0': result = 1
|
||||||
|
|
||||||
proc getIdent*(ic: IdentCache; identifier: cstring, length: int, h: Hash): PIdent =
|
var wordCounter = 1
|
||||||
var idx = h and high(ic.buckets)
|
|
||||||
result = ic.buckets[idx]
|
proc getIdent*(identifier: cstring, length: int, h: THash): PIdent =
|
||||||
|
var idx = h and high(buckets)
|
||||||
|
result = buckets[idx]
|
||||||
var last: PIdent = nil
|
var last: PIdent = nil
|
||||||
var id = 0
|
var id = 0
|
||||||
while result != nil:
|
while result != nil:
|
||||||
if cmpExact(cstring(result.s), identifier, length) == 0:
|
if cmpExact(cstring(result.s), identifier, length) == 0:
|
||||||
if last != nil:
|
if last != nil:
|
||||||
# make access to last looked up identifier faster:
|
# make access to last looked up identifier faster:
|
||||||
last.next = result.next
|
last.next = result.next
|
||||||
result.next = ic.buckets[idx]
|
result.next = buckets[idx]
|
||||||
ic.buckets[idx] = result
|
buckets[idx] = result
|
||||||
return
|
return
|
||||||
elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
|
elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
|
||||||
assert((id == 0) or (id == result.id))
|
assert((id == 0) or (id == result.id))
|
||||||
id = result.id
|
id = result.id
|
||||||
|
|
@ -87,37 +86,25 @@ proc getIdent*(ic: IdentCache; identifier: cstring, length: int, h: Hash): PIden
|
||||||
new(result)
|
new(result)
|
||||||
result.h = h
|
result.h = h
|
||||||
result.s = newString(length)
|
result.s = newString(length)
|
||||||
for i in 0..<length: result.s[i] = identifier[i]
|
for i in countup(0, length - 1): result.s[i] = identifier[i]
|
||||||
result.next = ic.buckets[idx]
|
result.next = buckets[idx]
|
||||||
ic.buckets[idx] = result
|
buckets[idx] = result
|
||||||
if id == 0:
|
if id == 0:
|
||||||
inc(ic.wordCounter)
|
inc(wordCounter)
|
||||||
result.id = -ic.wordCounter
|
result.id = -wordCounter
|
||||||
else:
|
else:
|
||||||
result.id = id
|
result.id = id
|
||||||
|
|
||||||
proc getIdent*(ic: IdentCache; identifier: string): PIdent =
|
proc getIdent*(identifier: string): PIdent =
|
||||||
result = getIdent(ic, cstring(identifier), identifier.len,
|
result = getIdent(cstring(identifier), len(identifier),
|
||||||
hashIgnoreStyle(identifier))
|
hashIgnoreStyle(identifier))
|
||||||
|
|
||||||
proc getIdent*(ic: IdentCache; identifier: string, h: Hash): PIdent =
|
proc getIdent*(identifier: string, h: THash): PIdent =
|
||||||
result = getIdent(ic, cstring(identifier), identifier.len, h)
|
result = getIdent(cstring(identifier), len(identifier), h)
|
||||||
|
|
||||||
proc newIdentCache*(): IdentCache =
|
proc identEq*(id: PIdent, name: string): bool =
|
||||||
result = IdentCache()
|
result = id.id == getIdent(name).id
|
||||||
result.idAnon = result.getIdent":anonymous"
|
|
||||||
result.wordCounter = 1
|
|
||||||
result.idDelegator = result.getIdent":delegator"
|
|
||||||
result.emptyIdent = result.getIdent("")
|
|
||||||
# initialize the keywords:
|
|
||||||
for s in succ(low(TSpecialWord))..high(TSpecialWord):
|
|
||||||
result.getIdent($s, hashIgnoreStyle($s)).id = ord(s)
|
|
||||||
|
|
||||||
proc whichKeyword*(id: PIdent): TSpecialWord =
|
var idAnon* = getIdent":anonymous"
|
||||||
if id.id < 0: result = wInvalid
|
let idDelegator* = getIdent":delegator"
|
||||||
else: result = TSpecialWord(id.id)
|
|
||||||
|
|
||||||
proc hash*(x: PIdent): Hash {.inline.} = x.h
|
|
||||||
proc `==`*(a, b: PIdent): bool {.inline.} =
|
|
||||||
if a.isNil or b.isNil: result = system.`==`(a, b)
|
|
||||||
else: result = a.id == b.id
|
|
||||||
|
|
|
||||||
65
compiler/idgen.nim
Normal file
65
compiler/idgen.nim
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# The Nim Compiler
|
||||||
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
## This module contains a simple persistent id generator.
|
||||||
|
|
||||||
|
import idents, strutils, os, options
|
||||||
|
|
||||||
|
var gFrontEndId, gBackendId*: int
|
||||||
|
|
||||||
|
const
|
||||||
|
debugIds* = false
|
||||||
|
|
||||||
|
when debugIds:
|
||||||
|
import intsets
|
||||||
|
|
||||||
|
var usedIds = initIntSet()
|
||||||
|
|
||||||
|
proc registerID*(id: PIdObj) =
|
||||||
|
when debugIds:
|
||||||
|
if id.id == -1 or containsOrIncl(usedIds, id.id):
|
||||||
|
internalError("ID already used: " & $id.id)
|
||||||
|
|
||||||
|
proc getID*(): int {.inline.} =
|
||||||
|
result = gFrontEndId
|
||||||
|
inc(gFrontEndId)
|
||||||
|
|
||||||
|
proc backendId*(): int {.inline.} =
|
||||||
|
result = gBackendId
|
||||||
|
inc(gBackendId)
|
||||||
|
|
||||||
|
proc setId*(id: int) {.inline.} =
|
||||||
|
gFrontEndId = max(gFrontEndId, id + 1)
|
||||||
|
|
||||||
|
proc idSynchronizationPoint*(idRange: int) =
|
||||||
|
gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1
|
||||||
|
|
||||||
|
proc toGid(f: string): string =
|
||||||
|
# we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this
|
||||||
|
# will cause strange bugs if multiple projects are in the same folder, so
|
||||||
|
# we simply use a project independent name:
|
||||||
|
result = options.completeGeneratedFilePath("nimrod.gid")
|
||||||
|
|
||||||
|
proc saveMaxIds*(project: string) =
|
||||||
|
var f = open(project.toGid, fmWrite)
|
||||||
|
f.writeln($gFrontEndId)
|
||||||
|
f.writeln($gBackendId)
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
proc loadMaxIds*(project: string) =
|
||||||
|
var f: File
|
||||||
|
if open(f, project.toGid, fmRead):
|
||||||
|
var line = newStringOfCap(20)
|
||||||
|
if f.readLine(line):
|
||||||
|
var frontEndId = parseInt(line)
|
||||||
|
if f.readLine(line):
|
||||||
|
var backEndId = parseInt(line)
|
||||||
|
gFrontEndId = max(gFrontEndId, frontEndId)
|
||||||
|
gBackendId = max(gBackendId, backEndId)
|
||||||
|
f.close()
|
||||||
|
|
@ -7,211 +7,129 @@
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## This module implements the symbol importing mechanism.
|
# This module implements the symbol importing mechanism.
|
||||||
|
|
||||||
import
|
import
|
||||||
intsets, ast, astalgo, msgs, options, idents, lookups,
|
intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
|
||||||
semdata, modulepaths, sigmatch, lineinfos, sets,
|
semdata, passes, renderer
|
||||||
modulegraphs, wordrecg, tables
|
|
||||||
from strutils import `%`
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
proc evalImport*(c: PContext, n: PNode): PNode
|
||||||
import std/assertions
|
proc evalFrom*(c: PContext, n: PNode): PNode
|
||||||
|
|
||||||
proc readExceptSet*(c: PContext, n: PNode): IntSet =
|
proc getModuleName*(n: PNode): string =
|
||||||
assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
|
# This returns a short relative module name without the nim extension
|
||||||
result = initIntSet()
|
# e.g. like "system", "importer" or "somepath/module"
|
||||||
for i in 1..<n.len:
|
# The proc won't perform any checks that the path is actually valid
|
||||||
let ident = lookups.considerQuotedIdent(c, n[i])
|
case n.kind
|
||||||
result.incl(ident.id)
|
of nkStrLit, nkRStrLit, nkTripleStrLit:
|
||||||
|
result = unixToNativePath(n.strVal)
|
||||||
|
of nkIdent:
|
||||||
|
result = n.ident.s
|
||||||
|
of nkSym:
|
||||||
|
result = n.sym.name.s
|
||||||
|
of nkInfix, nkPrefix:
|
||||||
|
if n.sons[0].kind == nkIdent and n.sons[0].ident.id == getIdent("as").id:
|
||||||
|
# XXX hack ahead:
|
||||||
|
n.kind = nkImportAs
|
||||||
|
n.sons[0] = n.sons[1]
|
||||||
|
n.sons[1] = n.sons[2]
|
||||||
|
n.sons.setLen(2)
|
||||||
|
return getModuleName(n.sons[0])
|
||||||
|
# hacky way to implement 'x / y /../ z':
|
||||||
|
result = renderTree(n, {renderNoComments}).replace(" ")
|
||||||
|
of nkDotExpr:
|
||||||
|
result = renderTree(n, {renderNoComments}).replace(".", "/")
|
||||||
|
of nkImportAs:
|
||||||
|
result = getModuleName(n.sons[0])
|
||||||
|
else:
|
||||||
|
localError(n.info, errGenerated, "invalid module name: '$1'" % n.renderTree)
|
||||||
|
result = ""
|
||||||
|
|
||||||
proc declarePureEnumField*(c: PContext; s: PSym) =
|
proc checkModuleName*(n: PNode): int32 =
|
||||||
# XXX Remove the outer 'if' statement and see what breaks.
|
# This returns the full canonical path for a given module import
|
||||||
var amb = false
|
let modulename = n.getModuleName
|
||||||
if someSymFromImportTable(c, s.name, amb) == nil:
|
let fullPath = findModule(modulename, n.info.toFullPath)
|
||||||
strTableAdd(c.pureEnumFields, s)
|
if fullPath.len == 0:
|
||||||
when false:
|
localError(n.info, errCannotOpenFile, modulename)
|
||||||
let checkB = strTableGet(c.pureEnumFields, s.name)
|
result = InvalidFileIDX
|
||||||
if checkB == nil:
|
else:
|
||||||
strTableAdd(c.pureEnumFields, s)
|
result = fullPath.fileInfoIdx
|
||||||
when false:
|
|
||||||
# mark as ambiguous:
|
|
||||||
incl(c.ambiguousSymbols, checkB.id)
|
|
||||||
incl(c.ambiguousSymbols, s.id)
|
|
||||||
|
|
||||||
proc importPureEnumField(c: PContext; s: PSym) =
|
proc rawImportSymbol(c: PContext, s: PSym) =
|
||||||
var amb = false
|
|
||||||
if someSymFromImportTable(c, s.name, amb) == nil:
|
|
||||||
strTableAdd(c.pureEnumFields, s)
|
|
||||||
when false:
|
|
||||||
let checkB = strTableGet(c.pureEnumFields, s.name)
|
|
||||||
if checkB == nil:
|
|
||||||
strTableAdd(c.pureEnumFields, s)
|
|
||||||
when false:
|
|
||||||
# mark as ambiguous:
|
|
||||||
incl(c.ambiguousSymbols, checkB.id)
|
|
||||||
incl(c.ambiguousSymbols, s.id)
|
|
||||||
|
|
||||||
proc importPureEnumFields(c: PContext; s: PSym; etyp: PType) =
|
|
||||||
assert sfPure in s.flags
|
|
||||||
for j in 0..<etyp.n.len:
|
|
||||||
var e = etyp.n[j].sym
|
|
||||||
if e.kind != skEnumField:
|
|
||||||
internalError(c.config, s.info, "rawImportSymbol")
|
|
||||||
# BUGFIX: because of aliases for enums the symbol may already
|
|
||||||
# have been put into the symbol table
|
|
||||||
# BUGFIX: but only iff they are the same symbols!
|
|
||||||
for check in importedItems(c, e.name):
|
|
||||||
if check.id == e.id:
|
|
||||||
e = nil
|
|
||||||
break
|
|
||||||
if e != nil:
|
|
||||||
importPureEnumField(c, e)
|
|
||||||
|
|
||||||
proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) =
|
|
||||||
# This does not handle stubs, because otherwise loading on demand would be
|
# This does not handle stubs, because otherwise loading on demand would be
|
||||||
# pointless in practice. So importing stubs is fine here!
|
# pointless in practice. So importing stubs is fine here!
|
||||||
# check if we have already a symbol of the same name:
|
# check if we have already a symbol of the same name:
|
||||||
when false:
|
var check = strTableGet(c.importTable.symbols, s.name)
|
||||||
var check = someSymFromImportTable(c, s.name)
|
if check != nil and check.id != s.id:
|
||||||
if check != nil and check.id != s.id:
|
if s.kind notin OverloadableSyms:
|
||||||
if s.kind notin OverloadableSyms or check.kind notin OverloadableSyms:
|
# s and check need to be qualified:
|
||||||
# s and check need to be qualified:
|
incl(c.ambiguousSymbols, s.id)
|
||||||
incl(c.ambiguousSymbols, s.id)
|
incl(c.ambiguousSymbols, check.id)
|
||||||
incl(c.ambiguousSymbols, check.id)
|
|
||||||
# thanks to 'export' feature, it could be we import the same symbol from
|
# thanks to 'export' feature, it could be we import the same symbol from
|
||||||
# multiple sources, so we need to call 'strTableAdd' here:
|
# multiple sources, so we need to call 'StrTableAdd' here:
|
||||||
when false:
|
strTableAdd(c.importTable.symbols, s)
|
||||||
# now lazy. Speeds up the compiler and is a prerequisite for IC.
|
|
||||||
strTableAdd(c.importTable.symbols, s)
|
|
||||||
else:
|
|
||||||
importSet.incl s.id
|
|
||||||
if s.kind == skType:
|
if s.kind == skType:
|
||||||
var etyp = s.typ
|
var etyp = s.typ
|
||||||
if etyp.kind in {tyBool, tyEnum}:
|
if etyp.kind in {tyBool, tyEnum} and sfPure notin s.flags:
|
||||||
for j in 0..<etyp.n.len:
|
for j in countup(0, sonsLen(etyp.n) - 1):
|
||||||
var e = etyp.n[j].sym
|
var e = etyp.n.sons[j].sym
|
||||||
if e.kind != skEnumField:
|
if e.kind != skEnumField:
|
||||||
internalError(c.config, s.info, "rawImportSymbol")
|
internalError(s.info, "rawImportSymbol")
|
||||||
# BUGFIX: because of aliases for enums the symbol may already
|
# BUGFIX: because of aliases for enums the symbol may already
|
||||||
# have been put into the symbol table
|
# have been put into the symbol table
|
||||||
# BUGFIX: but only iff they are the same symbols!
|
# BUGFIX: but only iff they are the same symbols!
|
||||||
for check in importedItems(c, e.name):
|
var it: TIdentIter
|
||||||
|
check = initIdentIter(it, c.importTable.symbols, e.name)
|
||||||
|
while check != nil:
|
||||||
if check.id == e.id:
|
if check.id == e.id:
|
||||||
e = nil
|
e = nil
|
||||||
break
|
break
|
||||||
|
check = nextIdentIter(it, c.importTable.symbols)
|
||||||
if e != nil:
|
if e != nil:
|
||||||
if sfPure notin s.flags:
|
rawImportSymbol(c, e)
|
||||||
rawImportSymbol(c, e, origin, importSet)
|
|
||||||
else:
|
|
||||||
importPureEnumField(c, e)
|
|
||||||
else:
|
else:
|
||||||
if s.kind == skConverter: addConverter(c, LazySym(sym: s))
|
# rodgen assures that converters and patterns are no stubs
|
||||||
if hasPattern(s): addPattern(c, LazySym(sym: s))
|
if s.kind == skConverter: addConverter(c, s)
|
||||||
if s.owner != origin:
|
if hasPattern(s): addPattern(c, s)
|
||||||
c.exportIndirections.incl((origin.id, s.id))
|
|
||||||
|
|
||||||
proc splitPragmas(c: PContext, n: PNode): (PNode, seq[TSpecialWord]) =
|
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
|
||||||
template bail = globalError(c.config, n.info, "invalid pragma")
|
let ident = lookups.considerQuotedIdent(n)
|
||||||
if n.kind == nkPragmaExpr:
|
let s = strTableGet(fromMod.tab, ident)
|
||||||
if n.len == 2 and n[1].kind == nkPragma:
|
|
||||||
result[0] = n[0]
|
|
||||||
for ni in n[1]:
|
|
||||||
if ni.kind == nkIdent: result[1].add whichKeyword(ni.ident)
|
|
||||||
else: bail()
|
|
||||||
else: bail()
|
|
||||||
else:
|
|
||||||
result[0] = n
|
|
||||||
if result[0].safeLen > 0:
|
|
||||||
(result[0][^1], result[1]) = splitPragmas(c, result[0][^1])
|
|
||||||
|
|
||||||
proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
|
|
||||||
let (n, kws) = splitPragmas(c, n)
|
|
||||||
if kws.len > 0:
|
|
||||||
globalError(c.config, n.info, "unexpected pragma")
|
|
||||||
|
|
||||||
let ident = lookups.considerQuotedIdent(c, n)
|
|
||||||
let s = someSym(c.graph, fromMod, ident)
|
|
||||||
if s == nil:
|
if s == nil:
|
||||||
errorUndeclaredIdentifier(c, n.info, ident.s)
|
localError(n.info, errUndeclaredIdentifier, ident.s)
|
||||||
else:
|
else:
|
||||||
when false:
|
if s.kind == skStub: loadStub(s)
|
||||||
if s.kind == skStub: loadStub(s)
|
if s.kind notin ExportableSymKinds:
|
||||||
let multiImport = s.kind notin ExportableSymKinds or s.kind in skProcKinds
|
internalError(n.info, "importSymbol: 2")
|
||||||
# for an enumeration we have to add all identifiers
|
# for an enumeration we have to add all identifiers
|
||||||
if multiImport:
|
case s.kind
|
||||||
|
of skProcKinds:
|
||||||
# for a overloadable syms add all overloaded routines
|
# for a overloadable syms add all overloaded routines
|
||||||
var it: ModuleIter
|
var it: TIdentIter
|
||||||
var e = initModuleIter(it, c.graph, fromMod, s.name)
|
var e = initIdentIter(it, fromMod.tab, s.name)
|
||||||
while e != nil:
|
while e != nil:
|
||||||
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
|
if e.name.id != s.name.id: internalError(n.info, "importSymbol: 3")
|
||||||
if s.kind in ExportableSymKinds:
|
rawImportSymbol(c, e)
|
||||||
rawImportSymbol(c, e, fromMod, importSet)
|
e = nextIdentIter(it, fromMod.tab)
|
||||||
e = nextModuleIter(it, c.graph)
|
else: rawImportSymbol(c, s)
|
||||||
else:
|
|
||||||
rawImportSymbol(c, s, fromMod, importSet)
|
|
||||||
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
|
|
||||||
|
|
||||||
proc addImport(c: PContext; im: sink ImportedModule) =
|
|
||||||
for i in 0..high(c.imports):
|
|
||||||
if c.imports[i].m == im.m:
|
|
||||||
# we have already imported the module: Check which import
|
|
||||||
# is more "powerful":
|
|
||||||
case c.imports[i].mode
|
|
||||||
of importAll: discard "already imported all symbols"
|
|
||||||
of importSet:
|
|
||||||
case im.mode
|
|
||||||
of importAll, importExcept:
|
|
||||||
# XXX: slightly wrong semantics for 'importExcept'...
|
|
||||||
# But we should probably change the spec and disallow this case.
|
|
||||||
c.imports[i] = im
|
|
||||||
of importSet:
|
|
||||||
# merge the import sets:
|
|
||||||
c.imports[i].imported.incl im.imported
|
|
||||||
of importExcept:
|
|
||||||
case im.mode
|
|
||||||
of importAll:
|
|
||||||
c.imports[i] = im
|
|
||||||
of importSet:
|
|
||||||
discard
|
|
||||||
of importExcept:
|
|
||||||
var cut = initIntSet()
|
|
||||||
# only exclude what is consistent between the two sets:
|
|
||||||
for j in im.exceptSet:
|
|
||||||
if j in c.imports[i].exceptSet:
|
|
||||||
cut.incl j
|
|
||||||
c.imports[i].exceptSet = cut
|
|
||||||
return
|
|
||||||
c.imports.add im
|
|
||||||
|
|
||||||
template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
|
|
||||||
for it in mitems c.graph.ifaces[fromMod.position].converters:
|
|
||||||
if filter:
|
|
||||||
loadPackedSym(c.graph, it)
|
|
||||||
if sfExported in it.sym.flags:
|
|
||||||
addConverter(c, it)
|
|
||||||
for it in mitems c.graph.ifaces[fromMod.position].patterns:
|
|
||||||
if filter:
|
|
||||||
loadPackedSym(c.graph, it)
|
|
||||||
if sfExported in it.sym.flags:
|
|
||||||
addPattern(c, it)
|
|
||||||
for it in mitems c.graph.ifaces[fromMod.position].pureEnums:
|
|
||||||
if filter:
|
|
||||||
loadPackedSym(c.graph, it)
|
|
||||||
importPureEnumFields(c, it.sym, it.sym.typ)
|
|
||||||
|
|
||||||
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
|
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
|
||||||
c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
|
var i: TTabIter
|
||||||
addUnnamedIt(c, fromMod, it.sym.name.id notin exceptSet)
|
var s = initTabIter(i, fromMod.tab)
|
||||||
|
while s != nil:
|
||||||
|
if s.kind != skModule:
|
||||||
|
if s.kind != skEnumField:
|
||||||
|
if s.kind notin ExportableSymKinds:
|
||||||
|
internalError(s.info, "importAllSymbols: " & $s.kind)
|
||||||
|
if exceptSet.empty or s.name.id notin exceptSet:
|
||||||
|
rawImportSymbol(c, s)
|
||||||
|
s = nextIter(i, fromMod.tab)
|
||||||
|
|
||||||
proc importAllSymbols*(c: PContext, fromMod: PSym) =
|
proc importAllSymbols*(c: PContext, fromMod: PSym) =
|
||||||
c.addImport ImportedModule(m: fromMod, mode: importAll)
|
var exceptSet: IntSet
|
||||||
addUnnamedIt(c, fromMod, true)
|
importAllSymbolsExcept(c, fromMod, exceptSet)
|
||||||
when false:
|
|
||||||
var exceptSet: IntSet
|
|
||||||
importAllSymbolsExcept(c, fromMod, exceptSet)
|
|
||||||
|
|
||||||
proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; importSet: var IntSet) =
|
proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
|
||||||
if n.isNil: return
|
if n.isNil: return
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkExportStmt:
|
of nkExportStmt:
|
||||||
|
|
@ -220,151 +138,64 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
|
||||||
let s = a.sym
|
let s = a.sym
|
||||||
if s.kind == skModule:
|
if s.kind == skModule:
|
||||||
importAllSymbolsExcept(c, s, exceptSet)
|
importAllSymbolsExcept(c, s, exceptSet)
|
||||||
elif exceptSet.isNil or s.name.id notin exceptSet:
|
elif exceptSet.empty or s.name.id notin exceptSet:
|
||||||
rawImportSymbol(c, s, fromMod, importSet)
|
rawImportSymbol(c, s)
|
||||||
of nkExportExceptStmt:
|
of nkExportExceptStmt:
|
||||||
localError(c.config, n.info, "'export except' not implemented")
|
localError(n.info, errGenerated, "'export except' not implemented")
|
||||||
else:
|
else:
|
||||||
for i in 0..n.safeLen-1:
|
for i in 0..safeLen(n)-1:
|
||||||
importForwarded(c, n[i], exceptSet, fromMod, importSet)
|
importForwarded(c, n.sons[i], exceptSet)
|
||||||
|
|
||||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
|
proc importModuleAs(n: PNode, realModule: PSym): PSym =
|
||||||
result = realModule
|
result = realModule
|
||||||
template createModuleAliasImpl(ident): untyped =
|
|
||||||
createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options)
|
|
||||||
if n.kind != nkImportAs: discard
|
if n.kind != nkImportAs: discard
|
||||||
elif n.len != 2 or n[1].kind != nkIdent:
|
elif n.len != 2 or n.sons[1].kind != nkIdent:
|
||||||
localError(c.config, n.info, "module alias must be an identifier")
|
localError(n.info, errGenerated, "module alias must be an identifier")
|
||||||
elif n[1].ident.id != realModule.name.id:
|
elif n.sons[1].ident.id != realModule.name.id:
|
||||||
# some misguided guy will write 'import abc.foo as foo' ...
|
# some misguided guy will write 'import abc.foo as foo' ...
|
||||||
result = createModuleAliasImpl(n[1].ident)
|
result = createModuleAlias(realModule, n.sons[1].ident, realModule.info)
|
||||||
if result == realModule:
|
|
||||||
# avoids modifying `realModule`, see D20201209T194412 for `import {.all.}`
|
|
||||||
result = createModuleAliasImpl(realModule.name)
|
|
||||||
if importHidden:
|
|
||||||
result.options.incl optImportHidden
|
|
||||||
c.unusedImports.add((result, n.info))
|
|
||||||
c.importModuleMap[result.id] = realModule.id
|
|
||||||
|
|
||||||
proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
|
proc myImportModule(c: PContext, n: PNode): PSym =
|
||||||
var ret: typeof(result)
|
var f = checkModuleName(n)
|
||||||
proc processPragma(n2: PNode): PNode =
|
if f != InvalidFileIDX:
|
||||||
let (result2, kws) = splitPragmas(c, n2)
|
result = importModuleAs(n, gImportModule(c.module, f))
|
||||||
result = result2
|
if result.info.fileIndex == n.info.fileIndex:
|
||||||
for ai in kws:
|
localError(n.info, errGenerated, "A module cannot import itself")
|
||||||
case ai
|
if sfDeprecated in result.flags:
|
||||||
of wImportHidden: ret.importHidden = true
|
message(n.info, warnDeprecated, result.name.s)
|
||||||
else: globalError(c.config, n.info, "invalid pragma, expected: " & ${wImportHidden})
|
|
||||||
|
|
||||||
if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as":
|
proc evalImport(c: PContext, n: PNode): PNode =
|
||||||
ret.node = newNodeI(nkImportAs, n.info)
|
result = n
|
||||||
ret.node.add n[1].processPragma
|
var emptySet: IntSet
|
||||||
ret.node.add n[2]
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
else:
|
var m = myImportModule(c, n.sons[i])
|
||||||
ret.node = n.processPragma
|
if m != nil:
|
||||||
return ret
|
# ``addDecl`` needs to be done before ``importAllSymbols``!
|
||||||
|
addDecl(c, m) # add symbol to symbol table of module
|
||||||
|
importAllSymbolsExcept(c, m, emptySet)
|
||||||
|
#importForwarded(c, m.ast, emptySet)
|
||||||
|
|
||||||
proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
proc evalFrom(c: PContext, n: PNode): PNode =
|
||||||
let transf = transformImportAs(c, n)
|
result = n
|
||||||
n = transf.node
|
checkMinSonsLen(n, 2)
|
||||||
let f = checkModuleName(c.config, n)
|
var m = myImportModule(c, n.sons[0])
|
||||||
if f != InvalidFileIdx:
|
|
||||||
addImportFileDep(c, f)
|
|
||||||
let L = c.graph.importStack.len
|
|
||||||
let recursion = c.graph.importStack.find(f)
|
|
||||||
c.graph.importStack.add f
|
|
||||||
#echo "adding ", toFullPath(f), " at ", L+1
|
|
||||||
if recursion >= 0:
|
|
||||||
var err = ""
|
|
||||||
for i in recursion..<L:
|
|
||||||
if i > recursion: err.add "\n"
|
|
||||||
err.add toFullPath(c.config, c.graph.importStack[i]) & " imports " &
|
|
||||||
toFullPath(c.config, c.graph.importStack[i+1])
|
|
||||||
c.recursiveDep = err
|
|
||||||
|
|
||||||
var realModule: PSym
|
|
||||||
discard pushOptionEntry(c)
|
|
||||||
realModule = c.graph.importModuleCallback(c.graph, c.module, f)
|
|
||||||
result = importModuleAs(c, n, realModule, transf.importHidden)
|
|
||||||
popOptionEntry(c)
|
|
||||||
|
|
||||||
#echo "set back to ", L
|
|
||||||
c.graph.importStack.setLen(L)
|
|
||||||
# we cannot perform this check reliably because of
|
|
||||||
# test: modules/import_in_config) # xxx is that still true?
|
|
||||||
if realModule == c.module:
|
|
||||||
localError(c.config, n.info, "module '$1' cannot import itself" % realModule.name.s)
|
|
||||||
if sfDeprecated in realModule.flags:
|
|
||||||
var prefix = ""
|
|
||||||
if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
|
|
||||||
message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
|
|
||||||
suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
|
|
||||||
importStmtResult.add newSymNode(result, n.info)
|
|
||||||
#newStrNode(toFullPath(c.config, f), n.info)
|
|
||||||
|
|
||||||
proc afterImport(c: PContext, m: PSym) =
|
|
||||||
# fixes bug #17510, for re-exported symbols
|
|
||||||
let realModuleId = c.importModuleMap[m.id]
|
|
||||||
for s in allSyms(c.graph, m):
|
|
||||||
if s.owner.id != realModuleId:
|
|
||||||
c.exportIndirections.incl((m.id, s.id))
|
|
||||||
|
|
||||||
proc impMod(c: PContext; it: PNode; importStmtResult: PNode) =
|
|
||||||
var it = it
|
|
||||||
let m = myImportModule(c, it, importStmtResult)
|
|
||||||
if m != nil:
|
if m != nil:
|
||||||
# ``addDecl`` needs to be done before ``importAllSymbols``!
|
n.sons[0] = newSymNode(m)
|
||||||
addDecl(c, m, it.info) # add symbol to symbol table of module
|
addDecl(c, m) # add symbol to symbol table of module
|
||||||
importAllSymbols(c, m)
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
#importForwarded(c, m.ast, emptySet, m)
|
if n.sons[i].kind != nkNilLit:
|
||||||
afterImport(c, m)
|
importSymbol(c, n.sons[i], m)
|
||||||
|
|
||||||
proc evalImport*(c: PContext, n: PNode): PNode =
|
|
||||||
result = newNodeI(nkImportStmt, n.info)
|
|
||||||
for i in 0..<n.len:
|
|
||||||
let it = n[i]
|
|
||||||
if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
|
|
||||||
let sep = it[0]
|
|
||||||
let dir = it[1]
|
|
||||||
var imp = newNodeI(nkInfix, it.info)
|
|
||||||
imp.add sep
|
|
||||||
imp.add dir
|
|
||||||
imp.add sep # dummy entry, replaced in the loop
|
|
||||||
for x in it[2]:
|
|
||||||
# transform `a/b/[c as d]` to `/a/b/c as d`
|
|
||||||
if x.kind == nkInfix and x[0].ident.s == "as":
|
|
||||||
let impAs = copyTree(x)
|
|
||||||
imp[2] = x[1]
|
|
||||||
impAs[1] = imp
|
|
||||||
impMod(c, imp, result)
|
|
||||||
else:
|
|
||||||
imp[2] = x
|
|
||||||
impMod(c, imp, result)
|
|
||||||
else:
|
|
||||||
impMod(c, it, result)
|
|
||||||
|
|
||||||
proc evalFrom*(c: PContext, n: PNode): PNode =
|
|
||||||
result = newNodeI(nkImportStmt, n.info)
|
|
||||||
checkMinSonsLen(n, 2, c.config)
|
|
||||||
var m = myImportModule(c, n[0], result)
|
|
||||||
if m != nil:
|
|
||||||
n[0] = newSymNode(m)
|
|
||||||
addDecl(c, m, n.info) # add symbol to symbol table of module
|
|
||||||
|
|
||||||
var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
|
|
||||||
for i in 1..<n.len:
|
|
||||||
if n[i].kind != nkNilLit:
|
|
||||||
importSymbol(c, n[i], m, im.imported)
|
|
||||||
c.addImport im
|
|
||||||
afterImport(c, m)
|
|
||||||
|
|
||||||
proc evalImportExcept*(c: PContext, n: PNode): PNode =
|
proc evalImportExcept*(c: PContext, n: PNode): PNode =
|
||||||
result = newNodeI(nkImportStmt, n.info)
|
result = n
|
||||||
checkMinSonsLen(n, 2, c.config)
|
checkMinSonsLen(n, 2)
|
||||||
var m = myImportModule(c, n[0], result)
|
var m = myImportModule(c, n.sons[0])
|
||||||
if m != nil:
|
if m != nil:
|
||||||
n[0] = newSymNode(m)
|
n.sons[0] = newSymNode(m)
|
||||||
addDecl(c, m, n.info) # add symbol to symbol table of module
|
addDecl(c, m) # add symbol to symbol table of module
|
||||||
importAllSymbolsExcept(c, m, readExceptSet(c, n))
|
var exceptSet = initIntSet()
|
||||||
#importForwarded(c, m.ast, exceptSet, m)
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
afterImport(c, m)
|
let ident = lookups.considerQuotedIdent(n.sons[i])
|
||||||
|
exceptSet.incl(ident.id)
|
||||||
|
importAllSymbolsExcept(c, m, exceptSet)
|
||||||
|
#importForwarded(c, m.ast, exceptSet)
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
##[
|
|
||||||
This module only exists to generate docs for the compiler.
|
|
||||||
|
|
||||||
## links
|
|
||||||
* [main docs](../lib.html)
|
|
||||||
* [compiler user guide](../nimc.html)
|
|
||||||
* [Internals of the Nim Compiler](../intern.html)
|
|
||||||
]##
|
|
||||||
|
|
||||||
#[
|
|
||||||
note: this is named `index` so that navigating to https://nim-lang.github.io/Nim/compiler/
|
|
||||||
will work.
|
|
||||||
|
|
||||||
xxx this should also import other modules, not transitively imported by `compiler/nim.nim`,
|
|
||||||
eg `evalffi`, otherwise these aren't shown. A glob could be used at CT.
|
|
||||||
]#
|
|
||||||
|
|
||||||
import nim
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -6,17 +6,13 @@ Name: "Nim"
|
||||||
Version: "$version"
|
Version: "$version"
|
||||||
Platforms: """
|
Platforms: """
|
||||||
windows: i386;amd64
|
windows: i386;amd64
|
||||||
linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64
|
linux: i386;amd64;powerpc64;arm;sparc;mips;powerpc
|
||||||
macosx: i386;amd64;powerpc64;arm64
|
macosx: i386;amd64;powerpc64
|
||||||
solaris: i386;amd64;sparc;sparc64
|
solaris: i386;amd64;sparc
|
||||||
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
|
freebsd: i386;amd64
|
||||||
netbsd: i386;amd64
|
netbsd: i386;amd64
|
||||||
openbsd: i386;amd64;arm;arm64
|
openbsd: i386;amd64
|
||||||
dragonfly: i386;amd64
|
|
||||||
crossos: amd64
|
|
||||||
haiku: i386;amd64
|
haiku: i386;amd64
|
||||||
android: i386;arm;arm64
|
|
||||||
nintendoswitch: arm64
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Authors: "Andreas Rumpf"
|
Authors: "Andreas Rumpf"
|
||||||
|
|
@ -36,20 +32,23 @@ App: Console
|
||||||
License: "copying.txt"
|
License: "copying.txt"
|
||||||
|
|
||||||
[Config]
|
[Config]
|
||||||
Files: "config/*.cfg"
|
Files: "config/nim.cfg"
|
||||||
Files: "config/config.nims"
|
Files: "config/nimdoc.cfg"
|
||||||
|
Files: "config/nimdoc.tex.cfg"
|
||||||
|
|
||||||
[Documentation]
|
[Documentation]
|
||||||
; Files: "doc/*.html"
|
; Files: "doc/*.html"
|
||||||
; Files: "doc/*.cfg"
|
; Files: "doc/*.cfg"
|
||||||
; Files: "doc/*.pdf"
|
; Files: "doc/*.pdf"
|
||||||
; Files: "doc/*.ini"
|
; Files: "doc/*.ini"
|
||||||
Files: "doc/html/overview.html"
|
Files: "doc/overview.html"
|
||||||
Start: "doc/html/overview.html"
|
Start: "doc/overview.html"
|
||||||
|
|
||||||
|
|
||||||
[Other]
|
[Other]
|
||||||
Files: "copying.txt"
|
Files: "readme.txt;install.txt;contributors.txt;copying.txt"
|
||||||
|
Files: "makefile"
|
||||||
|
Files: "*.ini"
|
||||||
Files: "koch.nim"
|
Files: "koch.nim"
|
||||||
|
|
||||||
Files: "icons/nim.ico"
|
Files: "icons/nim.ico"
|
||||||
|
|
@ -61,58 +60,185 @@ Files: "icons/koch.rc"
|
||||||
Files: "icons/koch.res"
|
Files: "icons/koch.res"
|
||||||
Files: "icons/koch_icon.o"
|
Files: "icons/koch_icon.o"
|
||||||
|
|
||||||
Files: "compiler"
|
Files: "compiler/readme.txt"
|
||||||
Files: "doc"
|
Files: "compiler/installer.ini"
|
||||||
Files: "doc/html"
|
Files: "compiler/nim.nim.cfg"
|
||||||
Files: "tools"
|
Files: "compiler/*.nim"
|
||||||
Files: "tools/debug/nim-gdb.py"
|
Files: "doc/*.txt"
|
||||||
Files: "nimpretty"
|
Files: "doc/manual/*.txt"
|
||||||
Files: "testament"
|
Files: "doc/*.nim"
|
||||||
Files: "nimsuggest"
|
Files: "doc/*.cfg"
|
||||||
Files: "nimsuggest/tests/*.nim"
|
Files: "compiler/nimfix/*.nim"
|
||||||
Files: "changelogs/*.md"
|
Files: "compiler/nimfix/*.cfg"
|
||||||
|
Files: "compiler/nimsuggest/*.nim"
|
||||||
|
Files: "compiler/nimsuggest/*.cfg"
|
||||||
|
Files: "compiler/plugins/locals/*.nim"
|
||||||
|
Files: "compiler/plugins/active.nim"
|
||||||
|
Files: "tools/*.nim"
|
||||||
|
Files: "tools/*.cfg"
|
||||||
|
Files: "tools/*.tmpl"
|
||||||
|
Files: "tools/niminst/*.nim"
|
||||||
|
Files: "tools/niminst/*.cfg"
|
||||||
|
Files: "tools/niminst/*.tmpl"
|
||||||
|
Files: "tools/niminst/*.nsh"
|
||||||
|
Files: "web/website.ini"
|
||||||
|
Files: "web/*.nim"
|
||||||
|
Files: "web/*.txt"
|
||||||
|
|
||||||
[Lib]
|
[Lib]
|
||||||
Files: "lib"
|
Files: "lib/nimbase.h"
|
||||||
|
Files: "lib/*.nim"
|
||||||
|
Files: "lib/*.cfg"
|
||||||
|
|
||||||
|
Files: "lib/system/*.nim"
|
||||||
|
Files: "lib/core/*.nim"
|
||||||
|
Files: "lib/pure/*.nim"
|
||||||
|
Files: "lib/pure/*.cfg"
|
||||||
|
Files: "lib/pure/collections/*.nim"
|
||||||
|
Files: "lib/pure/concurrency/*.nim"
|
||||||
|
Files: "lib/pure/unidecode/*.nim"
|
||||||
|
Files: "lib/pure/concurrency/*.cfg"
|
||||||
|
Files: "lib/impure/*.nim"
|
||||||
|
Files: "lib/wrappers/*.nim"
|
||||||
|
|
||||||
|
Files: "lib/wrappers/readline/*.nim"
|
||||||
|
Files: "lib/wrappers/sdl/*.nim"
|
||||||
|
Files: "lib/wrappers/zip/*.nim"
|
||||||
|
Files: "lib/wrappers/zip/libzip_all.c"
|
||||||
|
|
||||||
|
Files: "lib/windows/*.nim"
|
||||||
|
Files: "lib/posix/*.nim"
|
||||||
|
Files: "lib/js/*.nim"
|
||||||
|
Files: "lib/packages/docutils/*.nim"
|
||||||
|
|
||||||
|
|
||||||
[Other]
|
[Other]
|
||||||
Files: "examples"
|
Files: "examples/*.nim"
|
||||||
Files: "dist/nimble"
|
Files: "examples/c++iface/*.nim"
|
||||||
|
Files: "examples/objciface/*.nim"
|
||||||
|
Files: "examples/cross_calculator/"
|
||||||
|
|
||||||
Files: "tests"
|
Files: "examples/*.html"
|
||||||
|
Files: "examples/*.txt"
|
||||||
|
Files: "examples/*.cfg"
|
||||||
|
Files: "examples/*.tmpl"
|
||||||
|
|
||||||
|
Files: "tests/actiontable/*.nim"
|
||||||
|
Files: "tests/alias/*.nim"
|
||||||
|
Files: "tests/ambsym/*.nim"
|
||||||
|
Files: "tests/array/*.nim"
|
||||||
|
Files: "tests/assign/*.nim"
|
||||||
|
Files: "tests/astoverload/*.nim"
|
||||||
|
Files: "tests/async/*.nim"
|
||||||
|
Files: "tests/benchmarks/*.nim"
|
||||||
|
Files: "tests/bind/*.nim"
|
||||||
|
Files: "tests/borrow/*.nim"
|
||||||
|
Files: "tests/casestmt/*.nim"
|
||||||
|
Files: "tests/ccgbugs/*.nim"
|
||||||
|
Files: "tests/clearmsg/*.nim"
|
||||||
|
Files: "tests/closure/*.nim"
|
||||||
|
Files: "tests/cnstseq/*.nim"
|
||||||
|
Files: "tests/collections/*.nim"
|
||||||
|
Files: "tests/compiles/*.nim"
|
||||||
|
Files: "tests/concat/*.nim"
|
||||||
|
Files: "tests/concepts/*.nim"
|
||||||
|
Files: "tests/constr/*.nim"
|
||||||
|
Files: "tests/constraints/*.nim"
|
||||||
|
Files: "tests/controlflow/*.nim"
|
||||||
|
Files: "tests/converter/*.nim"
|
||||||
|
Files: "tests/cpp/*.nim"
|
||||||
|
Files: "tests/defaultprocparam/*.nim"
|
||||||
|
Files: "tests/deprecated/*.nim"
|
||||||
|
Files: "tests/destructor/*.nim"
|
||||||
|
Files: "tests/dir with space/*.nim"
|
||||||
|
Files: "tests/discard/*.nim"
|
||||||
|
Files: "tests/distinct/*.nim"
|
||||||
|
Files: "tests/dll/*.nim"
|
||||||
|
Files: "tests/effects/*.nim"
|
||||||
|
Files: "tests/enum/*.nim"
|
||||||
|
Files: "tests/exception/*.nim"
|
||||||
|
Files: "tests/exprs/*.nim"
|
||||||
|
Files: "tests/fields/*.nim"
|
||||||
|
Files: "tests/float/*.nim"
|
||||||
|
Files: "tests/friends/*.nim"
|
||||||
|
Files: "tests/gc/*.nim"
|
||||||
|
Files: "tests/generics/*.nim"
|
||||||
|
Files: "tests/gensym/*.nim"
|
||||||
|
Files: "tests/global/*.nim"
|
||||||
|
Files: "tests/implicit/*.nim"
|
||||||
|
Files: "tests/init/*.nim"
|
||||||
|
Files: "tests/iter/*.nim"
|
||||||
|
Files: "tests/js/*.nim"
|
||||||
|
Files: "tests/js/*.cfg"
|
||||||
|
Files: "tests/let/*.nim"
|
||||||
|
Files: "tests/lexer/*.nim"
|
||||||
|
Files: "tests/lookups/*.nim"
|
||||||
|
Files: "tests/macros/*.nim"
|
||||||
|
Files: "tests/magics/*.nim"
|
||||||
|
Files: "tests/metatype/*.nim"
|
||||||
|
Files: "tests/method/*.nim"
|
||||||
|
Files: "tests/misc/*.nim"
|
||||||
|
Files: "tests/modules/*.nim"
|
||||||
|
Files: "tests/namedparams/*.nim"
|
||||||
|
Files: "tests/notnil/*.nim"
|
||||||
|
Files: "tests/objects/*.nim"
|
||||||
|
Files: "tests/objvariant/*.nim"
|
||||||
|
Files: "tests/openarray/*.nim"
|
||||||
|
Files: "tests/osproc/*.nim"
|
||||||
|
Files: "tests/overflw/*.nim"
|
||||||
|
Files: "tests/overload/*.nim"
|
||||||
|
Files: "tests/parallel/*.nim"
|
||||||
|
Files: "tests/parallel/*.cfg"
|
||||||
|
Files: "tests/parser/*.nim"
|
||||||
|
Files: "tests/pragmas/*.nim"
|
||||||
|
Files: "tests/proc/*.nim"
|
||||||
|
Files: "tests/procvar/*.nim"
|
||||||
|
Files: "tests/range/*.nim"
|
||||||
|
Files: "tests/rodfiles/*.nim"
|
||||||
|
Files: "tests/seq/*.nim"
|
||||||
|
Files: "tests/sets/*.nim"
|
||||||
|
Files: "tests/showoff/*.nim"
|
||||||
|
Files: "tests/specialops/*.nim"
|
||||||
|
Files: "tests/stdlib/*.nim"
|
||||||
|
Files: "tests/system/*.nim"
|
||||||
|
Files: "tests/template/*.nim"
|
||||||
|
Files: "tests/testament/*.nim"
|
||||||
|
Files: "tests/testdata/*.nim"
|
||||||
|
Files: "tests/threads/*.nim"
|
||||||
|
Files: "tests/threads/*.cfg"
|
||||||
|
Files: "tests/trmacros/*.nim"
|
||||||
|
Files: "tests/tuples/*.nim"
|
||||||
|
Files: "tests/typerel/*.nim"
|
||||||
|
Files: "tests/types/*.nim"
|
||||||
|
Files: "tests/usingstmt/*.nim"
|
||||||
|
Files: "tests/varres/*.nim"
|
||||||
|
Files: "tests/varstmt/*.nim"
|
||||||
|
Files: "tests/vm/*.nim"
|
||||||
|
Files: "tests/readme.txt"
|
||||||
|
Files: "tests/testament/css/*.css"
|
||||||
|
Files: "tests/testament/*.cfg"
|
||||||
|
Files: "lib/pure/unidecode/unidecode.dat"
|
||||||
|
|
||||||
[Windows]
|
[Windows]
|
||||||
Files: "bin/nim.exe"
|
Files: "bin/nim.exe"
|
||||||
|
Files: "bin/c2nim.exe"
|
||||||
Files: "bin/nimgrep.exe"
|
Files: "bin/nimgrep.exe"
|
||||||
Files: "bin/nimsuggest.exe"
|
Files: "bin/nimsuggest.exe"
|
||||||
Files: "bin/nimble.exe"
|
Files: "bin/nimble.exe"
|
||||||
Files: "bin/vccexe.exe"
|
Files: "bin/*.dll"
|
||||||
Files: "bin/nimgrab.exe"
|
|
||||||
Files: "bin/nimpretty.exe"
|
|
||||||
Files: "bin/testament.exe"
|
|
||||||
Files: "bin/nim-gdb.bat"
|
|
||||||
|
|
||||||
|
Files: "dist/*.dll"
|
||||||
Files: "koch.exe"
|
Files: "koch.exe"
|
||||||
Files: "finish.exe"
|
|
||||||
; Files: "bin/downloader.exe"
|
|
||||||
|
|
||||||
; Files: "dist/mingw"
|
; Files: "dist/mingw"
|
||||||
Files: r"tools\start.bat"
|
Files: "start.bat"
|
||||||
BinPath: r"bin;dist\mingw\bin;dist"
|
BinPath: r"bin;dist\mingw\bin;dist"
|
||||||
|
|
||||||
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
|
; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry
|
||||||
Download: r"Documentation|doc|docs.zip|13824|https://nim-lang.org/download/docs-${version}.zip|overview.html"
|
Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html"
|
||||||
Download: r"C Compiler (MingW)|dist|mingw.zip|82944|https://nim-lang.org/download/${mingw}.zip"
|
Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip"
|
||||||
Download: r"Support DLLs|bin|nim_dlls.zip|479|https://nim-lang.org/download/dlls.zip"
|
Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.3.0.zip|aporia-0.3.0\bin\aporia.exe"
|
||||||
Download: r"Aporia Text Editor|dist|aporia.zip|97997|https://nim-lang.org/download/aporia-0.4.0.zip|aporia-0.4.0\bin\aporia.exe"
|
|
||||||
; for now only NSIS supports optional downloads
|
; for now only NSIS supports optional downloads
|
||||||
|
|
||||||
[WinBin]
|
|
||||||
Files: "bin/makelink.exe"
|
|
||||||
Files: "bin/7zG.exe"
|
|
||||||
Files: "bin/*.dll"
|
|
||||||
Files: "bin/cacert.pem"
|
|
||||||
|
|
||||||
[UnixBin]
|
[UnixBin]
|
||||||
Files: "bin/nim"
|
Files: "bin/nim"
|
||||||
|
|
||||||
|
|
@ -120,8 +246,6 @@ Files: "bin/nim"
|
||||||
[Unix]
|
[Unix]
|
||||||
InstallScript: "yes"
|
InstallScript: "yes"
|
||||||
UninstallScript: "yes"
|
UninstallScript: "yes"
|
||||||
Files: "bin/nim-gdb"
|
|
||||||
Files: "build_all.sh"
|
|
||||||
|
|
||||||
|
|
||||||
[InnoSetup]
|
[InnoSetup]
|
||||||
|
|
@ -129,6 +253,7 @@ path = r"c:\Program Files (x86)\Inno Setup 5\iscc.exe"
|
||||||
flags = "/Q"
|
flags = "/Q"
|
||||||
|
|
||||||
[NSIS]
|
[NSIS]
|
||||||
|
path = r"c:\Program Files (x86)\NSIS\makensis.exe"
|
||||||
flags = "/V0"
|
flags = "/V0"
|
||||||
|
|
||||||
[C_Compiler]
|
[C_Compiler]
|
||||||
|
|
@ -141,7 +266,3 @@ buildDepends: "gcc (>= 4:4.3.2)"
|
||||||
pkgDepends: "gcc (>= 4:4.3.2)"
|
pkgDepends: "gcc (>= 4:4.3.2)"
|
||||||
shortDesc: "The Nim Compiler"
|
shortDesc: "The Nim Compiler"
|
||||||
licenses: "bin/nim,MIT;lib/*,MIT;"
|
licenses: "bin/nim,MIT;lib/*,MIT;"
|
||||||
|
|
||||||
[nimble]
|
|
||||||
pkgName: "nim"
|
|
||||||
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css"
|
|
||||||
|
|
|
||||||
|
|
@ -1,574 +0,0 @@
|
||||||
## This module is for compiler internal use only. For reliable error
|
|
||||||
## messages and range checks, the compiler needs a data type that can
|
|
||||||
## hold all from `low(BiggestInt)` to `high(BiggestUInt)`, This
|
|
||||||
## type is for that purpose.
|
|
||||||
|
|
||||||
from math import trunc
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
|
||||||
Int128* = object
|
|
||||||
udata: array[4, uint32]
|
|
||||||
|
|
||||||
template sdata(arg: Int128, idx: int): int32 =
|
|
||||||
# udata and sdata was supposed to be in a union, but unions are
|
|
||||||
# handled incorrectly in the VM.
|
|
||||||
cast[ptr int32](arg.udata[idx].unsafeAddr)[]
|
|
||||||
|
|
||||||
# encoding least significant int first (like LittleEndian)
|
|
||||||
|
|
||||||
const
|
|
||||||
Zero* = Int128(udata: [0'u32, 0, 0, 0])
|
|
||||||
One* = Int128(udata: [1'u32, 0, 0, 0])
|
|
||||||
Ten* = Int128(udata: [10'u32, 0, 0, 0])
|
|
||||||
Min = Int128(udata: [0'u32, 0, 0, 0x80000000'u32])
|
|
||||||
Max = Int128(udata: [high(uint32), high(uint32), high(uint32), uint32(high(int32))])
|
|
||||||
NegOne* = Int128(udata: [0xffffffff'u32, 0xffffffff'u32, 0xffffffff'u32, 0xffffffff'u32])
|
|
||||||
|
|
||||||
template low*(t: typedesc[Int128]): Int128 = Min
|
|
||||||
template high*(t: typedesc[Int128]): Int128 = Max
|
|
||||||
|
|
||||||
proc `$`*(a: Int128): string
|
|
||||||
|
|
||||||
proc toInt128*[T: SomeInteger | bool](arg: T): Int128 =
|
|
||||||
{.noSideEffect.}:
|
|
||||||
when T is bool: result.sdata(0) = int32(arg)
|
|
||||||
elif T is SomeUnsignedInt:
|
|
||||||
when sizeof(arg) <= 4:
|
|
||||||
result.udata[0] = uint32(arg)
|
|
||||||
else:
|
|
||||||
result.udata[0] = uint32(arg and T(0xffffffff))
|
|
||||||
result.udata[1] = uint32(arg shr 32)
|
|
||||||
elif sizeof(arg) <= 4:
|
|
||||||
result.sdata(0) = int32(arg)
|
|
||||||
if arg < 0: # sign extend
|
|
||||||
result.sdata(1) = -1
|
|
||||||
result.sdata(2) = -1
|
|
||||||
result.sdata(3) = -1
|
|
||||||
else:
|
|
||||||
let tmp = int64(arg)
|
|
||||||
result.udata[0] = uint32(tmp and 0xffffffff)
|
|
||||||
result.sdata(1) = int32(tmp shr 32)
|
|
||||||
if arg < 0: # sign extend
|
|
||||||
result.sdata(2) = -1
|
|
||||||
result.sdata(3) = -1
|
|
||||||
|
|
||||||
template isNegative(arg: Int128): bool =
|
|
||||||
arg.sdata(3) < 0
|
|
||||||
|
|
||||||
proc bitconcat(a, b: uint32): uint64 =
|
|
||||||
(uint64(a) shl 32) or uint64(b)
|
|
||||||
|
|
||||||
proc toInt64*(arg: Int128): int64 =
|
|
||||||
if isNegative(arg):
|
|
||||||
assert(arg.sdata(3) == -1, "out of range")
|
|
||||||
assert(arg.sdata(2) == -1, "out of range")
|
|
||||||
else:
|
|
||||||
assert(arg.sdata(3) == 0, "out of range")
|
|
||||||
assert(arg.sdata(2) == 0, "out of range")
|
|
||||||
|
|
||||||
cast[int64](bitconcat(arg.udata[1], arg.udata[0]))
|
|
||||||
|
|
||||||
proc toInt64Checked*(arg: Int128; onError: int64): int64 =
|
|
||||||
if isNegative(arg):
|
|
||||||
if arg.sdata(3) != -1 or arg.sdata(2) != -1:
|
|
||||||
return onError
|
|
||||||
else:
|
|
||||||
if arg.sdata(3) != 0 or arg.sdata(2) != 0:
|
|
||||||
return onError
|
|
||||||
return cast[int64](bitconcat(arg.udata[1], arg.udata[0]))
|
|
||||||
|
|
||||||
proc toInt32*(arg: Int128): int32 =
|
|
||||||
if isNegative(arg):
|
|
||||||
assert(arg.sdata(3) == -1, "out of range")
|
|
||||||
assert(arg.sdata(2) == -1, "out of range")
|
|
||||||
assert(arg.sdata(1) == -1, "out of range")
|
|
||||||
else:
|
|
||||||
assert(arg.sdata(3) == 0, "out of range")
|
|
||||||
assert(arg.sdata(2) == 0, "out of range")
|
|
||||||
assert(arg.sdata(1) == 0, "out of range")
|
|
||||||
|
|
||||||
arg.sdata(0)
|
|
||||||
|
|
||||||
proc toInt16*(arg: Int128): int16 =
|
|
||||||
if isNegative(arg):
|
|
||||||
assert(arg.sdata(3) == -1, "out of range")
|
|
||||||
assert(arg.sdata(2) == -1, "out of range")
|
|
||||||
assert(arg.sdata(1) == -1, "out of range")
|
|
||||||
else:
|
|
||||||
assert(arg.sdata(3) == 0, "out of range")
|
|
||||||
assert(arg.sdata(2) == 0, "out of range")
|
|
||||||
assert(arg.sdata(1) == 0, "out of range")
|
|
||||||
|
|
||||||
int16(arg.sdata(0))
|
|
||||||
|
|
||||||
proc toInt8*(arg: Int128): int8 =
|
|
||||||
if isNegative(arg):
|
|
||||||
assert(arg.sdata(3) == -1, "out of range")
|
|
||||||
assert(arg.sdata(2) == -1, "out of range")
|
|
||||||
assert(arg.sdata(1) == -1, "out of range")
|
|
||||||
else:
|
|
||||||
assert(arg.sdata(3) == 0, "out of range")
|
|
||||||
assert(arg.sdata(2) == 0, "out of range")
|
|
||||||
assert(arg.sdata(1) == 0, "out of range")
|
|
||||||
|
|
||||||
int8(arg.sdata(0))
|
|
||||||
|
|
||||||
proc toInt*(arg: Int128): int =
|
|
||||||
when sizeof(int) == 4:
|
|
||||||
cast[int](toInt32(arg))
|
|
||||||
else:
|
|
||||||
cast[int](toInt64(arg))
|
|
||||||
|
|
||||||
proc toUInt64*(arg: Int128): uint64 =
|
|
||||||
assert(arg.udata[3] == 0)
|
|
||||||
assert(arg.udata[2] == 0)
|
|
||||||
bitconcat(arg.udata[1], arg.udata[0])
|
|
||||||
|
|
||||||
proc toUInt32*(arg: Int128): uint32 =
|
|
||||||
assert(arg.udata[3] == 0)
|
|
||||||
assert(arg.udata[2] == 0)
|
|
||||||
assert(arg.udata[1] == 0)
|
|
||||||
arg.udata[0]
|
|
||||||
|
|
||||||
proc toUInt16*(arg: Int128): uint16 =
|
|
||||||
assert(arg.udata[3] == 0)
|
|
||||||
assert(arg.udata[2] == 0)
|
|
||||||
assert(arg.udata[1] == 0)
|
|
||||||
uint16(arg.udata[0])
|
|
||||||
|
|
||||||
proc toUInt8*(arg: Int128): uint8 =
|
|
||||||
assert(arg.udata[3] == 0)
|
|
||||||
assert(arg.udata[2] == 0)
|
|
||||||
assert(arg.udata[1] == 0)
|
|
||||||
uint8(arg.udata[0])
|
|
||||||
|
|
||||||
proc toUInt*(arg: Int128): uint =
|
|
||||||
when sizeof(int) == 4:
|
|
||||||
cast[uint](toInt32(arg))
|
|
||||||
else:
|
|
||||||
cast[uint](toInt64(arg))
|
|
||||||
|
|
||||||
proc castToInt64*(arg: Int128): int64 =
|
|
||||||
## Conversion to int64 without range check.
|
|
||||||
cast[int64](bitconcat(arg.udata[1], arg.udata[0]))
|
|
||||||
|
|
||||||
proc castToUInt64*(arg: Int128): uint64 =
|
|
||||||
## Conversion to uint64 without range check.
|
|
||||||
cast[uint64](bitconcat(arg.udata[1], arg.udata[0]))
|
|
||||||
|
|
||||||
proc addToHex(result: var string; arg: uint32) =
|
|
||||||
for i in 0..<8:
|
|
||||||
let idx = (arg shr ((7-i) * 4)) and 0xf
|
|
||||||
result.add "0123456789abcdef"[idx]
|
|
||||||
|
|
||||||
proc addToHex*(result: var string; arg: Int128) =
|
|
||||||
var i = 3
|
|
||||||
while i >= 0:
|
|
||||||
result.addToHex(arg.udata[i])
|
|
||||||
i -= 1
|
|
||||||
|
|
||||||
proc toHex*(arg: Int128): string =
|
|
||||||
result.addToHex(arg)
|
|
||||||
|
|
||||||
proc inc*(a: var Int128, y: uint32 = 1) =
|
|
||||||
a.udata[0] += y
|
|
||||||
if unlikely(a.udata[0] < y):
|
|
||||||
a.udata[1].inc
|
|
||||||
if unlikely(a.udata[1] == 0):
|
|
||||||
a.udata[2].inc
|
|
||||||
if unlikely(a.udata[2] == 0):
|
|
||||||
a.udata[3].inc
|
|
||||||
doAssert(a.sdata(3) != low(int32), "overflow")
|
|
||||||
|
|
||||||
proc cmp*(a, b: Int128): int =
|
|
||||||
let tmp1 = cmp(a.sdata(3), b.sdata(3))
|
|
||||||
if tmp1 != 0: return tmp1
|
|
||||||
let tmp2 = cmp(a.udata[2], b.udata[2])
|
|
||||||
if tmp2 != 0: return tmp2
|
|
||||||
let tmp3 = cmp(a.udata[1], b.udata[1])
|
|
||||||
if tmp3 != 0: return tmp3
|
|
||||||
let tmp4 = cmp(a.udata[0], b.udata[0])
|
|
||||||
return tmp4
|
|
||||||
|
|
||||||
proc `<`*(a, b: Int128): bool =
|
|
||||||
cmp(a, b) < 0
|
|
||||||
|
|
||||||
proc `<=`*(a, b: Int128): bool =
|
|
||||||
cmp(a, b) <= 0
|
|
||||||
|
|
||||||
proc `==`*(a, b: Int128): bool =
|
|
||||||
if a.udata[0] != b.udata[0]: return false
|
|
||||||
if a.udata[1] != b.udata[1]: return false
|
|
||||||
if a.udata[2] != b.udata[2]: return false
|
|
||||||
if a.udata[3] != b.udata[3]: return false
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc bitnot*(a: Int128): Int128 =
|
|
||||||
result.udata[0] = not a.udata[0]
|
|
||||||
result.udata[1] = not a.udata[1]
|
|
||||||
result.udata[2] = not a.udata[2]
|
|
||||||
result.udata[3] = not a.udata[3]
|
|
||||||
|
|
||||||
proc bitand*(a, b: Int128): Int128 =
|
|
||||||
result.udata[0] = a.udata[0] and b.udata[0]
|
|
||||||
result.udata[1] = a.udata[1] and b.udata[1]
|
|
||||||
result.udata[2] = a.udata[2] and b.udata[2]
|
|
||||||
result.udata[3] = a.udata[3] and b.udata[3]
|
|
||||||
|
|
||||||
proc bitor*(a, b: Int128): Int128 =
|
|
||||||
result.udata[0] = a.udata[0] or b.udata[0]
|
|
||||||
result.udata[1] = a.udata[1] or b.udata[1]
|
|
||||||
result.udata[2] = a.udata[2] or b.udata[2]
|
|
||||||
result.udata[3] = a.udata[3] or b.udata[3]
|
|
||||||
|
|
||||||
proc bitxor*(a, b: Int128): Int128 =
|
|
||||||
result.udata[0] = a.udata[0] xor b.udata[0]
|
|
||||||
result.udata[1] = a.udata[1] xor b.udata[1]
|
|
||||||
result.udata[2] = a.udata[2] xor b.udata[2]
|
|
||||||
result.udata[3] = a.udata[3] xor b.udata[3]
|
|
||||||
|
|
||||||
proc `shr`*(a: Int128, b: int): Int128 =
|
|
||||||
let b = b and 127
|
|
||||||
if b < 32:
|
|
||||||
result.sdata(3) = a.sdata(3) shr b
|
|
||||||
result.udata[2] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr b)
|
|
||||||
result.udata[1] = cast[uint32](bitconcat(a.udata[2], a.udata[1]) shr b)
|
|
||||||
result.udata[0] = cast[uint32](bitconcat(a.udata[1], a.udata[0]) shr b)
|
|
||||||
elif b < 64:
|
|
||||||
if isNegative(a):
|
|
||||||
result.sdata(3) = -1
|
|
||||||
result.sdata(2) = a.sdata(3) shr (b and 31)
|
|
||||||
result.udata[1] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr (b and 31))
|
|
||||||
result.udata[0] = cast[uint32](bitconcat(a.udata[2], a.udata[1]) shr (b and 31))
|
|
||||||
elif b < 96:
|
|
||||||
if isNegative(a):
|
|
||||||
result.sdata(3) = -1
|
|
||||||
result.sdata(2) = -1
|
|
||||||
result.sdata(1) = a.sdata(3) shr (b and 31)
|
|
||||||
result.udata[0] = cast[uint32](bitconcat(a.udata[3], a.udata[2]) shr (b and 31))
|
|
||||||
else: # b < 128
|
|
||||||
if isNegative(a):
|
|
||||||
result.sdata(3) = -1
|
|
||||||
result.sdata(2) = -1
|
|
||||||
result.sdata(1) = -1
|
|
||||||
result.sdata(0) = a.sdata(3) shr (b and 31)
|
|
||||||
|
|
||||||
proc `shl`*(a: Int128, b: int): Int128 =
|
|
||||||
let b = b and 127
|
|
||||||
if b < 32:
|
|
||||||
result.udata[0] = a.udata[0] shl b
|
|
||||||
result.udata[1] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl b) shr 32)
|
|
||||||
result.udata[2] = cast[uint32]((bitconcat(a.udata[2], a.udata[1]) shl b) shr 32)
|
|
||||||
result.udata[3] = cast[uint32]((bitconcat(a.udata[3], a.udata[2]) shl b) shr 32)
|
|
||||||
elif b < 64:
|
|
||||||
result.udata[0] = 0
|
|
||||||
result.udata[1] = a.udata[0] shl (b and 31)
|
|
||||||
result.udata[2] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl (b and 31)) shr 32)
|
|
||||||
result.udata[3] = cast[uint32]((bitconcat(a.udata[2], a.udata[1]) shl (b and 31)) shr 32)
|
|
||||||
elif b < 96:
|
|
||||||
result.udata[0] = 0
|
|
||||||
result.udata[1] = 0
|
|
||||||
result.udata[2] = a.udata[0] shl (b and 31)
|
|
||||||
result.udata[3] = cast[uint32]((bitconcat(a.udata[1], a.udata[0]) shl (b and 31)) shr 32)
|
|
||||||
else:
|
|
||||||
result.udata[0] = 0
|
|
||||||
result.udata[1] = 0
|
|
||||||
result.udata[2] = 0
|
|
||||||
result.udata[3] = a.udata[0] shl (b and 31)
|
|
||||||
|
|
||||||
proc `+`*(a, b: Int128): Int128 =
|
|
||||||
let tmp0 = uint64(a.udata[0]) + uint64(b.udata[0])
|
|
||||||
result.udata[0] = cast[uint32](tmp0)
|
|
||||||
let tmp1 = uint64(a.udata[1]) + uint64(b.udata[1]) + (tmp0 shr 32)
|
|
||||||
result.udata[1] = cast[uint32](tmp1)
|
|
||||||
let tmp2 = uint64(a.udata[2]) + uint64(b.udata[2]) + (tmp1 shr 32)
|
|
||||||
result.udata[2] = cast[uint32](tmp2)
|
|
||||||
let tmp3 = uint64(a.udata[3]) + uint64(b.udata[3]) + (tmp2 shr 32)
|
|
||||||
result.udata[3] = cast[uint32](tmp3)
|
|
||||||
|
|
||||||
proc `+=`*(a: var Int128, b: Int128) =
|
|
||||||
a = a + b
|
|
||||||
|
|
||||||
proc `-`*(a: Int128): Int128 =
|
|
||||||
result = bitnot(a)
|
|
||||||
result.inc
|
|
||||||
|
|
||||||
proc `-`*(a, b: Int128): Int128 =
|
|
||||||
a + (-b)
|
|
||||||
|
|
||||||
proc `-=`*(a: var Int128, b: Int128) =
|
|
||||||
a = a - b
|
|
||||||
|
|
||||||
proc abs*(a: Int128): Int128 =
|
|
||||||
if isNegative(a):
|
|
||||||
-a
|
|
||||||
else:
|
|
||||||
a
|
|
||||||
|
|
||||||
proc abs(a: int32): int =
|
|
||||||
if a < 0: -a else: a
|
|
||||||
|
|
||||||
proc `*`(a: Int128, b: uint32): Int128 =
|
|
||||||
let tmp0 = uint64(a.udata[0]) * uint64(b)
|
|
||||||
let tmp1 = uint64(a.udata[1]) * uint64(b)
|
|
||||||
let tmp2 = uint64(a.udata[2]) * uint64(b)
|
|
||||||
let tmp3 = uint64(a.udata[3]) * uint64(b)
|
|
||||||
|
|
||||||
if unlikely(tmp3 > uint64(high(int32))):
|
|
||||||
assert(false, "overflow")
|
|
||||||
|
|
||||||
result.udata[0] = cast[uint32](tmp0)
|
|
||||||
result.udata[1] = cast[uint32](tmp1) + cast[uint32](tmp0 shr 32)
|
|
||||||
result.udata[2] = cast[uint32](tmp2) + cast[uint32](tmp1 shr 32)
|
|
||||||
result.udata[3] = cast[uint32](tmp3) + cast[uint32](tmp2 shr 32)
|
|
||||||
|
|
||||||
proc `*`*(a: Int128, b: int32): Int128 =
|
|
||||||
result = a * cast[uint32](abs(b))
|
|
||||||
if b < 0:
|
|
||||||
result = -result
|
|
||||||
|
|
||||||
proc `*=`*(a: var Int128, b: int32): Int128 =
|
|
||||||
result = result * b
|
|
||||||
|
|
||||||
proc makeInt128(high, low: uint64): Int128 =
|
|
||||||
result.udata[0] = cast[uint32](low)
|
|
||||||
result.udata[1] = cast[uint32](low shr 32)
|
|
||||||
result.udata[2] = cast[uint32](high)
|
|
||||||
result.udata[3] = cast[uint32](high shr 32)
|
|
||||||
|
|
||||||
proc high64(a: Int128): uint64 =
|
|
||||||
bitconcat(a.udata[3], a.udata[2])
|
|
||||||
|
|
||||||
proc low64(a: Int128): uint64 =
|
|
||||||
bitconcat(a.udata[1], a.udata[0])
|
|
||||||
|
|
||||||
proc `*`*(lhs, rhs: Int128): Int128 =
|
|
||||||
let a32 = uint64(lhs.udata[1])
|
|
||||||
let a00 = uint64(lhs.udata[0])
|
|
||||||
let b32 = uint64(rhs.udata[1])
|
|
||||||
let b00 = uint64(rhs.udata[0])
|
|
||||||
result = makeInt128(high64(lhs) * low64(rhs) + low64(lhs) * high64(rhs) + a32 * b32, a00 * b00)
|
|
||||||
result += toInt128(a32 * b00) shl 32
|
|
||||||
result += toInt128(a00 * b32) shl 32
|
|
||||||
|
|
||||||
proc `*=`*(a: var Int128, b: Int128) =
|
|
||||||
a = a * b
|
|
||||||
|
|
||||||
import bitops
|
|
||||||
|
|
||||||
proc fastLog2*(a: Int128): int =
|
|
||||||
if a.udata[3] != 0:
|
|
||||||
return 96 + fastLog2(a.udata[3])
|
|
||||||
if a.udata[2] != 0:
|
|
||||||
return 64 + fastLog2(a.udata[2])
|
|
||||||
if a.udata[1] != 0:
|
|
||||||
return 32 + fastLog2(a.udata[1])
|
|
||||||
if a.udata[0] != 0:
|
|
||||||
return fastLog2(a.udata[0])
|
|
||||||
|
|
||||||
proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
|
|
||||||
assert(divisor != Zero)
|
|
||||||
let isNegativeA = isNegative(dividend)
|
|
||||||
let isNegativeB = isNegative(divisor)
|
|
||||||
|
|
||||||
var dividend = abs(dividend)
|
|
||||||
let divisor = abs(divisor)
|
|
||||||
|
|
||||||
if divisor > dividend:
|
|
||||||
result.quotient = Zero
|
|
||||||
if isNegativeA:
|
|
||||||
result.remainder = -dividend
|
|
||||||
else:
|
|
||||||
result.remainder = dividend
|
|
||||||
return
|
|
||||||
|
|
||||||
if divisor == dividend:
|
|
||||||
if isNegativeA xor isNegativeB:
|
|
||||||
result.quotient = NegOne
|
|
||||||
else:
|
|
||||||
result.quotient = One
|
|
||||||
result.remainder = Zero
|
|
||||||
return
|
|
||||||
|
|
||||||
var denominator = divisor
|
|
||||||
var quotient = Zero
|
|
||||||
|
|
||||||
# Left aligns the MSB of the denominator and the dividend.
|
|
||||||
let shift = fastLog2(dividend) - fastLog2(denominator)
|
|
||||||
denominator = denominator shl shift
|
|
||||||
|
|
||||||
# Uses shift-subtract algorithm to divide dividend by denominator. The
|
|
||||||
# remainder will be left in dividend.
|
|
||||||
for i in 0..shift:
|
|
||||||
quotient = quotient shl 1
|
|
||||||
if dividend >= denominator:
|
|
||||||
dividend -= denominator
|
|
||||||
quotient = bitor(quotient, One)
|
|
||||||
|
|
||||||
denominator = denominator shr 1
|
|
||||||
|
|
||||||
if isNegativeA xor isNegativeB:
|
|
||||||
result.quotient = -quotient
|
|
||||||
else:
|
|
||||||
result.quotient = quotient
|
|
||||||
if isNegativeA:
|
|
||||||
result.remainder = -dividend
|
|
||||||
else:
|
|
||||||
result.remainder = dividend
|
|
||||||
|
|
||||||
proc `div`*(a, b: Int128): Int128 =
|
|
||||||
let (a, _) = divMod(a, b)
|
|
||||||
return a
|
|
||||||
|
|
||||||
proc `mod`*(a, b: Int128): Int128 =
|
|
||||||
let (_, b) = divMod(a, b)
|
|
||||||
return b
|
|
||||||
|
|
||||||
proc addInt128*(result: var string; value: Int128) =
|
|
||||||
let initialSize = result.len
|
|
||||||
if value == Zero:
|
|
||||||
result.add '0'
|
|
||||||
elif value == low(Int128):
|
|
||||||
result.add "-170141183460469231731687303715884105728"
|
|
||||||
else:
|
|
||||||
let isNegative = isNegative(value)
|
|
||||||
var value = abs(value)
|
|
||||||
while value > Zero:
|
|
||||||
let (quot, rem) = divMod(value, Ten)
|
|
||||||
result.add "0123456789"[rem.toInt64]
|
|
||||||
value = quot
|
|
||||||
if isNegative:
|
|
||||||
result.add '-'
|
|
||||||
|
|
||||||
var i = initialSize
|
|
||||||
var j = high(result)
|
|
||||||
while i < j:
|
|
||||||
swap(result[i], result[j])
|
|
||||||
i += 1
|
|
||||||
j -= 1
|
|
||||||
|
|
||||||
proc `$`*(a: Int128): string =
|
|
||||||
# "-170141183460469231731687303715884105728".len == 41
|
|
||||||
result = newStringOfCap(41)
|
|
||||||
result.addInt128(a)
|
|
||||||
|
|
||||||
proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
|
|
||||||
assert(pos < arg.len)
|
|
||||||
assert(arg[pos] in {'-', '0'..'9'})
|
|
||||||
|
|
||||||
var isNegative = false
|
|
||||||
var pos = pos
|
|
||||||
if arg[pos] == '-':
|
|
||||||
isNegative = true
|
|
||||||
pos += 1
|
|
||||||
|
|
||||||
result = Zero
|
|
||||||
while pos < arg.len and arg[pos] in '0'..'9':
|
|
||||||
result = result * Ten
|
|
||||||
result.inc(uint32(arg[pos]) - uint32('0'))
|
|
||||||
pos += 1
|
|
||||||
|
|
||||||
if isNegative:
|
|
||||||
result = -result
|
|
||||||
|
|
||||||
# fluff
|
|
||||||
|
|
||||||
proc `<`*(a: Int128, b: BiggestInt): bool =
|
|
||||||
cmp(a, toInt128(b)) < 0
|
|
||||||
|
|
||||||
proc `<`*(a: BiggestInt, b: Int128): bool =
|
|
||||||
cmp(toInt128(a), b) < 0
|
|
||||||
|
|
||||||
proc `<=`*(a: Int128, b: BiggestInt): bool =
|
|
||||||
cmp(a, toInt128(b)) <= 0
|
|
||||||
|
|
||||||
proc `<=`*(a: BiggestInt, b: Int128): bool =
|
|
||||||
cmp(toInt128(a), b) <= 0
|
|
||||||
|
|
||||||
proc `==`*(a: Int128, b: BiggestInt): bool =
|
|
||||||
a == toInt128(b)
|
|
||||||
|
|
||||||
proc `==`*(a: BiggestInt, b: Int128): bool =
|
|
||||||
toInt128(a) == b
|
|
||||||
|
|
||||||
proc `-`*(a: BiggestInt, b: Int128): Int128 =
|
|
||||||
toInt128(a) - b
|
|
||||||
|
|
||||||
proc `-`*(a: Int128, b: BiggestInt): Int128 =
|
|
||||||
a - toInt128(b)
|
|
||||||
|
|
||||||
proc `+`*(a: BiggestInt, b: Int128): Int128 =
|
|
||||||
toInt128(a) + b
|
|
||||||
|
|
||||||
proc `+`*(a: Int128, b: BiggestInt): Int128 =
|
|
||||||
a + toInt128(b)
|
|
||||||
|
|
||||||
proc toFloat64*(arg: Int128): float64 =
|
|
||||||
let isNegative = isNegative(arg)
|
|
||||||
let arg = abs(arg)
|
|
||||||
|
|
||||||
let a = float64(bitconcat(arg.udata[1], arg.udata[0]))
|
|
||||||
let b = float64(bitconcat(arg.udata[3], arg.udata[2]))
|
|
||||||
|
|
||||||
result = a + 18446744073709551616'f64 * b # a + 2^64 * b
|
|
||||||
if isNegative:
|
|
||||||
result = -result
|
|
||||||
|
|
||||||
proc ldexp(x: float64, exp: cint): float64 {.importc: "ldexp", header: "<math.h>".}
|
|
||||||
|
|
||||||
template bitor(a, b, c: Int128): Int128 = bitor(bitor(a, b), c)
|
|
||||||
|
|
||||||
proc toInt128*(arg: float64): Int128 =
|
|
||||||
let isNegative = arg < 0
|
|
||||||
let v0 = ldexp(abs(arg), -100)
|
|
||||||
let w0 = uint64(trunc(v0))
|
|
||||||
let v1 = ldexp(v0 - float64(w0), 50)
|
|
||||||
let w1 = uint64(trunc(v1))
|
|
||||||
let v2 = ldexp(v1 - float64(w1), 50)
|
|
||||||
let w2 = uint64(trunc(v2))
|
|
||||||
|
|
||||||
let res = bitor(toInt128(w0) shl 100, toInt128(w1) shl 50, toInt128(w2))
|
|
||||||
if isNegative:
|
|
||||||
return -res
|
|
||||||
else:
|
|
||||||
return res
|
|
||||||
|
|
||||||
proc maskUInt64*(arg: Int128): Int128 {.noinit, inline.} =
|
|
||||||
result.udata[0] = arg.udata[0]
|
|
||||||
result.udata[1] = arg.udata[1]
|
|
||||||
result.udata[2] = 0
|
|
||||||
result.udata[3] = 0
|
|
||||||
|
|
||||||
proc maskUInt32*(arg: Int128): Int128 {.noinit, inline.} =
|
|
||||||
result.udata[0] = arg.udata[0]
|
|
||||||
result.udata[1] = 0
|
|
||||||
result.udata[2] = 0
|
|
||||||
result.udata[3] = 0
|
|
||||||
|
|
||||||
proc maskUInt16*(arg: Int128): Int128 {.noinit, inline.} =
|
|
||||||
result.udata[0] = arg.udata[0] and 0xffff
|
|
||||||
result.udata[1] = 0
|
|
||||||
result.udata[2] = 0
|
|
||||||
result.udata[3] = 0
|
|
||||||
|
|
||||||
proc maskUInt8*(arg: Int128): Int128 {.noinit, inline.} =
|
|
||||||
result.udata[0] = arg.udata[0] and 0xff
|
|
||||||
result.udata[1] = 0
|
|
||||||
result.udata[2] = 0
|
|
||||||
result.udata[3] = 0
|
|
||||||
|
|
||||||
proc maskBytes*(arg: Int128, numbytes: int): Int128 {.noinit.} =
|
|
||||||
case numbytes
|
|
||||||
of 1:
|
|
||||||
return maskUInt8(arg)
|
|
||||||
of 2:
|
|
||||||
return maskUInt16(arg)
|
|
||||||
of 4:
|
|
||||||
return maskUInt32(arg)
|
|
||||||
of 8:
|
|
||||||
return maskUInt64(arg)
|
|
||||||
else:
|
|
||||||
assert(false, "masking only implemented for 1, 2, 4 and 8 bytes")
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue