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)
|
|
||||||
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,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,10 +10,7 @@
|
||||||
## 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
|
||||||
|
|
@ -25,22 +22,22 @@ 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
|
||||||
|
|
@ -49,14 +46,13 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
|
||||||
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
|
||||||
|
|
||||||
|
|
@ -77,33 +73,28 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
## 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:
|
||||||
|
|
@ -116,7 +107,7 @@ 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;
|
||||||
|
|
@ -155,7 +146,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
# 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
|
||||||
|
|
@ -187,21 +178,5 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||||
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
|
||||||
|
|
||||||
|
|
|
||||||
1651
compiler/ast.nim
1651
compiler/ast.nim
File diff suppressed because it is too large
Load diff
1009
compiler/astalgo.nim
1009
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):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
ElemType = byte
|
TBitSet* = seq[int8] # we use byte here to avoid issues with
|
||||||
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
|
const
|
||||||
ElemSize* = 8
|
ElemSize* = sizeof(int8) * 8
|
||||||
One = ElemType(1)
|
|
||||||
Zero = ElemType(0)
|
|
||||||
|
|
||||||
template modElemSize(arg: untyped): untyped = arg and 7
|
proc bitSetInit*(b: var TBitSet, length: int)
|
||||||
template divElemSize(arg: untyped): untyped = arg shr 3
|
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 bitSetIn*(x: TBitSet, e: BiggestInt): bool =
|
proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
|
||||||
result = (x[int(e.divElemSize)] and (One shl e.modElemSize)) != Zero
|
result = (x[int(e div ElemSize)] and toU8(int(1 shl (e mod ElemSize)))) !=
|
||||||
|
toU8(0)
|
||||||
|
|
||||||
proc bitSetIncl*(x: var TBitSet, elem: BiggestInt) =
|
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])
|
|
||||||
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", [])
|
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, "for ($1 = 0; $1 < $2; $1++) {$n",
|
|
||||||
[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", [])
|
lineF(p, cpsStmts, "}$n", [])
|
||||||
|
|
||||||
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
|
proc genTraverseProc(m: BModule, typ: PType, reason: TTypeInfoReason): 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,11 +10,8 @@
|
||||||
# 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
|
||||||
|
|
@ -30,118 +27,186 @@ proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
|
||||||
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
|
|
||||||
# disallow this here:
|
|
||||||
if i > 0 and i < name.len-1 and name[i+1] in Digits:
|
|
||||||
discard
|
discard
|
||||||
|
of 'a'..'z', '0'..'9':
|
||||||
|
add(result, c)
|
||||||
else:
|
else:
|
||||||
result.add(c)
|
add(result, "HEX" & toHex(ord(c), 2))
|
||||||
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:
|
|
||||||
result.add("X" & 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()
|
||||||
|
|
|
||||||
2334
compiler/cgen.nim
2334
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,34 +53,26 @@ 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]]
|
|
||||||
# in how many nested try statements we are
|
|
||||||
# (the vars must be volatile then)
|
# (the vars must be volatile then)
|
||||||
# bool is true when are in the except part of a try block
|
inExceptBlock*: int # are we currently inside an except block?
|
||||||
|
# leaving such scopes by raise or by return must
|
||||||
|
# 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
|
||||||
|
|
@ -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
|
|
||||||
# would be ideal but for some reason enums
|
|
||||||
# don't seem to get cached so it'd generate
|
|
||||||
# 1 ABI check per occurence in code
|
|
||||||
forwTypeCache*: TypeCache # cache for forward declarations of types
|
|
||||||
declaredThings*: IntSet # things we have declared in this .c file
|
declaredThings*: IntSet # things we have declared in this .c file
|
||||||
declaredProtos*: IntSet # prototypes 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*: TLinkedList # needed headers to include
|
||||||
headerFiles*: seq[string] # needed headers to include
|
typeInfoMarker*: IntSet # needed for generating type information
|
||||||
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,203 +7,173 @@
|
||||||
# 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 1..<result.len:
|
for i in countup(1, sonsLen(result)-1):
|
||||||
result[i] = genConv(result[i], disp.typ[i], true, conf)
|
result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true)
|
||||||
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 =
|
||||||
|
result = false
|
||||||
if a.name.id != b.name.id: return
|
if a.name.id != b.name.id: return
|
||||||
if a.typ.len != b.typ.len:
|
if sonsLen(a.typ) != sonsLen(b.typ):
|
||||||
return
|
return # check for return type:
|
||||||
|
if not sameTypeOrNil(a.typ.sons[0], b.typ.sons[0]): return
|
||||||
for i in 1..<a.typ.len:
|
for i in countup(1, sonsLen(a.typ) - 1):
|
||||||
var aa = a.typ[i]
|
var aa = a.typ.sons[i]
|
||||||
var bb = b.typ[i]
|
var bb = b.typ.sons[i]
|
||||||
while true:
|
while true:
|
||||||
aa = skipTypes(aa, {tyGenericInst, tyAlias})
|
aa = skipTypes(aa, {tyGenericInst})
|
||||||
bb = skipTypes(bb, {tyGenericInst, tyAlias})
|
bb = skipTypes(bb, {tyGenericInst})
|
||||||
if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}:
|
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:
|
else:
|
||||||
return No
|
return
|
||||||
elif diff != high(int) and sfFromGeneric notin (a.flags+b.flags):
|
result = true
|
||||||
result = Invalid
|
|
||||||
else:
|
|
||||||
return No
|
|
||||||
else:
|
|
||||||
return No
|
|
||||||
if result == Yes:
|
|
||||||
# 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:
|
||||||
|
disp.typ.lockLevel = meth.typ.lockLevel
|
||||||
|
|
||||||
|
proc methodDef*(s: PSym, fromCache: bool) =
|
||||||
|
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
|
return
|
||||||
of No: discard
|
|
||||||
of Invalid:
|
|
||||||
if witness.isNil: witness = g.methods[i].methods[0]
|
|
||||||
# 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:
|
||||||
|
|
@ -213,73 +183,60 @@ proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
|
||||||
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, boolType)
|
var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
|
||||||
a.add newSymNode(ands)
|
addSon(a, newSymNode(ands))
|
||||||
a.add cond
|
addSon(a, cond)
|
||||||
a.add isn
|
addSon(a, 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
|
|
||||||
2102
compiler/docgen.nim
2102
compiler/docgen.nim
File diff suppressed because it is too large
Load diff
|
|
@ -11,68 +11,39 @@
|
||||||
# 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
|
type
|
||||||
TGen = object of PPassContext
|
TGen = object of TPassContext
|
||||||
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) =
|
case templ.kind
|
||||||
let x = param
|
of nkSym:
|
||||||
|
var s = templ.sym
|
||||||
|
if s.owner.id == c.owner.id:
|
||||||
|
if s.kind == skParam and sfGenSym notin s.flags:
|
||||||
|
let x = actual.sons[s.position]
|
||||||
if x.kind == nkArgList:
|
if x.kind == nkArgList:
|
||||||
for y in items(x): result.add(y)
|
for y in items(x): result.add(y)
|
||||||
else:
|
else:
|
||||||
result.add copyTree(x)
|
result.add copyTree(x)
|
||||||
|
|
||||||
case templ.kind
|
|
||||||
of nkSym:
|
|
||||||
var s = templ.sym
|
|
||||||
if (s.owner == nil and s.kind == skParam) or s.owner == c.owner:
|
|
||||||
if s.kind == skParam and {sfGenSym, sfTemplateParam} * s.flags == {sfTemplateParam}:
|
|
||||||
handleParam actual[s.position]
|
|
||||||
elif (s.owner != nil) and (s.kind == skGenericParam or
|
|
||||||
s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam):
|
|
||||||
handleParam actual[s.owner.typ.len + s.position - 1]
|
|
||||||
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 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)
|
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:
|
|
||||||
var isDeclarative = false
|
|
||||||
if templ.kind in {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef,
|
|
||||||
nkMacroDef, nkTemplateDef, nkConverterDef, nkTypeSection,
|
|
||||||
nkVarSection, nkLetSection, nkConstSection} and
|
|
||||||
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:
|
else:
|
||||||
var res = copyNode(c, templ, actual)
|
var res = copyNode(c, templ, actual)
|
||||||
for i in 0..<templ.len:
|
for i in countup(0, sonsLen(templ) - 1):
|
||||||
evalTemplateAux(templ[i], actual, c, res)
|
evalTemplateAux(templ.sons[i], actual, c, res)
|
||||||
result.add 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
|
|
@ -10,24 +10,28 @@
|
||||||
# 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
|
||||||
|
|
||||||
|
proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream
|
||||||
|
# #! template(subsChar='$', metaChar='#') | standard(version="0.7.2")
|
||||||
|
# implementation
|
||||||
|
|
||||||
type
|
type
|
||||||
TParseState = enum
|
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', '.', '_'}
|
||||||
|
|
@ -35,15 +39,16 @@ const
|
||||||
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)
|
||||||
|
|
@ -56,48 +61,44 @@ proc scanPar(p: var TTmplParser, d: int) =
|
||||||
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
|
|
||||||
LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '^',
|
|
||||||
'|', '%', '&', '$', '@', '~', ','}
|
|
||||||
|
|
||||||
proc parseLine(p: var TTmplParser) =
|
proc parseLine(p: var TTmplParser) =
|
||||||
var j = 0
|
var
|
||||||
let len = p.x.len
|
d, j, curly: int
|
||||||
|
keyw: string
|
||||||
while j < len and p.x[j] == ' ': inc(j)
|
j = 0
|
||||||
|
while p.x[j] == ' ': inc(j)
|
||||||
if len >= 2 and p.x[0] == p.nimDirective and p.x[1] == '?':
|
if (p.x[0] == p.nimDirective) and (p.x[0 + 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({':', '='}):
|
||||||
|
|
@ -128,8 +129,10 @@ proc parseLine(p: var TTmplParser) =
|
||||||
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 '\0':
|
||||||
|
break
|
||||||
of '\x01'..'\x1F', '\x80'..'\xFF':
|
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))
|
||||||
|
|
@ -155,9 +158,12 @@ proc parseLine(p: var TTmplParser) =
|
||||||
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 '\0':
|
||||||
|
localError(p.info, errXExpected, "}")
|
||||||
|
break
|
||||||
of '{':
|
of '{':
|
||||||
inc(j)
|
inc(j)
|
||||||
inc(curly)
|
inc(curly)
|
||||||
|
|
@ -170,9 +176,6 @@ proc parseLine(p: var TTmplParser) =
|
||||||
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, '\"')
|
||||||
|
|
@ -181,7 +184,7 @@ proc parseLine(p: var TTmplParser) =
|
||||||
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, ')')
|
||||||
|
|
@ -193,30 +196,25 @@ proc parseLine(p: var TTmplParser) =
|
||||||
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
|
|
||||||
|
|
@ -12,27 +12,24 @@
|
||||||
# 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):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
type
|
type
|
||||||
PIdent* = ref TIdent
|
TIdObj* = object of RootObj
|
||||||
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
|
||||||
|
|
@ -66,9 +63,11 @@ proc cmpExact(a, b: cstring, blen: int): int =
|
||||||
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:
|
||||||
|
|
@ -76,8 +75,8 @@ proc getIdent*(ic: IdentCache; identifier: cstring, length: int, h: Hash): PIden
|
||||||
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))
|
||||||
|
|
@ -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 or check.kind notin OverloadableSyms:
|
if s.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:
|
|
||||||
# now lazy. Speeds up the compiler and is a prerequisite for IC.
|
|
||||||
strTableAdd(c.importTable.symbols, s)
|
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:
|
else:
|
||||||
importPureEnumField(c, e)
|
# rodgen assures that converters and patterns are no stubs
|
||||||
else:
|
if s.kind == skConverter: addConverter(c, s)
|
||||||
if s.kind == skConverter: addConverter(c, LazySym(sym: s))
|
if hasPattern(s): addPattern(c, s)
|
||||||
if hasPattern(s): addPattern(c, LazySym(sym: s))
|
|
||||||
if s.owner != origin:
|
|
||||||
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)
|
||||||
let multiImport = s.kind notin ExportableSymKinds or s.kind in skProcKinds
|
if s.kind notin ExportableSymKinds:
|
||||||
|
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)
|
|
||||||
addUnnamedIt(c, fromMod, true)
|
|
||||||
when false:
|
|
||||||
var exceptSet: IntSet
|
var exceptSet: IntSet
|
||||||
importAllSymbolsExcept(c, fromMod, exceptSet)
|
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
|
|
||||||
return ret
|
|
||||||
|
|
||||||
proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
|
||||||
let transf = transformImportAs(c, n)
|
|
||||||
n = transf.node
|
|
||||||
let f = checkModuleName(c.config, n)
|
|
||||||
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``!
|
# ``addDecl`` needs to be done before ``importAllSymbols``!
|
||||||
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)
|
importAllSymbolsExcept(c, m, emptySet)
|
||||||
#importForwarded(c, m.ast, emptySet, m)
|
#importForwarded(c, m.ast, emptySet)
|
||||||
afterImport(c, m)
|
|
||||||
|
|
||||||
proc evalImport*(c: PContext, n: PNode): PNode =
|
proc evalFrom(c: PContext, n: PNode): PNode =
|
||||||
result = newNodeI(nkImportStmt, n.info)
|
result = n
|
||||||
for i in 0..<n.len:
|
checkMinSonsLen(n, 2)
|
||||||
let it = n[i]
|
var m = myImportModule(c, n.sons[0])
|
||||||
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:
|
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
|
||||||
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
var im = ImportedModule(m: m, mode: importSet, imported: initIntSet())
|
if n.sons[i].kind != nkNilLit:
|
||||||
for i in 1..<n.len:
|
importSymbol(c, n.sons[i], m)
|
||||||
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")
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
#
|
|
||||||
#
|
|
||||||
# The Nim Compiler
|
|
||||||
# (c) Copyright 2020 Andreas Rumpf
|
|
||||||
#
|
|
||||||
# See the file "copying.txt", included in this
|
|
||||||
# distribution, for details about the copyright.
|
|
||||||
#
|
|
||||||
|
|
||||||
## Implementation of the check that `recover` needs, see
|
|
||||||
## https://github.com/nim-lang/RFCs/issues/244 for more details.
|
|
||||||
|
|
||||||
import
|
|
||||||
ast, types, renderer, intsets
|
|
||||||
|
|
||||||
when defined(nimPreviewSlimSystem):
|
|
||||||
import std/assertions
|
|
||||||
|
|
||||||
proc canAlias(arg, ret: PType; marker: var IntSet): bool
|
|
||||||
|
|
||||||
proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
|
|
||||||
case n.kind
|
|
||||||
of nkRecList:
|
|
||||||
for i in 0..<n.len:
|
|
||||||
result = canAliasN(arg, n[i], marker)
|
|
||||||
if result: return
|
|
||||||
of nkRecCase:
|
|
||||||
assert(n[0].kind == nkSym)
|
|
||||||
result = canAliasN(arg, n[0], marker)
|
|
||||||
if result: return
|
|
||||||
for i in 1..<n.len:
|
|
||||||
case n[i].kind
|
|
||||||
of nkOfBranch, nkElse:
|
|
||||||
result = canAliasN(arg, lastSon(n[i]), marker)
|
|
||||||
if result: return
|
|
||||||
else: discard
|
|
||||||
of nkSym:
|
|
||||||
result = canAlias(arg, n.sym.typ, marker)
|
|
||||||
else: discard
|
|
||||||
|
|
||||||
proc canAlias(arg, ret: PType; marker: var IntSet): bool =
|
|
||||||
if containsOrIncl(marker, ret.id):
|
|
||||||
return false
|
|
||||||
|
|
||||||
if ret.kind in {tyPtr, tyPointer}:
|
|
||||||
# unsafe so we don't care:
|
|
||||||
return false
|
|
||||||
if compareTypes(arg, ret, dcEqIgnoreDistinct):
|
|
||||||
return true
|
|
||||||
case ret.kind
|
|
||||||
of tyObject:
|
|
||||||
if isFinal(ret):
|
|
||||||
result = canAliasN(arg, ret.n, marker)
|
|
||||||
if not result and ret.len > 0 and ret[0] != nil:
|
|
||||||
result = canAlias(arg, ret[0], marker)
|
|
||||||
else:
|
|
||||||
result = true
|
|
||||||
of tyTuple:
|
|
||||||
for i in 0..<ret.len:
|
|
||||||
result = canAlias(arg, ret[i], marker)
|
|
||||||
if result: break
|
|
||||||
of tyArray, tySequence, tyDistinct, tyGenericInst,
|
|
||||||
tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef:
|
|
||||||
result = canAlias(arg, ret.lastSon, marker)
|
|
||||||
of tyProc:
|
|
||||||
result = ret.callConv == ccClosure
|
|
||||||
else:
|
|
||||||
result = false
|
|
||||||
|
|
||||||
proc isValueOnlyType(t: PType): bool =
|
|
||||||
# t doesn't contain pointers and references
|
|
||||||
proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent}
|
|
||||||
result = not types.searchTypeFor(t, wrap)
|
|
||||||
|
|
||||||
proc canAlias*(arg, ret: PType): bool =
|
|
||||||
if isValueOnlyType(arg):
|
|
||||||
# can alias only with addr(arg.x) and we don't care if it is not safe
|
|
||||||
result = false
|
|
||||||
else:
|
|
||||||
var marker = initIntSet()
|
|
||||||
result = canAlias(arg, ret, marker)
|
|
||||||
|
|
||||||
proc containsVariable(n: PNode): bool =
|
|
||||||
case n.kind
|
|
||||||
of nodesToIgnoreSet:
|
|
||||||
result = false
|
|
||||||
of nkSym:
|
|
||||||
result = n.sym.kind in {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
|
|
||||||
else:
|
|
||||||
for ch in n:
|
|
||||||
if containsVariable(ch): return true
|
|
||||||
result = false
|
|
||||||
|
|
||||||
proc checkIsolate*(n: PNode): bool =
|
|
||||||
if types.containsTyRef(n.typ):
|
|
||||||
# XXX Maybe require that 'n.typ' is acyclic. This is not much
|
|
||||||
# worse than the already exisiting inheritance and closure restrictions.
|
|
||||||
case n.kind
|
|
||||||
of nkCharLit..nkNilLit:
|
|
||||||
result = true
|
|
||||||
of nkCallKinds:
|
|
||||||
# XXX: as long as we don't update the analysis while examining arguments
|
|
||||||
# we can do an early check of the return type, otherwise this is a
|
|
||||||
# bug and needs to be moved below
|
|
||||||
if tfNoSideEffect notin n[0].typ.flags:
|
|
||||||
return false
|
|
||||||
for i in 1..<n.len:
|
|
||||||
if checkIsolate(n[i]):
|
|
||||||
discard "fine, it is isolated already"
|
|
||||||
else:
|
|
||||||
let argType = n[i].typ
|
|
||||||
if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType):
|
|
||||||
if argType.canAlias(n.typ) or containsVariable(n[i]):
|
|
||||||
# bug #19013: Alias information is not enough, we need to check for potential
|
|
||||||
# "overlaps". I claim the problem can only happen by reading again from a location
|
|
||||||
# that materialized which is only possible if a variable that contains a `ref`
|
|
||||||
# is involved.
|
|
||||||
return false
|
|
||||||
result = true
|
|
||||||
of nkIfStmt, nkIfExpr:
|
|
||||||
for it in n:
|
|
||||||
result = checkIsolate(it.lastSon)
|
|
||||||
if not result: break
|
|
||||||
of nkCaseStmt:
|
|
||||||
for i in 1..<n.len:
|
|
||||||
result = checkIsolate(n[i].lastSon)
|
|
||||||
if not result: break
|
|
||||||
of nkObjConstr:
|
|
||||||
result = true
|
|
||||||
for i in 1..<n.len:
|
|
||||||
result = checkIsolate(n[i].lastSon)
|
|
||||||
if not result: break
|
|
||||||
of nkBracket, nkTupleConstr, nkPar:
|
|
||||||
for it in n:
|
|
||||||
result = checkIsolate(it)
|
|
||||||
if not result: break
|
|
||||||
of nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv:
|
|
||||||
result = checkIsolate(n[1])
|
|
||||||
of nkObjUpConv, nkObjDownConv, nkDotExpr:
|
|
||||||
result = checkIsolate(n[0])
|
|
||||||
of nkStmtList, nkStmtListExpr:
|
|
||||||
if n.len > 0:
|
|
||||||
result = checkIsolate(n[^1])
|
|
||||||
else:
|
|
||||||
result = false
|
|
||||||
else:
|
|
||||||
# unanalysable expression:
|
|
||||||
result = false
|
|
||||||
else:
|
|
||||||
# no ref, no cry:
|
|
||||||
result = true
|
|
||||||
3129
compiler/jsgen.nim
3129
compiler/jsgen.nim
File diff suppressed because it is too large
Load diff
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