mirror of
https://github.com/nim-lang/Nim.git
synced 2026-08-31 10:53:40 +00:00
Compare commits
1 Commits
version-20
...
v1.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
727c6378d2 |
33
.builds/freebsd.yml
Normal file
33
.builds/freebsd.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||
|
||||
# see https://man.sr.ht/builds.sr.ht/compatibility.md#freebsd
|
||||
image: freebsd/latest
|
||||
packages:
|
||||
- databases/sqlite3
|
||||
- devel/boehm-gc-threaded
|
||||
- devel/pcre
|
||||
- devel/sdl20
|
||||
- devel/sfml
|
||||
- www/node
|
||||
- devel/gmake
|
||||
|
||||
|
||||
sources:
|
||||
- https://github.com/nim-lang/Nim
|
||||
environment:
|
||||
NIM_TESTAMENT_BATCH: "0_1"
|
||||
CC: /usr/bin/clang
|
||||
tasks:
|
||||
- setup: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimBuildCsourcesIfNeeded
|
||||
echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
|
||||
- test: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimInternalBuildKochAndRunCI
|
||||
triggers:
|
||||
- action: email
|
||||
condition: failure
|
||||
to: Andreas Rumpf <rumpf_a@web.de>
|
||||
33
.builds/openbsd_0.yml
Normal file
33
.builds/openbsd_0.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||
|
||||
image: openbsd/latest
|
||||
packages:
|
||||
- gmake
|
||||
- sqlite3
|
||||
- node
|
||||
- boehm-gc
|
||||
- pcre
|
||||
- sfml
|
||||
- sdl2
|
||||
- libffi
|
||||
|
||||
|
||||
sources:
|
||||
- https://github.com/nim-lang/Nim
|
||||
environment:
|
||||
NIM_TESTAMENT_BATCH: "0_2"
|
||||
CC: /usr/bin/clang
|
||||
tasks:
|
||||
- setup: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimBuildCsourcesIfNeeded
|
||||
echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
|
||||
- test: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimInternalBuildKochAndRunCI
|
||||
triggers:
|
||||
- action: email
|
||||
condition: failure
|
||||
to: Andreas Rumpf <rumpf_a@web.de>
|
||||
33
.builds/openbsd_1.yml
Normal file
33
.builds/openbsd_1.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`
|
||||
|
||||
image: openbsd/latest
|
||||
packages:
|
||||
- gmake
|
||||
- sqlite3
|
||||
- node
|
||||
- boehm-gc
|
||||
- pcre
|
||||
- sfml
|
||||
- sdl2
|
||||
- libffi
|
||||
|
||||
|
||||
sources:
|
||||
- https://github.com/nim-lang/Nim
|
||||
environment:
|
||||
NIM_TESTAMENT_BATCH: "1_2"
|
||||
CC: /usr/bin/clang
|
||||
tasks:
|
||||
- setup: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimBuildCsourcesIfNeeded
|
||||
echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv
|
||||
- test: |
|
||||
set -e
|
||||
cd Nim
|
||||
. ci/funs.sh && nimInternalBuildKochAndRunCI
|
||||
triggers:
|
||||
- action: email
|
||||
condition: failure
|
||||
to: Andreas Rumpf <rumpf_a@web.de>
|
||||
53
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
53
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Have you found an unexpected behavior? Use this template.
|
||||
title: Think about the title, twice
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
(Consider writing a PR targetting devel branch after filing this, see [contributing.html](https://nim-lang.github.io/Nim/contributing.html)).
|
||||
|
||||
Function `echo` outputs the wrong string.
|
||||
|
||||
### Example
|
||||
```nim
|
||||
echo "Hello World!"
|
||||
# This code should be a minimum reproducible example:
|
||||
# try to simplify and minimize as much as possible. If it's a compiler
|
||||
# issue, try to minimize further by removing any imports if possible.
|
||||
```
|
||||
|
||||
### Current Output
|
||||
please check whether the problem still exists in git head before posting,
|
||||
see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler).
|
||||
```
|
||||
Hola mundo!
|
||||
```
|
||||
|
||||
### Expected Output
|
||||
```
|
||||
Hello World!
|
||||
```
|
||||
|
||||
### Possible Solution
|
||||
|
||||
* In file xyz there is a call that might be the cause of it.
|
||||
|
||||
### Additional Information
|
||||
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`).
|
||||
|
||||
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.
|
||||
|
||||
* Issue #abc is related, but different because of ...
|
||||
* This issue is blocking my project xyz
|
||||
|
||||
```
|
||||
$ nim -v
|
||||
Nim Compiler Version 0.1.2
|
||||
# make sure to include the git hash if not using a tagged release
|
||||
```
|
||||
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)
|
||||
35
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Do you want to suggest a new feature? Use this template.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!---
|
||||
Notice there is now 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 template,
|
||||
but bear in mind that adding new features to the language is currently a low priority.
|
||||
-->
|
||||
|
||||
|
||||
### Summary
|
||||
<!--- Short summary of your proposed feature -->
|
||||
|
||||
|
||||
### Description
|
||||
<!--- Describe your solution, what problem does it fix? -->
|
||||
|
||||
|
||||
### Alternatives
|
||||
<!--- Are there any alternatives you've considered? -->
|
||||
|
||||
|
||||
### Additional Information
|
||||
<!--- For Example:
|
||||
* A link to a project where the issue is relevant.
|
||||
* A link to a related issue or discussion.
|
||||
-->
|
||||
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
Normal file
69
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
# 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
|
||||
23
.github/workflows/bisects.yml
vendored
23
.github/workflows/bisects.yml
vendored
@@ -1,23 +0,0 @@
|
||||
# See https://github.com/juancarlospaco/nimrun-action/issues/3#issuecomment-1607344901
|
||||
name: issue comments bisects
|
||||
on:
|
||||
issue_comment:
|
||||
types: created
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
# nimrun-action requires Nim installed.
|
||||
- uses: jiro4989/setup-nim-action@v1
|
||||
with:
|
||||
nim-version: 'devel'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get install --no-install-recommends -yq valgrind
|
||||
|
||||
- uses: juancarlospaco/nimrun-action@nim
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
115
.github/workflows/ci_bench.yml
vendored
115
.github/workflows/ci_bench.yml
vendored
@@ -1,115 +0,0 @@
|
||||
name: Benchmarks CI
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Build csourcesAny'
|
||||
shell: bash
|
||||
run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
|
||||
|
||||
- name: 'Build koch'
|
||||
shell: bash
|
||||
run: nim c koch
|
||||
|
||||
- name: 'Build Nim'
|
||||
shell: bash
|
||||
run: ./koch boot -d:release -d:nimStrictMode --lib:lib
|
||||
|
||||
- name: 'Build Nimble'
|
||||
shell: bash
|
||||
run: ./koch nimble
|
||||
|
||||
- name: 'Action'
|
||||
shell: bash
|
||||
run: nim c -r -d:release ci/action.nim
|
||||
|
||||
- name: 'Checkout minimize'
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'nim-lang/ci_bench'
|
||||
path: minimize
|
||||
|
||||
- name: 'Run minimize benchmarks'
|
||||
shell: bash
|
||||
run: ./minimize/minimize ci-bench
|
||||
|
||||
- name: 'Restore minimize cached database'
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: minimize.csv
|
||||
key: minimize-db-key
|
||||
|
||||
- name: 'Update minimize db'
|
||||
shell: bash
|
||||
run: ./minimize/minimize update-db
|
||||
|
||||
- name: 'Save minimize cached database'
|
||||
if: |
|
||||
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
|
||||
matrix.target == 'linux'
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
path: minimize.csv
|
||||
key: minimize-db-key
|
||||
|
||||
- name: 'Generate minimize report'
|
||||
shell: bash
|
||||
run: ./minimize/minimize generate-report
|
||||
|
||||
- name: 'Archive minimize report'
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: minimize-report
|
||||
path: |
|
||||
minimize/minimize.html
|
||||
minimize/minimize.csv
|
||||
|
||||
# Requires additional permissions, see:
|
||||
# https://github.com/nim-lang/Nim/actions/runs/4778177321/jobs/8494423792?pr=21566
|
||||
# - name: 'Publish HTML report'
|
||||
# uses: rossjrw/pr-preview-action@v1
|
||||
# with:
|
||||
# source-dir: minimize
|
||||
# umbrella-dir: minimize
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract md summary
|
||||
run: |
|
||||
cat minimize/summary.md >> $GITHUB_STEP_SUMMARY
|
||||
15
.github/workflows/ci_docs.yml
vendored
15
.github/workflows/ci_docs.yml
vendored
@@ -6,7 +6,6 @@ on:
|
||||
- 'compiler/renderverbatim.nim'
|
||||
- 'config/nimdoc.cfg'
|
||||
- 'doc/**.rst'
|
||||
- 'doc/**.md'
|
||||
- 'doc/nimdoc.css'
|
||||
- 'lib/**.nim'
|
||||
- 'nimdoc/testproject/expected/testproject.html'
|
||||
@@ -14,7 +13,6 @@ on:
|
||||
- 'tools/kochdocs.nim'
|
||||
- '.github/workflows/ci_docs.yml'
|
||||
- 'koch.nim'
|
||||
|
||||
pull_request:
|
||||
# Run only on changes on these files.
|
||||
paths:
|
||||
@@ -22,7 +20,6 @@ on:
|
||||
- 'compiler/renderverbatim.nim'
|
||||
- 'config/nimdoc.cfg'
|
||||
- 'doc/**.rst'
|
||||
- 'doc/**.md'
|
||||
- 'doc/nimdoc.css'
|
||||
- 'lib/**.nim'
|
||||
- 'nimdoc/testproject/expected/testproject.html'
|
||||
@@ -31,10 +28,6 @@ on:
|
||||
- '.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:
|
||||
@@ -43,11 +36,11 @@ jobs:
|
||||
target: [linux, windows, osx]
|
||||
include:
|
||||
- target: linux
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-20.04
|
||||
- target: windows
|
||||
os: windows-2019
|
||||
- target: osx
|
||||
os: macos-13
|
||||
os: macos-10.15
|
||||
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -55,7 +48,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -111,7 +104,7 @@ jobs:
|
||||
if: |
|
||||
github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
|
||||
matrix.target == 'linux'
|
||||
uses: crazy-max/ghaction-github-pages@v3
|
||||
uses: crazy-max/ghaction-github-pages@v1
|
||||
with:
|
||||
build_dir: doc/html
|
||||
env:
|
||||
|
||||
76
.github/workflows/ci_gcc14.yml
vendored
76
.github/workflows/ci_gcc14.yml
vendored
@@ -1,76 +0,0 @@
|
||||
name: GCC 14
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'devel'
|
||||
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04]
|
||||
cpu: [amd64]
|
||||
name: '${{ matrix.os }}'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60 # refs bug #18178
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt update -qq
|
||||
sudo apt remove needrestart
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt 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 (Linux amd64 gcc 14)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo add-apt-repository universe
|
||||
sudo apt update -qq
|
||||
sudo apt install -y gcc-14 g++-14 libpcre3 liblapack-dev
|
||||
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
|
||||
- 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: 'NIM_TESTAMENT_DISABLE_SSL'
|
||||
shell: bash
|
||||
run: echo "NIM_TESTAMENT_DISABLE_SSL=1" >> $GITHUB_ENV
|
||||
|
||||
- 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
|
||||
24
.github/workflows/ci_packages.yml
vendored
24
.github/workflows/ci_packages.yml
vendored
@@ -1,23 +1,12 @@
|
||||
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
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-13]
|
||||
os: [ubuntu-20.04, macos-10.15]
|
||||
cpu: [amd64]
|
||||
batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
|
||||
name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
|
||||
@@ -28,14 +17,14 @@ jobs:
|
||||
NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: 'Install node.js 20.x'
|
||||
uses: actions/setup-node@v4
|
||||
- name: 'Install node.js 16.x'
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '20.x'
|
||||
node-version: '16.x'
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
@@ -48,7 +37,6 @@ jobs:
|
||||
- name: 'Install dependencies (macOS)'
|
||||
if: runner.os == 'macOS'
|
||||
run: brew install boehmgc make sfml gtk+3
|
||||
# XXX can't find boehm and gtk on macos 13
|
||||
- name: 'Install dependencies (Windows)'
|
||||
if: runner.os == 'Windows'
|
||||
shell: bash
|
||||
|
||||
79
.github/workflows/ci_publish.yml
vendored
79
.github/workflows/ci_publish.yml
vendored
@@ -1,79 +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-22.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'
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ''
|
||||
|
||||
- name: 'Install dependencies (Linux amd64)'
|
||||
if: runner.os == 'Linux' && matrix.cpu == 'amd64'
|
||||
run: |
|
||||
sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
sudo apt-fast install --no-install-recommends -yq \
|
||||
libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
|
||||
valgrind libc6-dbg libblas-dev xorg-dev
|
||||
|
||||
- name: 'Add build binaries to PATH'
|
||||
shell: bash
|
||||
run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: '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);
|
||||
}
|
||||
|
||||
25
.github/workflows/stale.yml
vendored
25
.github/workflows/stale.yml
vendored
@@ -1,25 +0,0 @@
|
||||
# https://github.com/actions/stale#usage
|
||||
name: Stale pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Midnight.
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v8
|
||||
with:
|
||||
days-before-pr-stale: 365
|
||||
days-before-pr-close: 30
|
||||
days-before-issue-stale: -1
|
||||
days-before-issue-close: -1
|
||||
exempt-pr-labels: "ARC,bounty,Codegen,Crash,Generics,High Priority,Macros,Next release,Showstopper,Static[T]"
|
||||
exempt-issue-labels: "Showstopper,Severe,bounty,Compiler Crash,Medium Priority"
|
||||
stale-pr-message: >
|
||||
This pull request is stale because it has been open for 1 year with no activity.
|
||||
Contribute more commits on the pull request and rebase it on the latest devel,
|
||||
or it will be closed in 30 days. Thank you for your contributions.
|
||||
close-pr-message: >
|
||||
This pull request has been marked as stale and closed due to inactivity after 395 days.
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -3,9 +3,9 @@
|
||||
!*.*
|
||||
|
||||
# Cache
|
||||
nimcache*/
|
||||
rnimcache*/
|
||||
dnimcache*/
|
||||
nimcache/
|
||||
rnimcache/
|
||||
dnimcache/
|
||||
|
||||
*.o
|
||||
!/icons/*.o
|
||||
@@ -67,7 +67,6 @@ testament.db
|
||||
|
||||
/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
|
||||
@@ -110,5 +109,3 @@ htmldocs
|
||||
nimdoc.out.css
|
||||
# except here:
|
||||
!/nimdoc/testproject/expected/*
|
||||
pkgs/
|
||||
/compiler/compiler/
|
||||
|
||||
62
.gitlab-ci.yml
Normal file
62
.gitlab-ci.yml
Normal file
@@ -0,0 +1,62 @@
|
||||
# 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
|
||||
@@ -1,8 +1,7 @@
|
||||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- 'devel'
|
||||
- 'version-*'
|
||||
- '*'
|
||||
pr:
|
||||
branches:
|
||||
include:
|
||||
@@ -20,32 +19,32 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
Linux_amd64:
|
||||
vmImage: 'ubuntu-24.04'
|
||||
vmImage: 'ubuntu-18.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
|
||||
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-13'
|
||||
vmImage: 'macOS-10.15'
|
||||
CPU: amd64
|
||||
OSX_amd64_cpp:
|
||||
vmImage: 'macOS-13'
|
||||
vmImage: 'macOS-10.15'
|
||||
CPU: amd64
|
||||
NIM_COMPILE_TO_CPP: true
|
||||
Windows_amd64_batch0_3:
|
||||
vmImage: 'windows-2025'
|
||||
vmImage: 'windows-2019'
|
||||
CPU: amd64
|
||||
# see also: `NIM_TEST_PACKAGES`
|
||||
NIM_TESTAMENT_BATCH: "0_3"
|
||||
Windows_amd64_batch1_3:
|
||||
vmImage: 'windows-2025'
|
||||
vmImage: 'windows-2019'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "1_3"
|
||||
Windows_amd64_batch2_3:
|
||||
vmImage: 'windows-2025'
|
||||
vmImage: 'windows-2019'
|
||||
CPU: amd64
|
||||
NIM_TESTAMENT_BATCH: "2_3"
|
||||
|
||||
@@ -80,12 +79,10 @@ jobs:
|
||||
- bash: |
|
||||
set -e
|
||||
. ci/funs.sh
|
||||
echo_run sudo add-apt-repository universe
|
||||
echo_run sudo apt-get update -qq
|
||||
echo_run sudo apt-fast update -qq
|
||||
DEBIAN_FRONTEND='noninteractive' \
|
||||
echo_run sudo apt-get install --no-install-recommends -yq \
|
||||
gcc-14 g++-14 libpcre3 liblapack-dev libpcre3 liblapack-dev libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg
|
||||
echo_run sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
|
||||
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'))
|
||||
|
||||
@@ -102,12 +99,12 @@ jobs:
|
||||
Pin-Priority: 1001
|
||||
EOF
|
||||
|
||||
# echo_run sudo apt-get update -qq
|
||||
echo_run sudo apt-get update -qq || echo "failed, see bug #17343"
|
||||
# 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-get install --no-install-recommends --allow-downgrades -yq \
|
||||
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
|
||||
|
||||
@@ -132,7 +129,6 @@ jobs:
|
||||
- bash: brew install boehmgc make sfml
|
||||
displayName: 'Install dependencies (OSX)'
|
||||
condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin'))
|
||||
# XXX can't find boehm on macos 13
|
||||
|
||||
- bash: |
|
||||
set -e
|
||||
|
||||
@@ -7,14 +7,14 @@ 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
|
||||
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"
|
||||
GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/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
|
||||
|
||||
@@ -3,7 +3,7 @@ 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 @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\nim-gdb.py
|
||||
set @NIM_GDB=gdb.exe
|
||||
|
||||
@echo source %@GDB_PYTHON_MODULE_PATH%> wingdbcommand.txt
|
||||
|
||||
@@ -24,6 +24,6 @@ if not exist %nim_csources% (
|
||||
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
|
||||
bin\nim.exe c --skipUserCfg --skipParentCfg --hints:off koch
|
||||
koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||
koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||
|
||||
@@ -11,7 +11,7 @@ set -e # exit on first error
|
||||
. ci/funs.sh
|
||||
nimBuildCsourcesIfNeeded "$@"
|
||||
|
||||
echo_run bin/nim c --noNimblePath --skipUserCfg --skipParentCfg --hints:off koch
|
||||
echo_run bin/nim c --skipUserCfg --skipParentCfg --hints:off koch
|
||||
echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg --hints:off
|
||||
echo_run ./koch tools --skipUserCfg --skipParentCfg --hints:off
|
||||
|
||||
|
||||
122
changelog.md
122
changelog.md
@@ -1,135 +1,25 @@
|
||||
# v2.2.0 - yyyy-mm-dd
|
||||
# v1.8.x - yyyy-mm-dd
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` was out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backwards compatibility.
|
||||
- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
|
||||
- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
|
||||
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
|
||||
|
||||
[//]: # "Additions:"
|
||||
|
||||
- Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content.
|
||||
- Added `setLenUninit` to system, which doesn't initalize
|
||||
slots when enlarging a sequence.
|
||||
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
|
||||
- Added Viewport API for the JavaScript targets in the `dom` module.
|
||||
- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
|
||||
- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
|
||||
objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
|
||||
instead of `--mm:orc`.
|
||||
- A `$` template is provided for `Path` in `std/paths`.
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
|
||||
|
||||
[//]: # "Removals:"
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
|
||||
|
||||
- The experimental option `--experimental:openSym` has been added to allow
|
||||
captured symbols in generic routine and template bodies respectively to be
|
||||
replaced by symbols injected locally by templates/macros at instantiation
|
||||
time. `bind` may be used to keep the captured symbols over the injected ones
|
||||
regardless of enabling the option, but other methods like renaming the
|
||||
captured symbols should be used instead so that the code is not affected by
|
||||
context changes.
|
||||
|
||||
Since this change may affect runtime behavior, the experimental switch
|
||||
`openSym` needs to be enabled; and a warning is given in the case where an
|
||||
injected symbol would replace a captured symbol not bound by `bind` and
|
||||
the experimental switch isn't enabled.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
body
|
||||
|
||||
proc old[T](): string =
|
||||
foo(123):
|
||||
return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo old[int]() # "captured"
|
||||
|
||||
template oldTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
|
||||
echo oldTempl() # "captured"
|
||||
|
||||
{.experimental: "openSym".}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
assert bar[int]() == "injected" # previously it would be "captured"
|
||||
|
||||
proc baz[T](): string =
|
||||
bind value
|
||||
foo(123):
|
||||
return value
|
||||
assert baz[int]() == "captured"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert barTempl() == "injected" # previously it would be "captured"
|
||||
|
||||
template bazTempl(): string =
|
||||
bind value
|
||||
block:
|
||||
foo(123):
|
||||
value
|
||||
assert bazTempl() == "captured"
|
||||
```
|
||||
|
||||
This option also generates a new node kind `nnkOpenSym` which contains
|
||||
exactly 1 `nnkSym` node. In the future this might be merged with a slightly
|
||||
modified `nnkOpenSymChoice` node but macros that want to support the
|
||||
experimental feature should still handle `nnkOpenSym`, as the node kind would
|
||||
simply not be generated as opposed to being removed.
|
||||
|
||||
Another experimental switch `genericsOpenSym` exists that enables this behavior
|
||||
at instantiation time, meaning templates etc can enable it specifically when
|
||||
they are being called. However this does not generate `nnkOpenSym` nodes
|
||||
(unless the other switch is enabled) and so doesn't reflect the regular
|
||||
behavior of the switch.
|
||||
|
||||
```nim
|
||||
const value = "captured"
|
||||
template foo(x: int, body: untyped): untyped =
|
||||
let value {.inject.} = "injected"
|
||||
{.push experimental: "genericsOpenSym".}
|
||||
body
|
||||
{.pop.}
|
||||
|
||||
proc bar[T](): string =
|
||||
foo(123):
|
||||
return value
|
||||
echo bar[int]() # "injected"
|
||||
|
||||
template barTempl(): string =
|
||||
block:
|
||||
var res: string
|
||||
foo(123):
|
||||
res = value
|
||||
res
|
||||
assert barTempl() == "injected"
|
||||
```
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- `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.
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package in tow.
|
||||
|
||||
|
||||
|
||||
@@ -282,7 +282,7 @@
|
||||
- Removed the deprecated `asyncdispatch.newAsyncNativeSocket`.
|
||||
- Removed the deprecated `dom.releaseEvents` and `dom.captureEvents`.
|
||||
|
||||
- Removed `sharedlist.initSharedList`, was deprecated and produces undefined behaviour.
|
||||
- Removed `sharedlists.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)
|
||||
|
||||
@@ -1,330 +0,0 @@
|
||||
# v2.0.0 - 2023-08-01
|
||||
|
||||
Version 2.0 is a big milestone with too many changes to list them all here.
|
||||
|
||||
For a full list see [details](changelog_2_0_0_details.html).
|
||||
|
||||
|
||||
## New features
|
||||
|
||||
### Better tuple unpacking
|
||||
|
||||
Tuple unpacking for variables is now treated as syntax sugar that directly
|
||||
expands into multiple assignments. Along with this, tuple unpacking for
|
||||
variables can now be nested.
|
||||
|
||||
```nim
|
||||
proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
|
||||
|
||||
# Now nesting is supported!
|
||||
let (x, (_, y), _, z) = returnsNestedTuple()
|
||||
|
||||
```
|
||||
|
||||
### Improved type inference
|
||||
|
||||
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")]
|
||||
```
|
||||
|
||||
### Forbidden Tags
|
||||
|
||||
[Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition
|
||||
of forbidden tags by the `.forbids` pragma which can be used to disable certain effects in proc types.
|
||||
|
||||
For example:
|
||||
|
||||
```nim
|
||||
|
||||
type IO = object ## input/output effect
|
||||
proc readLine(): string {.tags: [IO].} = discard
|
||||
proc echoLine(): void = discard
|
||||
|
||||
proc no_IO_please() {.forbids: [IO].} =
|
||||
# this is OK because it didn't define any tag:
|
||||
echoLine()
|
||||
# the compiler prevents this:
|
||||
let y = readLine()
|
||||
|
||||
```
|
||||
|
||||
### New standard library modules
|
||||
|
||||
The famous `os` module got an overhaul. Several of its features are available
|
||||
under a new interface that introduces a `Path` abstraction. A `Path` is
|
||||
a `distinct string`, which improves the type safety when dealing with paths, files
|
||||
and directories.
|
||||
|
||||
Use:
|
||||
|
||||
- `std/oserrors` for OS error reporting.
|
||||
- `std/envvars` for environment variables handling.
|
||||
- `std/paths` for path handling.
|
||||
- `std/dirs` for directory creation/deletion/traversal.
|
||||
- `std/files` for file existence checking, file deletions and moves.
|
||||
- `std/symlinks` for symlink handling.
|
||||
- `std/appdirs` for accessing configuration/home/temp directories.
|
||||
- `std/cmdline` for reading command line parameters.
|
||||
|
||||
### Consistent underscore handling
|
||||
|
||||
The underscore identifier (`_`) is now generally not added to scope when
|
||||
used as the name of a definition. While this was already the case for
|
||||
variables, it is now also the case for routine parameters, generic
|
||||
parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
|
||||
|
||||
```nim
|
||||
proc foo(_: int): int = _ + 1
|
||||
echo foo(1)
|
||||
|
||||
proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
|
||||
echo foo[int]()
|
||||
|
||||
proc _() = echo "_"
|
||||
_()
|
||||
|
||||
type _ = int
|
||||
let x: _ = 3
|
||||
```
|
||||
|
||||
Whereas the following code now compiles:
|
||||
|
||||
```nim
|
||||
proc foo(_, _: int): int = 123
|
||||
echo foo(1, 2)
|
||||
|
||||
proc foo[_, _](): int = 123
|
||||
echo foo[int, bool]()
|
||||
|
||||
proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
|
||||
echo foo(int, bool)
|
||||
|
||||
proc _() = echo "one"
|
||||
proc _() = echo "two"
|
||||
|
||||
type _ = int
|
||||
type _ = float
|
||||
```
|
||||
|
||||
### JavaScript codegen improvement
|
||||
|
||||
The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
|
||||
for 64-bit integer types (`int64` and `uint64`) by default. As this affects
|
||||
JS code generation, code using these types to interface with the JS backend
|
||||
may need to be updated. Note that `int` and `uint` are not affected.
|
||||
|
||||
For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
|
||||
and in the case of potential bugs with the new implementation, the
|
||||
old behavior is currently still supported with the command line option
|
||||
`--jsbigint64:off`.
|
||||
|
||||
|
||||
## Docgen improvements
|
||||
|
||||
`Markdown` is now the default markup language of doc comments (instead
|
||||
of the legacy `RstMarkdown` mode). In this release we begin to separate
|
||||
RST and Markdown features to better follow specification of each
|
||||
language, with the focus on Markdown development.
|
||||
See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
|
||||
|
||||
* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
|
||||
select the markup language mode in the doc comments of the current `.nim`
|
||||
file for processing by `nim doc`:
|
||||
|
||||
1. `Markdown` (default) is basically CommonMark (standard Markdown) +
|
||||
some Pandoc Markdown features + some RST features that are missing
|
||||
in our current implementation of CommonMark and Pandoc Markdown.
|
||||
2. `RST` closely follows the RST spec with few additional Nim features.
|
||||
3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
|
||||
is kept for the sake of compatibility and ease of migration.
|
||||
|
||||
* Added separate `md2html` and `rst2html` commands for processing
|
||||
standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
|
||||
|
||||
* Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
|
||||
* Docgen now supports concise syntax for referencing Nim symbols:
|
||||
instead of specifying HTML anchors directly one can use original
|
||||
Nim symbol declarations (adding the aforementioned link brackets
|
||||
`[...]` around them).
|
||||
* To use this feature across modules, a new `importdoc` directive was added.
|
||||
Using this feature for referencing also helps to ensure that links
|
||||
(inside one module or the whole project) are not broken.
|
||||
* Added support for RST & Markdown quote blocks (blocks starting with `>`).
|
||||
* Added a popular Markdown definition lists extension.
|
||||
* Added Markdown indented code blocks (blocks indented by >= 4 spaces).
|
||||
* Added syntax for additional parameters to Markdown code blocks:
|
||||
|
||||
```nim test="nim c $1"
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
## C++ interop enhancements
|
||||
|
||||
Nim 2.0 takes C++ interop to the next level. With the new [virtual](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma and the extended [constructor](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
|
||||
Now one can define constructors and virtual procs that maps to C++ constructors and virtual methods, allowing one to further customize
|
||||
the interoperability. There is also extended support for the [codeGenDecl](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-codegendecl-pragma) pragma, so that it works on types.
|
||||
|
||||
It's a common pattern in C++ to use inheritance to extend a library. Some even use multiple inheritance as a mechanism to make interfaces.
|
||||
|
||||
Consider the following example:
|
||||
|
||||
```cpp
|
||||
|
||||
struct Base {
|
||||
int someValue;
|
||||
Base(int inValue) {
|
||||
someValue = inValue;
|
||||
};
|
||||
};
|
||||
|
||||
class IPrinter {
|
||||
public:
|
||||
virtual void print() = 0;
|
||||
};
|
||||
```
|
||||
|
||||
```nim
|
||||
|
||||
type
|
||||
Base* {.importcpp, inheritable.} = object
|
||||
someValue*: int32
|
||||
IPrinter* {.importcpp.} = object
|
||||
|
||||
const objTemplate = """
|
||||
struct $1 : public $3, public IPrinter {
|
||||
$2
|
||||
};
|
||||
""";
|
||||
|
||||
type NimChild {.codegenDecl: objTemplate .} = object of Base
|
||||
|
||||
proc makeNimChild(val: int32): NimChild {.constructor: "NimClass('1 #1) : Base(#1)".} =
|
||||
echo "It calls the base constructor passing " & $this.someValue
|
||||
this.someValue = val * 2 # Notice how we can access `this` inside the constructor. It's of the type `ptr NimChild`.
|
||||
|
||||
proc print*(self: NimChild) {.virtual.} =
|
||||
echo "Some value is " & $self.someValue
|
||||
|
||||
let child = makeNimChild(10)
|
||||
child.print()
|
||||
```
|
||||
|
||||
It outputs:
|
||||
|
||||
```
|
||||
It calls the base constructor passing 10
|
||||
Some value is 20
|
||||
```
|
||||
|
||||
|
||||
## ARC/ORC refinements
|
||||
|
||||
With the 2.0 release, the ARC/ORC model got refined once again and is now finally complete:
|
||||
|
||||
1. Programmers now have control over the "item was moved from" state as `=wasMoved` is overridable.
|
||||
2. There is a new `=dup` hook which is more efficient than the old combination of `=wasMoved(tmp); =copy(tmp, x)` operations.
|
||||
3. Destructors now take a parameter of the attached object type `T` directly and don't have to take a `var T` parameter.
|
||||
|
||||
With these important optimizations we improved the runtime of the compiler and important benchmarks by 0%! Wait ... what?
|
||||
Yes, unfortunately it turns out that for a modern optimizer like in GCC or LLVM there is no difference.
|
||||
|
||||
But! This refined model is more efficient once separate compilation enters the picture. In other words, as we think of
|
||||
providing a stable ABI it is important not to lose any efficiency in the calling conventions.
|
||||
|
||||
|
||||
## Tool changes
|
||||
|
||||
- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
|
||||
- nimgrep now offers the option `--inContext` (and `--notInContext`), which
|
||||
allows to filter only matches with the context block containing a given pattern.
|
||||
- nimgrep: names of options containing "include/exclude" are deprecated,
|
||||
e.g. instead of `--includeFile` and `--excludeFile` we have
|
||||
`--filename` and `--notFilename` respectively.
|
||||
Also, the semantics are now consistent for such positive/negative filters.
|
||||
- Nim now ships with an alternative package manager called Atlas. More on this in upcoming versions.
|
||||
|
||||
|
||||
## Porting guide
|
||||
|
||||
### Block and Break
|
||||
|
||||
Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. In other words, turn:
|
||||
|
||||
```nim
|
||||
|
||||
block:
|
||||
a()
|
||||
if cond:
|
||||
break
|
||||
b()
|
||||
|
||||
```
|
||||
|
||||
Into:
|
||||
|
||||
```nim
|
||||
|
||||
block maybePerformB:
|
||||
a()
|
||||
if cond:
|
||||
break maybePerformB
|
||||
b()
|
||||
|
||||
```
|
||||
|
||||
### Strict funcs
|
||||
|
||||
The definition of `"strictFuncs"` was changed.
|
||||
The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter".
|
||||
The new definition is: "A store to a ref/ptr deref is forbidden."
|
||||
|
||||
This new definition is much easier to understand, the price is some expressitivity. The following code used to be
|
||||
accepted:
|
||||
|
||||
```nim
|
||||
|
||||
{.experimental: "strictFuncs".}
|
||||
|
||||
type Node = ref object
|
||||
s: string
|
||||
|
||||
func create(s: string): Node =
|
||||
result = Node()
|
||||
result.s = s # store to result[]
|
||||
|
||||
```
|
||||
|
||||
Now it has to be rewritten to:
|
||||
|
||||
```nim
|
||||
|
||||
{.experimental: "strictFuncs".}
|
||||
|
||||
type Node = ref object
|
||||
s: string
|
||||
|
||||
func create(s: string): Node =
|
||||
result = Node(s: s)
|
||||
|
||||
```
|
||||
|
||||
### Standard library
|
||||
|
||||
Several standard library modules have been moved to nimble packages, use `nimble` or `atlas` to install them:
|
||||
|
||||
- `std/punycode` => `punycode`
|
||||
- `std/asyncftpclient` => `asyncftpclient`
|
||||
- `std/smtp` => `smtp`
|
||||
- `std/db_common` => `db_connector/db_common`
|
||||
- `std/db_sqlite` => `db_connector/db_sqlite`
|
||||
- `std/db_mysql` => `db_connector/db_mysql`
|
||||
- `std/db_postgres` => `db_connector/db_postgres`
|
||||
- `std/db_odbc` => `db_connector/db_odbc`
|
||||
- `std/md5` => `checksums/md5`
|
||||
- `std/sha1` => `checksums/sha1`
|
||||
- `std/sums` => `sums`
|
||||
@@ -1,560 +0,0 @@
|
||||
# v2.0.0 - 2023-08-01
|
||||
|
||||
|
||||
## Changes affecting backward compatibility
|
||||
|
||||
- ORC is now the default memory management strategy. Use
|
||||
`--mm:refc` for a transition period.
|
||||
|
||||
- The `threads:on` option is now the default.
|
||||
|
||||
- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache's `HttpClient` (Java), `http` (go) and .NET `HttpWebResponse` (C#) behaviors. Previously it raised a `ValueError`.
|
||||
|
||||
- `addr` is now available for all addressable locations,
|
||||
`unsafeAddr` is now deprecated and an alias for `addr`.
|
||||
|
||||
- Certain definitions from the default `system` module have been moved to
|
||||
the following new modules:
|
||||
|
||||
- `std/syncio`
|
||||
- `std/assertions`
|
||||
- `std/formatfloat`
|
||||
- `std/objectdollar`
|
||||
- `std/widestrs`
|
||||
- `std/typedthreads`
|
||||
- `std/sysatomics`
|
||||
|
||||
In the future, these definitions will be removed from the `system` module,
|
||||
and their respective modules will have to be imported to use them.
|
||||
Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option
|
||||
may be used.
|
||||
|
||||
- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated
|
||||
symbols in the `system` module:
|
||||
- Aliases with an `Error` suffix to exception types that have a `Defect` suffix
|
||||
(see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)):
|
||||
`ArithmeticError`, `DivByZeroError`, `OverflowError`,
|
||||
`AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`,
|
||||
`FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`,
|
||||
`ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`,
|
||||
`FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`,
|
||||
`DeadThreadError`, `NilAccessError`
|
||||
- `addQuitProc`, replaced by `exitprocs.addExitProc`
|
||||
- Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32`
|
||||
- `TaintedString`, formerly a distinct alias to `string`
|
||||
- `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to
|
||||
`ptr int32`, `ptr int64`, `ptr float32`, `ptr float64`
|
||||
|
||||
- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in
|
||||
in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command `nimble install threading` and import `threading/channels`.
|
||||
|
||||
- Enabling `-d:nimPreviewCstringConversion` causes `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` to not support conversion to `cstring` anymore.
|
||||
|
||||
- Enabling `-d:nimPreviewProcConversion` causes `proc` to not support conversion to
|
||||
`pointer` anymore. `cast` may be used instead.
|
||||
|
||||
- The `gc:v2` option is removed.
|
||||
|
||||
- The `mainmodule` and `m` options are removed.
|
||||
|
||||
- Optional parameters in combination with `: body` syntax ([RFC #405](https://github.com/nim-lang/RFCs/issues/405))
|
||||
are now opt-in via `experimental:flexibleOptionalParams`.
|
||||
|
||||
- Automatic dereferencing (experimental feature) is removed.
|
||||
|
||||
- The `Math.trunc` polyfill for targeting Internet Explorer was
|
||||
previously included in most JavaScript output files.
|
||||
Now, it is only included with `-d:nimJsMathTruncPolyfill`.
|
||||
If you are targeting Internet Explorer, you may choose to enable this option
|
||||
or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma).
|
||||
Nim uses `Math.trunc` for the division and modulo operators for integers.
|
||||
|
||||
- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and
|
||||
`sink` for optimization purposes.
|
||||
|
||||
- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fulfill its promises.
|
||||
|
||||
- The `{.this.}` pragma, deprecated since 0.19, has been removed.
|
||||
- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead.
|
||||
```nim
|
||||
type Foo = distinct ptr int
|
||||
|
||||
# Before:
|
||||
var x: Foo = nil
|
||||
# After:
|
||||
var x: Foo = Foo(nil)
|
||||
```
|
||||
- Removed two type pragma syntaxes deprecated since 0.20, namely
|
||||
`type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. Instead,
|
||||
use `type Foo[T] {.final.} = object`.
|
||||
|
||||
- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent
|
||||
with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`.
|
||||
This decision was made with the assumption that the old syntax was used rarely;
|
||||
if your code used the old syntax, please be aware of this change.
|
||||
|
||||
- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators
|
||||
are no longer experimental.
|
||||
|
||||
- `macros.getImpl` for `const` symbols now returns the full definition node
|
||||
(as `nnkConstDef`) rather than the AST of the constant value.
|
||||
|
||||
- Lock levels are deprecated, now a noop.
|
||||
|
||||
- `strictEffects` are no longer experimental.
|
||||
Use `legacy:laxEffects` to keep backward compatibility.
|
||||
|
||||
- The `gorge`/`staticExec` calls will now return a descriptive message in the output
|
||||
if the execution fails for whatever reason. To get back legacy behaviour, use `-d:nimLegacyGorgeErrors`.
|
||||
|
||||
- Pointer to `cstring` conversions now trigger a `[PtrToCstringConv]` warning.
|
||||
This warning will become an error in future versions! Use a `cast` operation
|
||||
like `cast[cstring](x)` instead.
|
||||
|
||||
- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`.
|
||||
|
||||
- Redefining templates with the same signature was previously
|
||||
allowed to support certain macro code. To do this explicitly, the
|
||||
`{.redefine.}` pragma has been added. Note that this is only for templates.
|
||||
Implicit redefinition of templates is now deprecated and will give an error in the future.
|
||||
|
||||
- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead.
|
||||
|
||||
- Several Standard libraries have been moved to nimble packages, use `nimble` to install them:
|
||||
- `std/punycode` => `punycode`
|
||||
- `std/asyncftpclient` => `asyncftpclient`
|
||||
- `std/smtp` => `smtp`
|
||||
- `std/db_common` => `db_connector/db_common`
|
||||
- `std/db_sqlite` => `db_connector/db_sqlite`
|
||||
- `std/db_mysql` => `db_connector/db_mysql`
|
||||
- `std/db_postgres` => `db_connector/db_postgres`
|
||||
- `std/db_odbc` => `db_connector/db_odbc`
|
||||
- `std/md5` => `checksums/md5`
|
||||
- `std/sha1` => `checksums/sha1`
|
||||
- `std/sums` => `std/sums`
|
||||
|
||||
- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of
|
||||
`foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`.
|
||||
This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead.
|
||||
|
||||
- When `--warning[BareExcept]:on` is enabled, if an `except` specifies no exception or any exception not inheriting from `Defect` or `CatchableError`, a `warnBareExcept` warning will be triggered. For example, the following code will emit a warning:
|
||||
```nim
|
||||
try:
|
||||
discard
|
||||
except: # Warning: The bare except clause is deprecated; use `except CatchableError:` instead [BareExcept]
|
||||
discard
|
||||
```
|
||||
|
||||
- The experimental `strictFuncs` feature now disallows a store to the heap via a `ref` or `ptr` indirection.
|
||||
|
||||
- The underscore identifier (`_`) is now generally not added to scope when
|
||||
used as the name of a definition. While this was already the case for
|
||||
variables, it is now also the case for routine parameters, generic
|
||||
parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
|
||||
|
||||
```nim
|
||||
proc foo(_: int): int = _ + 1
|
||||
echo foo(1)
|
||||
|
||||
proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
|
||||
echo foo[int]()
|
||||
|
||||
proc _() = echo "_"
|
||||
_()
|
||||
|
||||
type _ = int
|
||||
let x: _ = 3
|
||||
```
|
||||
|
||||
Whereas the following code now compiles:
|
||||
|
||||
```nim
|
||||
proc foo(_, _: int): int = 123
|
||||
echo foo(1, 2)
|
||||
|
||||
proc foo[_, _](): int = 123
|
||||
echo foo[int, bool]()
|
||||
|
||||
proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
|
||||
echo foo(int, bool)
|
||||
|
||||
proc _() = echo "one"
|
||||
proc _() = echo "two"
|
||||
|
||||
type _ = int
|
||||
type _ = float
|
||||
```
|
||||
|
||||
- Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
|
||||
|
||||
- The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
|
||||
for 64-bit integer types (`int64` and `uint64`) by default. As this affects
|
||||
JS code generation, code using these types to interface with the JS backend
|
||||
may need to be updated. Note that `int` and `uint` are not affected.
|
||||
|
||||
For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
|
||||
and in the case of potential bugs with the new implementation, the
|
||||
old behavior is currently still supported with the command line option
|
||||
`--jsbigint64:off`.
|
||||
|
||||
- The `proc` and `iterator` type classes now respectively only match
|
||||
procs and iterators. Previously both type classes matched any of
|
||||
procs or iterators.
|
||||
|
||||
```nim
|
||||
proc prc(): int =
|
||||
123
|
||||
|
||||
iterator iter(): int =
|
||||
yield 123
|
||||
|
||||
proc takesProc[T: proc](x: T) = discard
|
||||
proc takesIter[T: iterator](x: T) = discard
|
||||
|
||||
# always compiled:
|
||||
takesProc(prc)
|
||||
takesIter(iter)
|
||||
# no longer compiles:
|
||||
takesProc(iter)
|
||||
takesIter(prc)
|
||||
```
|
||||
|
||||
- The `proc` and `iterator` type classes now accept a calling convention pragma
|
||||
(i.e. `proc {.closure.}`) that must be shared by matching proc or iterator
|
||||
types. Previously, pragmas were parsed but discarded if no parameter list
|
||||
was given.
|
||||
|
||||
This is represented in the AST by an `nnkProcTy`/`nnkIteratorTy` node with
|
||||
an `nnkEmpty` node in the place of the `nnkFormalParams` node, and the pragma
|
||||
node in the same place as in a concrete `proc` or `iterator` type node. This
|
||||
state of the AST may be unexpected to existing code, both due to the
|
||||
replacement of the `nnkFormalParams` node as well as having child nodes
|
||||
unlike other type class AST.
|
||||
|
||||
- Signed integer literals in `set` literals now default to a range type of
|
||||
`0..255` instead of `0..65535` (the maximum size of sets).
|
||||
|
||||
- `case` statements with `else` branches put before `elif`/`of` branches in macros
|
||||
are rejected with "invalid order of case branches".
|
||||
|
||||
- Destructors now default to `.raises: []` (i.e. destructors must not raise
|
||||
unlisted exceptions) and explicitly raising destructors are implementation
|
||||
defined behavior.
|
||||
|
||||
- The very old, undocumented `deprecated` pragma statement syntax for
|
||||
deprecated aliases is now a no-op. The regular deprecated pragma syntax is
|
||||
generally sufficient instead.
|
||||
|
||||
```nim
|
||||
# now does nothing:
|
||||
{.deprecated: [OldName: NewName].}
|
||||
|
||||
# instead use:
|
||||
type OldName* {.deprecated: "use NewName instead".} = NewName
|
||||
const oldName* {.deprecated: "use newName instead".} = newName
|
||||
```
|
||||
|
||||
`defined(nimalias)` can be used to check for versions when this syntax was
|
||||
available; however since code that used this syntax is usually very old,
|
||||
these deprecated aliases are likely not used anymore and it may make sense
|
||||
to simply remove these statements.
|
||||
|
||||
- `getProgramResult` and `setProgramResult` in `std/exitprocs` are no longer
|
||||
declared when they are not available on the backend. Previously it would call
|
||||
`doAssert false` at runtime despite the condition being checkable at compile-time.
|
||||
|
||||
- Custom destructors now supports non-var parameters, e.g. ``proc `=destroy`[T: object](x: T)`` is valid. ``proc `=destroy`[T: object](x: var T)`` is deprecated.
|
||||
|
||||
- Relative imports will not resolve to searched paths anymore, e.g. `import ./tables` now reports an error properly.
|
||||
|
||||
## Standard library additions and changes
|
||||
|
||||
[//]: # "Changes:"
|
||||
- OpenSSL 3 is now supported.
|
||||
- `macros.parseExpr` and `macros.parseStmt` now accept an optional
|
||||
`filename` argument for more informative errors.
|
||||
- The `colors` module is expanded with missing colors from the CSS color standard.
|
||||
`colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard.
|
||||
- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
|
||||
- The `md5` module now works at compile time and in JavaScript.
|
||||
- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef`, to support `const` tables.
|
||||
- `strutils.find` now uses and defaults to `last = -1` for whole string searches,
|
||||
making limiting it to just the first char (`last = 0`) valid.
|
||||
- `strutils.split` and `strutils.rsplit` now return the source string as a single element for an empty separator.
|
||||
- `random.rand` now works with `Ordinal`s.
|
||||
- Undeprecated `os.isvalidfilename`.
|
||||
- `std/oids` now uses `int64` to store time internally (before, it was int32).
|
||||
- `std/uri.Uri` dollar (`$`) improved, precalculates the `string` result length from the `Uri`.
|
||||
- `std/uri.Uri.isIpv6` is now exported.
|
||||
- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2.
|
||||
|
||||
|
||||
- `std/jsfetch.newFetchOptions` now has default values for all parameters.
|
||||
- `std/jsformdata` now accepts the `Blob` data type.
|
||||
|
||||
- `std/sharedlist` and `std/sharedtables` are now deprecated, see [RFC #433](https://github.com/nim-lang/RFCs/issues/433).
|
||||
|
||||
- There is a new compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove the dependency on the Linux `getrandom` syscall.
|
||||
|
||||
This compile flag only affects Linux builds and is necessary if either compiling on a Linux kernel version < 3.17, or if code built will be executing on kernel < 3.17.
|
||||
|
||||
On Linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since Linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall.
|
||||
|
||||
When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be:
|
||||
```sh
|
||||
$ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details
|
||||
$ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8
|
||||
$ sh build.sh # per unix install instructions
|
||||
$ bin/nim c koch # per unix install instructions
|
||||
$ ./koch boot -d:release # per unix install instructions
|
||||
$ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall
|
||||
```
|
||||
|
||||
This is necessary to pass when building Nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for the stdlib (`sysrand` in particular).
|
||||
|
||||
[//]: # "Additions:"
|
||||
- Added ISO 8601 week date utilities in `times`:
|
||||
- Added `IsoWeekRange`, a range type for weeks in a week-based year.
|
||||
- Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year.
|
||||
- Added an `initDateTime` overload to create a `DateTime` from an ISO week date.
|
||||
- Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime.
|
||||
- Added `getIsoWeeksInYear` to return the number of weeks in a week-based year.
|
||||
- Added new modules which were previously part of `std/os`:
|
||||
- Added `std/oserrors` for OS error reporting.
|
||||
- Added `std/envvars` for environment variables handling.
|
||||
- Added `std/cmdline` for reading command line parameters.
|
||||
- Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`.
|
||||
- Added `sep` parameter in `std/uri` to specify the query separator.
|
||||
- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`.
|
||||
- Added `complex.sgn` for obtaining the phase of complex numbers.
|
||||
- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`,
|
||||
`after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`,
|
||||
`hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`,
|
||||
`replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`,
|
||||
`toggleAttribute`, and `matches` to `std/dom`.
|
||||
- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices).
|
||||
- Added `capacity` for `string` and `seq` to return the current capacity, see [RFC #460](https://github.com/nim-lang/RFCs/issues/460).
|
||||
- Added `openArray[char]` overloads for `std/parseutils` and `std/unicode`, allowing for more code reuse.
|
||||
- Added a `safe` parameter to `base64.encodeMime`.
|
||||
- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes.
|
||||
- Added `minmax` to `sequtils`, as a more efficient `(min(_), max(_))` over sequences.
|
||||
- `std/jscore` for the JavaScript target:
|
||||
+ Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift)
|
||||
and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask).
|
||||
+ Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`.
|
||||
- Added `BackwardsIndex` overload for `CacheSeq`.
|
||||
- Added support for nested `with` blocks in `std/with`.
|
||||
- Added `ensureMove` to the system module. It ensures that the passed argument is moved, otherwise an error is given at the compile time.
|
||||
|
||||
|
||||
[//]: # "Deprecations:"
|
||||
- Deprecated `selfExe` for Nimscript.
|
||||
- Deprecated `std/base64.encode` for collections of arbitrary integer element type.
|
||||
Now only `byte` and `char` are supported.
|
||||
|
||||
[//]: # "Removals:"
|
||||
- Removed deprecated module `parseopt2`.
|
||||
- Removed deprecated module `sharedstrings`.
|
||||
- Removed deprecated module `dom_extensions`.
|
||||
- Removed deprecated module `LockFreeHash`.
|
||||
- Removed deprecated module `events`.
|
||||
- Removed deprecated `oids.oidToString`.
|
||||
- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`.
|
||||
- Removed deprecated `jsre.test` and `jsre.toString`.
|
||||
- Removed deprecated `math.c_frexp`.
|
||||
- Removed deprecated `` httpcore.`==` ``.
|
||||
- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that take wrong argument types.
|
||||
- Removed deprecated `osproc.poDemon`, symbol with typo.
|
||||
- Removed deprecated `tables.rightSize`.
|
||||
|
||||
|
||||
- Removed deprecated `posix.CLONE_STOPPED`.
|
||||
|
||||
|
||||
## Language changes
|
||||
|
||||
- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition of forbidden tags by the `.forbids` pragma
|
||||
which can be used to disable certain effects in proc types.
|
||||
- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental,
|
||||
meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them.
|
||||
- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed
|
||||
for the right-hand side of type definitions in type sections. Previously
|
||||
they would error with "invalid indentation".
|
||||
|
||||
- Compile-time define changes:
|
||||
- `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`.
|
||||
In the command line, this is defined as `-d:a.b.c`. Older versions can
|
||||
use backticks as in ``defined(`a.b.c`)`` to access such defines.
|
||||
- [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas)
|
||||
now support a string argument for qualified define names.
|
||||
|
||||
```nim
|
||||
# -d:package.FooBar=42
|
||||
const FooBar {.intdefine: "package.FooBar".}: int = 5
|
||||
echo FooBar # 42
|
||||
```
|
||||
|
||||
This was added to help disambiguate similar define names for different packages.
|
||||
In older versions, this could only be achieved with something like the following:
|
||||
|
||||
```nim
|
||||
const FooBar = block:
|
||||
const `package.FooBar` {.intdefine.}: int = 5
|
||||
`package.FooBar`
|
||||
```
|
||||
- A generic `define` pragma for constants has been added that interprets
|
||||
the value of the define based on the type of the constant value.
|
||||
See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-nimdefine-pragma)
|
||||
for a list of supported types.
|
||||
|
||||
- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes:
|
||||
- Templates now accept macro pragmas.
|
||||
- Macro pragmas for var/let/const sections have been redesigned in a way that works
|
||||
similarly to routine macro pragmas. The new behavior is documented in the
|
||||
[experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas).
|
||||
- Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
|
||||
allowing multiple type definitions to be injected in place of the original type definition.
|
||||
|
||||
```nim
|
||||
import macros
|
||||
|
||||
macro multiply(amount: static int, s: untyped): untyped =
|
||||
let name = $s[0].basename
|
||||
result = newNimNode(nnkTypeSection)
|
||||
for i in 1 .. amount:
|
||||
result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
|
||||
|
||||
type
|
||||
Foo = object
|
||||
Bar {.multiply: 3.} = object
|
||||
x, y, z: int
|
||||
Baz = object
|
||||
# becomes
|
||||
type
|
||||
Foo = object
|
||||
Bar1 = object
|
||||
x, y, z: int
|
||||
Bar2 = object
|
||||
x, y, z: int
|
||||
Bar3 = object
|
||||
x, y, z: int
|
||||
Baz = object
|
||||
```
|
||||
|
||||
- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference)
|
||||
has been implemented for a variety of basic cases. For example, code like the following now compiles:
|
||||
|
||||
```nim
|
||||
let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
|
||||
```
|
||||
|
||||
- `cstring` is now accepted as a selector in `case` statements, removing the
|
||||
need to convert to `string`. On the JS backend, this is translated directly
|
||||
to a `switch` statement.
|
||||
|
||||
- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters).
|
||||
- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`.
|
||||
|
||||
- IBM Z architecture and macOS m1 arm64 architecture are supported.
|
||||
|
||||
- `=wasMoved` can now be overridden by users.
|
||||
|
||||
- There is a new pragma called [quirky](https://nim-lang.github.io/Nim/manual_experimental.html#quirky-routines) that can be used to affect the code
|
||||
generation of goto based exception handling. It can improve the produced code size but its effects can be subtle so use it with care.
|
||||
|
||||
- Tuple unpacking for variables is now treated as syntax sugar that directly
|
||||
expands into multiple assignments. Along with this, tuple unpacking for
|
||||
variables can now be nested.
|
||||
|
||||
```nim
|
||||
proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
|
||||
|
||||
let (x, (_, y), _, z) = returnsNestedTuple()
|
||||
# roughly becomes
|
||||
let
|
||||
tmpTup1 = returnsNestedTuple()
|
||||
x = tmpTup1[0]
|
||||
tmpTup2 = tmpTup1[1]
|
||||
y = tmpTup2[1]
|
||||
z = tmpTup1[3]
|
||||
```
|
||||
|
||||
As a result `nnkVarTuple` nodes in variable sections will no longer be
|
||||
reflected in `typed` AST.
|
||||
|
||||
- C++ interoperability:
|
||||
- New [`virtual`](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma added.
|
||||
- Improvements to [`constructor`](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
|
||||
|
||||
## Compiler changes
|
||||
|
||||
- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the
|
||||
reality better. (Nim moved away from all techniques based on "tracing".)
|
||||
|
||||
- Defines the `gcRefc` symbol which allows writing specific code for the refc GC.
|
||||
|
||||
- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`,
|
||||
without requiring `-d:nimVersion140` which is now a noop.
|
||||
|
||||
- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package.
|
||||
|
||||
- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and
|
||||
related functions produced on the backend. This prevents conflicts with other Nim
|
||||
static libraries.
|
||||
|
||||
- When compiling for release, the flag `-fno-math-errno` is used for GCC.
|
||||
- Removed deprecated `LineTooLong` hint.
|
||||
- Line numbers and file names of source files work correctly inside templates for JavaScript targets.
|
||||
|
||||
- Removed support for LCC (Local C), Pelles C, Digital Mars and Watcom compilers.
|
||||
|
||||
|
||||
## Docgen
|
||||
|
||||
- `Markdown` is now the default markup language of doc comments (instead
|
||||
of the legacy `RstMarkdown` mode). In this release we begin to separate
|
||||
RST and Markdown features to better follow specification of each
|
||||
language, with the focus on Markdown development.
|
||||
See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
|
||||
|
||||
* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
|
||||
select the markup language mode in the doc comments of the current `.nim`
|
||||
file for processing by `nim doc`:
|
||||
|
||||
1. `Markdown` (default) is basically CommonMark (standard Markdown) +
|
||||
some Pandoc Markdown features + some RST features that are missing
|
||||
in our current implementation of CommonMark and Pandoc Markdown.
|
||||
2. `RST` closely follows the RST spec with few additional Nim features.
|
||||
3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
|
||||
is kept for the sake of compatibility and ease of migration.
|
||||
|
||||
* Added separate `md2html` and `rst2html` commands for processing
|
||||
standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
|
||||
|
||||
- Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
|
||||
- Docgen now supports concise syntax for referencing Nim symbols:
|
||||
instead of specifying HTML anchors directly one can use original
|
||||
Nim symbol declarations (adding the aforementioned link brackets
|
||||
`[...]` around them).
|
||||
* To use this feature across modules, a new `importdoc` directive was added.
|
||||
Using this feature for referencing also helps to ensure that links
|
||||
(inside one module or the whole project) are not broken.
|
||||
- Added support for RST & Markdown quote blocks (blocks starting with `>`).
|
||||
- Added a popular Markdown definition lists extension.
|
||||
- Added Markdown indented code blocks (blocks indented by >= 4 spaces).
|
||||
- Added syntax for additional parameters to Markdown code blocks:
|
||||
|
||||
```nim test="nim c $1"
|
||||
...
|
||||
```
|
||||
|
||||
## Tool changes
|
||||
|
||||
- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
|
||||
- nimgrep added the option `--inContext` (and `--notInContext`), which
|
||||
allows to filter only matches with the context block containing a given pattern.
|
||||
- nimgrep: names of options containing "include/exclude" are deprecated,
|
||||
e.g. instead of `--includeFile` and `--excludeFile` we have
|
||||
`--filename` and `--notFilename` respectively.
|
||||
Also the semantics are now consistent for such positive/negative filters.
|
||||
- koch now supports the `--skipIntegrityCheck` option. The command `koch --skipIntegrityCheck boot -d:release` always builds the compiler twice.
|
||||
@@ -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()
|
||||
@@ -58,11 +58,7 @@ _nimNumCpu(){
|
||||
# FreeBSD | macOS: $(sysctl -n hw.ncpu)
|
||||
# OpenBSD: $(sysctl -n hw.ncpuonline)
|
||||
# windows: $NUMBER_OF_PROCESSORS ?
|
||||
if env | grep -q '^NIMCORES='; then
|
||||
echo $NIMCORES
|
||||
else
|
||||
echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1)
|
||||
fi
|
||||
echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1)
|
||||
}
|
||||
|
||||
_nimBuildCsourcesIfNeeded(){
|
||||
@@ -80,8 +76,6 @@ _nimBuildCsourcesIfNeeded(){
|
||||
makeX=gmake
|
||||
elif [ "$unamestr" = 'CROSSOS' ]; then
|
||||
makeX=gmake
|
||||
elif [ "$unamestr" = 'SunOS' ]; then
|
||||
makeX=gmake
|
||||
else
|
||||
makeX=make
|
||||
fi
|
||||
|
||||
9
compiler.nimble
Normal file
9
compiler.nimble
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
version = system.NimVersion
|
||||
author = "Andreas Rumpf"
|
||||
description = "Compiler package providing the compiler sources as a library."
|
||||
license = "MIT"
|
||||
|
||||
installDirs = @["compiler", "nimsuggest"]
|
||||
|
||||
requires "nim >= 0.14.0"
|
||||
@@ -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
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
import
|
||||
ast, astalgo, types, trees, intsets
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
TAnalysisResult* = enum
|
||||
arNo, arMaybe, arYes
|
||||
@@ -77,29 +74,24 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
|
||||
## cases:
|
||||
##
|
||||
## YES-cases:
|
||||
## ```
|
||||
## x <| x # for general trees
|
||||
## x[] <| x
|
||||
## x[i] <| x
|
||||
## x.f <| x
|
||||
## ```
|
||||
##
|
||||
## NO-cases:
|
||||
## ```
|
||||
## x !<| y # depending on type and symbol kind
|
||||
## x[constA] !<| x[constB]
|
||||
## x.f !<| x.g
|
||||
## x.f !<| y.f iff x !<= y
|
||||
## ```
|
||||
##
|
||||
## MAYBE-cases:
|
||||
##
|
||||
## ```
|
||||
## x[] ?<| y[] iff compatible type
|
||||
##
|
||||
##
|
||||
## x[] ?<| y depending on type
|
||||
## ```
|
||||
##
|
||||
if a.kind == b.kind:
|
||||
case a.kind
|
||||
of nkSym:
|
||||
|
||||
407
compiler/ast.nim
407
compiler/ast.nim
@@ -10,12 +10,9 @@
|
||||
# abstract syntax tree + symbol table
|
||||
|
||||
import
|
||||
lineinfos, hashes, options, ropes, idents, int128, tables, wordrecg
|
||||
lineinfos, hashes, options, ropes, idents, int128, tables
|
||||
from strutils import toLowerAscii
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
export int128
|
||||
|
||||
type
|
||||
@@ -207,11 +204,12 @@ type
|
||||
nkPtrTy, # ``ptr T``
|
||||
nkVarTy, # ``var T``
|
||||
nkConstTy, # ``const T``
|
||||
nkOutTy, # ``out T``
|
||||
nkMutableTy, # ``mutable T``
|
||||
nkDistinctTy, # distinct type
|
||||
nkProcTy, # proc type
|
||||
nkIteratorTy, # iterator type
|
||||
nkSinkAsgn, # '=sink(x, y)'
|
||||
nkSharedTy, # 'shared T'
|
||||
# we use 'nkPostFix' for the 'not nil' addition
|
||||
nkEnumTy, # enum body
|
||||
nkEnumFieldDef, # `ident = expr` in an enumeration
|
||||
nkArgList, # argument list
|
||||
@@ -227,12 +225,11 @@ type
|
||||
nkModuleRef # for .rod file support: A (moduleId, itemId) pair
|
||||
nkReplayAction # for .rod file support: A replay action
|
||||
nkNilRodNode # for .rod file support: a 'nil' PNode
|
||||
nkOpenSym # container for captured sym that can be overriden by local symbols
|
||||
|
||||
TNodeKinds* = set[TNodeKind]
|
||||
|
||||
type
|
||||
TSymFlag* = enum # 52 flags!
|
||||
TSymFlag* = enum # 48 flags!
|
||||
sfUsed, # read access of sym (for warnings) or simply used
|
||||
sfExported, # symbol is exported from module
|
||||
sfFromGeneric, # symbol is instantiation of a generic; this is needed
|
||||
@@ -260,8 +257,7 @@ type
|
||||
# *OR*: a proc is indirectly called (used as first class)
|
||||
sfCompilerProc, # proc is a compiler proc, that is a C proc that is
|
||||
# needed for the code generator
|
||||
sfEscapes # param escapes
|
||||
# currently unimplemented
|
||||
sfProcvar, # proc can be passed to a proc var
|
||||
sfDiscriminant, # field is a discriminant in a record/object
|
||||
sfRequiresInit, # field must be initialized during construction
|
||||
sfDeprecated, # symbol is deprecated
|
||||
@@ -284,7 +280,7 @@ type
|
||||
sfNamedParamCall, # symbol needs named parameter call syntax in target
|
||||
# language; for interfacing with Objective C
|
||||
sfDiscardable, # returned value may be discarded implicitly
|
||||
sfOverridden, # proc is overridden
|
||||
sfOverriden, # proc is overridden
|
||||
sfCallsite # A flag for template symbols to tell the
|
||||
# compiler it should use line information from
|
||||
# the calling side of the macro, not from the
|
||||
@@ -300,23 +296,10 @@ type
|
||||
sfInjectDestructors # whether the proc needs the 'injectdestructors' transformation
|
||||
sfNeverRaises # proc can never raise an exception, not even OverflowDefect
|
||||
# or out-of-memory
|
||||
sfSystemRaisesDefect # proc in the system can raise defects
|
||||
sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally'
|
||||
sfSingleUsedTemp # For temporaries that we know will only be used once
|
||||
sfNoalias # 'noalias' annotation, means C's 'restrict'
|
||||
# for templates and macros, means cannot be called
|
||||
# as a lone symbol (cannot use alias syntax)
|
||||
sfEffectsDelayed # an 'effectsDelayed' parameter
|
||||
sfGeneratedType # A anonymous generic type that is generated by the compiler for
|
||||
# objects that do not have generic parameters in case one of the
|
||||
# object fields has one.
|
||||
#
|
||||
# This is disallowed but can cause the typechecking to go into
|
||||
# an infinite loop, this flag is used as a sentinel to stop it.
|
||||
sfVirtual # proc is a C++ virtual function
|
||||
sfByCopy # param is marked as pass bycopy
|
||||
sfCodegenDecl # type, proc, global or proc param is marked as codegenDecl
|
||||
sfWasGenSym # symbol was 'gensym'ed
|
||||
|
||||
TSymFlags* = set[TSymFlag]
|
||||
|
||||
@@ -342,13 +325,13 @@ const
|
||||
|
||||
sfCompileToCpp* = sfInfixCall # compile the module as C++ code
|
||||
sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
|
||||
sfExperimental* = sfOverridden # module uses the .experimental switch
|
||||
sfGoto* = sfOverridden # var is used for 'goto' code generation
|
||||
sfExperimental* = sfOverriden # module uses the .experimental switch
|
||||
sfGoto* = sfOverriden # var is used for 'goto' code generation
|
||||
sfWrittenTo* = sfBorrow # param is assigned to
|
||||
# currently unimplemented
|
||||
sfEscapes* = sfProcvar # param escapes
|
||||
sfBase* = sfDiscriminant
|
||||
sfIsSelf* = sfOverriden # param is 'self'
|
||||
sfCustomPragma* = sfRegister # symbol is custom pragma template
|
||||
sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template
|
||||
|
||||
const
|
||||
# getting ready for the future expr/stmt merge
|
||||
@@ -361,8 +344,7 @@ const
|
||||
ensuresEffects* = 2 # 'ensures' annotation
|
||||
tagEffects* = 3 # user defined tag ('gc', 'time' etc.)
|
||||
pragmasEffects* = 4 # not an effect, but a slot for pragmas in proc type
|
||||
forbiddenEffects* = 5 # list of illegal effects
|
||||
effectListLen* = 6 # list of effects list
|
||||
effectListLen* = 5 # list of effects list
|
||||
nkLastBlockStmts* = {nkRaiseStmt, nkReturnStmt, nkBreakStmt, nkContinueStmt}
|
||||
# these must be last statements in a block
|
||||
|
||||
@@ -515,15 +497,11 @@ type
|
||||
# the flag is applied to proc default values and to calls
|
||||
nfExecuteOnReload # A top-level statement that will be executed during reloads
|
||||
nfLastRead # this node is a last read
|
||||
nfFirstWrite # this node is a first write
|
||||
nfFirstWrite# this node is a first write
|
||||
nfHasComment # node has a comment
|
||||
nfSkipFieldChecking # node skips field visable checking
|
||||
nfDisabledOpenSym # temporary: node should be nkOpenSym but cannot
|
||||
# because openSym experimental switch is disabled
|
||||
# gives warning instead
|
||||
|
||||
TNodeFlags* = set[TNodeFlag]
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47)
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 43)
|
||||
tfVarargs, # procedure has C styled varargs
|
||||
# tyArray type represeting a varargs list
|
||||
tfNoSideEffect, # procedure type does not allow side effects
|
||||
@@ -592,9 +570,6 @@ type
|
||||
tfExplicitCallConv
|
||||
tfIsConstructor
|
||||
tfEffectSystemWorkaround
|
||||
tfIsOutParam
|
||||
tfSendable
|
||||
tfImplicitStatic
|
||||
|
||||
TTypeFlags* = set[TTypeFlag]
|
||||
|
||||
@@ -630,12 +605,13 @@ type
|
||||
# file (it is loaded on demand, which may
|
||||
# mean: never)
|
||||
skPackage, # symbol is a package (used for canonicalization)
|
||||
skAlias # an alias (needs to be resolved immediately)
|
||||
TSymKinds* = set[TSymKind]
|
||||
|
||||
const
|
||||
routineKinds* = {skProc, skFunc, skMethod, skIterator,
|
||||
skConverter, skMacro, skTemplate}
|
||||
ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub} + routineKinds
|
||||
ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub, skAlias} + routineKinds
|
||||
|
||||
tfUnion* = tfNoSideEffect
|
||||
tfGcSafe* = tfThread
|
||||
@@ -644,7 +620,7 @@ const
|
||||
skError* = skUnknown
|
||||
|
||||
var
|
||||
eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam}
|
||||
eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect}
|
||||
## type flags that are essential for type equality.
|
||||
## This is now a variable because for emulation of version:1.0 we
|
||||
## might exclude {tfGcSafe, tfNoSideEffect}.
|
||||
@@ -694,10 +670,10 @@ type
|
||||
mInSet, mRepr, mExit,
|
||||
mSetLengthStr, mSetLengthSeq,
|
||||
mIsPartOf, mAstToStr, mParallel,
|
||||
mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
|
||||
mSwap, mIsNil, mArrToSeq,
|
||||
mNewString, mNewStringOfCap, mParseBiggestFloat,
|
||||
mMove, mEnsureMove, mWasMoved, mDup, mDestroy, mTrace,
|
||||
mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset,
|
||||
mMove, mWasMoved, mDestroy, mTrace,
|
||||
mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mReset,
|
||||
mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
|
||||
mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
|
||||
mOrdinal, mIterableType,
|
||||
@@ -717,19 +693,19 @@ type
|
||||
mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext,
|
||||
|
||||
mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
|
||||
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetStrVal, mNLineInfo,
|
||||
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
|
||||
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNSigHash, mNSizeOf,
|
||||
mNBindSym, mNCallSite,
|
||||
mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym,
|
||||
mNHint, mNWarning, mNError,
|
||||
mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2,
|
||||
mNimvm, mIntDefine, mStrDefine, mBoolDefine, mGenericDefine, mRunnableExamples,
|
||||
mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples,
|
||||
mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf,
|
||||
mSymIsInstantiationOf, mNodeId, mPrivateAccess, mZeroDefault
|
||||
mSymIsInstantiationOf, mNodeId, mPrivateAccess
|
||||
|
||||
|
||||
# things that we can evaluate safely at compile time, even if not asked for it:
|
||||
const
|
||||
# things that we can evaluate safely at compile time, even if not asked for it:
|
||||
ctfeWhitelist* = {mNone, mSucc,
|
||||
mPred, mInc, mDec, mOrd, mLengthOpenArray,
|
||||
mLengthStr, mLengthArray, mLengthSeq,
|
||||
@@ -757,19 +733,13 @@ const
|
||||
mEqStr, mLeStr, mLtStr,
|
||||
mEqSet, mLeSet, mLtSet, mMulSet, mPlusSet, mMinusSet,
|
||||
mConStrStr, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mInSet, mRepr, mOpenArrayToSeq}
|
||||
|
||||
generatedMagics* = {mNone, mIsolate, mFinished, mOpenArrayToSeq}
|
||||
## magics that are generated as normal procs in the backend
|
||||
mInSet, mRepr}
|
||||
|
||||
type
|
||||
ItemId* = object
|
||||
module*: int32
|
||||
item*: int32
|
||||
|
||||
proc `$`*(x: ItemId): string =
|
||||
"(module: " & $x.module & ", item: " & $x.item & ")"
|
||||
|
||||
proc `==`*(a, b: ItemId): bool {.inline.} =
|
||||
a.item == b.item and a.module == b.module
|
||||
|
||||
@@ -807,8 +777,6 @@ type
|
||||
ident*: PIdent
|
||||
else:
|
||||
sons*: TNodeSeq
|
||||
when defined(nimsuggest):
|
||||
endInfo*: TLineInfo
|
||||
|
||||
TStrTable* = object # a table[PIdent] of PSym
|
||||
counter*: int
|
||||
@@ -829,6 +797,9 @@ type
|
||||
locOther # location is something other
|
||||
TLocFlag* = enum
|
||||
lfIndirect, # backend introduced a pointer
|
||||
lfFullExternalName, # only used when 'conf.cmd == cmdNimfix': Indicates
|
||||
# that the symbol has been imported via 'importc: "fullname"' and
|
||||
# no format string.
|
||||
lfNoDeepCopy, # no need for a deep copy
|
||||
lfNoDecl, # do not declare it in C
|
||||
lfDynamicLib, # link symbol to dynamic library
|
||||
@@ -863,7 +834,7 @@ type
|
||||
# keep in sync with PackedLib
|
||||
kind*: TLibKind
|
||||
generated*: bool # needed for the backends:
|
||||
isOverridden*: bool
|
||||
isOverriden*: bool
|
||||
name*: Rope
|
||||
path*: PNode # can be a string literal!
|
||||
|
||||
@@ -902,9 +873,6 @@ type
|
||||
typ*: PType
|
||||
name*: PIdent
|
||||
info*: TLineInfo
|
||||
when defined(nimsuggest):
|
||||
endInfo*: TLineInfo
|
||||
hasUserSpecifiedType*: bool # used for determining whether to display inlay type hints
|
||||
owner*: PSym
|
||||
flags*: TSymFlags
|
||||
ast*: PNode # syntax tree of proc, iterator, etc.:
|
||||
@@ -926,9 +894,7 @@ type
|
||||
# for modules, an unique index corresponding
|
||||
# to the module's fileIdx
|
||||
# for variables a slot index for the evaluator
|
||||
offset*: int32 # offset of record field
|
||||
disamb*: int32 # disambiguation number; the basic idea is that
|
||||
# `<procname>__<module>_<disamb>`
|
||||
offset*: int # offset of record field
|
||||
loc*: TLoc
|
||||
annex*: PLib # additional fields (seldom used, so we use a
|
||||
# reference to another object to save space)
|
||||
@@ -936,7 +902,7 @@ type
|
||||
cname*: string # resolved C declaration name in importc decl, e.g.:
|
||||
# proc fun() {.importc: "$1aux".} => cname = funaux
|
||||
constraint*: PNode # additional constraints like 'lit|result'; also
|
||||
# misused for the codegenDecl and virtual pragmas in the hope
|
||||
# misused for the codegenDecl pragma in the hope
|
||||
# it won't cause problems
|
||||
# for skModule the string literal to output for
|
||||
# deprecated modules.
|
||||
@@ -944,12 +910,11 @@ type
|
||||
allUsages*: seq[TLineInfo]
|
||||
|
||||
TTypeSeq* = seq[PType]
|
||||
TLockLevel* = distinct int16
|
||||
|
||||
TTypeAttachedOp* = enum ## as usual, order is important here
|
||||
attachedWasMoved,
|
||||
attachedDestructor,
|
||||
attachedAsgn,
|
||||
attachedDup,
|
||||
attachedSink,
|
||||
attachedTrace,
|
||||
attachedDeepCopy
|
||||
@@ -979,6 +944,7 @@ type
|
||||
# -1 means that the size is unkwown
|
||||
align*: int16 # the type's alignment requirements
|
||||
paddingAtEnd*: int16 #
|
||||
lockLevel*: TLockLevel # lock level as required for deadlock checking
|
||||
loc*: TLoc
|
||||
typeInst*: PType # for generic instantiations the tyGenericInst that led to this
|
||||
# type.
|
||||
@@ -1092,9 +1058,7 @@ const
|
||||
nfDotSetter, nfDotField,
|
||||
nfIsRef, nfIsPtr, nfPreventCg, nfLL,
|
||||
nfFromTemplate, nfDefaultRefsParam,
|
||||
nfExecuteOnReload, nfLastRead,
|
||||
nfFirstWrite, nfSkipFieldChecking,
|
||||
nfDisabledOpenSym}
|
||||
nfExecuteOnReload, nfLastRead, nfFirstWrite}
|
||||
namePos* = 0
|
||||
patternPos* = 1 # empty except for term rewriting macros
|
||||
genericParamsPos* = 2
|
||||
@@ -1110,7 +1074,7 @@ const
|
||||
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
|
||||
nkCommand, nkCallStrLit, nkHiddenCallConv}
|
||||
nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
|
||||
nkClosedSymChoice, nkOpenSym}
|
||||
nkClosedSymChoice}
|
||||
|
||||
nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
|
||||
nkLiterals* = {nkCharLit..nkTripleStrLit}
|
||||
@@ -1134,13 +1098,28 @@ const
|
||||
|
||||
proc getPIdent*(a: PNode): PIdent {.inline.} =
|
||||
## Returns underlying `PIdent` for `{nkSym, nkIdent}`, or `nil`.
|
||||
# xxx consider whether also returning the 1st ident for {nkOpenSymChoice, nkClosedSymChoice}
|
||||
# which may simplify code.
|
||||
case a.kind
|
||||
of nkSym: a.sym.name
|
||||
of nkIdent: a.ident
|
||||
of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
|
||||
of nkOpenSym: getPIdent(a.sons[0])
|
||||
else: nil
|
||||
|
||||
proc getnimblePkg*(a: PSym): PSym =
|
||||
result = a
|
||||
while result != nil:
|
||||
case result.kind
|
||||
of skModule:
|
||||
result = result.owner
|
||||
assert result.kind == skPackage
|
||||
of skPackage:
|
||||
if result.owner == nil:
|
||||
break
|
||||
else:
|
||||
result = result.owner
|
||||
else:
|
||||
assert false, $result.kind
|
||||
|
||||
const
|
||||
moduleShift = when defined(cpu32): 20 else: 24
|
||||
|
||||
@@ -1154,17 +1133,13 @@ type
|
||||
symId*: int32
|
||||
typeId*: int32
|
||||
sealed*: bool
|
||||
disambTable*: CountTable[PIdent]
|
||||
|
||||
const
|
||||
PackageModuleId* = -3'i32
|
||||
|
||||
proc idGeneratorFromModule*(m: PSym): IdGenerator =
|
||||
assert m.kind == skModule
|
||||
result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0, disambTable: initCountTable[PIdent]())
|
||||
|
||||
proc idGeneratorForPackage*(nextIdWillBe: int32): IdGenerator =
|
||||
result = IdGenerator(module: PackageModuleId, symId: nextIdWillBe - 1'i32, typeId: 0, disambTable: initCountTable[PIdent]())
|
||||
result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
|
||||
|
||||
proc nextSymId*(x: IdGenerator): ItemId {.inline.} =
|
||||
assert(not x.sealed)
|
||||
@@ -1189,7 +1164,13 @@ when false:
|
||||
assert dest.ItemId.item <= src.ItemId.item
|
||||
dest = src
|
||||
|
||||
proc getnimblePkgId*(a: PSym): int =
|
||||
let b = a.getnimblePkg
|
||||
result = if b == nil: -1 else: b.id
|
||||
|
||||
var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things
|
||||
#var
|
||||
# gMainPackageId*: int
|
||||
|
||||
proc isCallExpr*(n: PNode): bool =
|
||||
result = n.kind in nkCallKinds
|
||||
@@ -1256,34 +1237,6 @@ proc getDeclPragma*(n: PNode): PNode =
|
||||
if result != nil:
|
||||
assert result.kind == nkPragma, $(result.kind, n.kind)
|
||||
|
||||
proc extractPragma*(s: PSym): PNode =
|
||||
## gets the pragma node of routine/type/var/let/const symbol `s`
|
||||
if s.kind in routineKinds: # bug #24167
|
||||
if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty:
|
||||
result = s.ast[pragmasPos]
|
||||
else:
|
||||
result = nil
|
||||
elif s.kind in {skType, skVar, skLet, skConst}:
|
||||
if s.ast != nil and s.ast.len > 0:
|
||||
if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
|
||||
# s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
|
||||
result = s.ast[0][1]
|
||||
assert result == nil or result.kind == nkPragma
|
||||
|
||||
proc skipPragmaExpr*(n: PNode): PNode =
|
||||
## if pragma expr, give the node the pragmas are applied to,
|
||||
## otherwise give node itself
|
||||
if n.kind == nkPragmaExpr:
|
||||
result = n[0]
|
||||
else:
|
||||
result = n
|
||||
|
||||
proc setInfoRecursive*(n: PNode, info: TLineInfo) =
|
||||
## set line info recursively
|
||||
if n != nil:
|
||||
for i in 0..<n.safeLen: setInfoRecursive(n[i], info)
|
||||
n.info = info
|
||||
|
||||
when defined(useNodeIds):
|
||||
const nodeIdToDebug* = -1 # 2322968
|
||||
var gNodeId: int
|
||||
@@ -1359,15 +1312,11 @@ when false:
|
||||
echo k
|
||||
echo v
|
||||
|
||||
proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym,
|
||||
proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
|
||||
info: TLineInfo; options: TOptions = {}): PSym =
|
||||
# generates a symbol and initializes the hash field too
|
||||
assert not name.isNil
|
||||
let id = nextSymId idgen
|
||||
result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
|
||||
options: options, owner: owner, offset: defaultOffset,
|
||||
disamb: getOrDefault(idgen.disambTable, name).int32)
|
||||
idgen.disambTable.inc name
|
||||
options: options, owner: owner, offset: defaultOffset)
|
||||
when false:
|
||||
if id.module == 48 and id.item == 39:
|
||||
writeStackTrace()
|
||||
@@ -1376,7 +1325,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym,
|
||||
|
||||
proc astdef*(s: PSym): PNode =
|
||||
# get only the definition (initializer) portion of the ast
|
||||
if s.ast != nil and s.ast.kind in {nkIdentDefs, nkConstDef}:
|
||||
if s.ast != nil and s.ast.kind == nkIdentDefs:
|
||||
s.ast[2]
|
||||
else:
|
||||
s.ast
|
||||
@@ -1460,9 +1409,6 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
|
||||
result.typ = sym.typ
|
||||
result.info = info
|
||||
|
||||
proc newOpenSym*(n: PNode): PNode {.inline.} =
|
||||
result = newTreeI(nkOpenSym, n.info, n)
|
||||
|
||||
proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
|
||||
result = newNode(kind)
|
||||
result.intVal = intVal
|
||||
@@ -1471,42 +1417,7 @@ proc newIntNode*(kind: TNodeKind, intVal: Int128): PNode =
|
||||
result = newNode(kind)
|
||||
result.intVal = castToInt64(intVal)
|
||||
|
||||
proc lastSon*(n: Indexable): Indexable {.inline.} = n.sons[^1]
|
||||
template setLastSon*(n: PNode, s: PNode) = n.sons[^1] = s
|
||||
|
||||
template firstSon*(n: PNode): PNode = n.sons[0]
|
||||
template secondSon*(n: PNode): PNode = n.sons[1]
|
||||
|
||||
template hasSon*(n: PNode): bool = n.len > 0
|
||||
template has2Sons*(n: PNode): bool = n.len > 1
|
||||
|
||||
proc replaceFirstSon*(n, newson: PNode) {.inline.} =
|
||||
n.sons[0] = newson
|
||||
|
||||
proc replaceSon*(n: PNode; i: int; newson: PNode) {.inline.} =
|
||||
n.sons[i] = newson
|
||||
|
||||
proc last*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc elementType*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
proc skipModifier*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc indexType*(n: PType): PType {.inline.} = n.sons[0]
|
||||
proc baseClass*(n: PType): PType {.inline.} = n.sons[0]
|
||||
|
||||
proc base*(t: PType): PType {.inline.} =
|
||||
result = t.sons[0]
|
||||
|
||||
proc returnType*(n: PType): PType {.inline.} = n.sons[0]
|
||||
proc setReturnType*(n, r: PType) {.inline.} = n.sons[0] = r
|
||||
proc setIndexType*(n, idx: PType) {.inline.} = n.sons[0] = idx
|
||||
|
||||
proc firstParamType*(n: PType): PType {.inline.} = n.sons[1]
|
||||
proc firstGenericParam*(n: PType): PType {.inline.} = n.sons[1]
|
||||
|
||||
proc typeBodyImpl*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc genericHead*(n: PType): PType {.inline.} = n.sons[0]
|
||||
proc lastSon*(n: Indexable): Indexable = n.sons[^1]
|
||||
|
||||
proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
||||
## Used throughout the compiler code to test whether a type tree contains or
|
||||
@@ -1514,7 +1425,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
|
||||
## last child nodes of a type tree need to be searched. This is a really hot
|
||||
## path within the compiler!
|
||||
result = t
|
||||
while result.kind in kinds: result = last(result)
|
||||
while result.kind in kinds: result = lastSon(result)
|
||||
|
||||
proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
|
||||
let kind = skipTypes(typ, abstractVarRange).kind
|
||||
@@ -1564,8 +1475,16 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
|
||||
pragmas, exceptions, body]
|
||||
|
||||
const
|
||||
UnspecifiedLockLevel* = TLockLevel(-1'i16)
|
||||
MaxLockLevel* = 1000'i16
|
||||
UnknownLockLevel* = TLockLevel(1001'i16)
|
||||
AttachedOpToStr*: array[TTypeAttachedOp, string] = [
|
||||
"=wasMoved", "=destroy", "=copy", "=dup", "=sink", "=trace", "=deepcopy"]
|
||||
"=destroy", "=copy", "=sink", "=trace", "=deepcopy"]
|
||||
|
||||
proc `$`*(x: TLockLevel): string =
|
||||
if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
|
||||
elif x.ord == UnknownLockLevel.ord: result = "<unknown>"
|
||||
else: result = $int16(x)
|
||||
|
||||
proc `$`*(s: PSym): string =
|
||||
if s != nil:
|
||||
@@ -1573,110 +1492,10 @@ proc `$`*(s: PSym): string =
|
||||
else:
|
||||
result = "<nil>"
|
||||
|
||||
when false:
|
||||
iterator items*(t: PType): PType =
|
||||
for i in 0..<t.sons.len: yield t.sons[i]
|
||||
|
||||
iterator pairs*(n: PType): tuple[i: int, n: PType] =
|
||||
for i in 0..<n.sons.len: yield (i, n.sons[i])
|
||||
|
||||
when true:
|
||||
proc len*(n: PType): int {.inline.} =
|
||||
result = n.sons.len
|
||||
|
||||
proc sameTupleLengths*(a, b: PType): bool {.inline.} =
|
||||
result = a.sons.len == b.sons.len
|
||||
|
||||
iterator tupleTypePairs*(a, b: PType): (int, PType, PType) =
|
||||
for i in 0 ..< a.sons.len:
|
||||
yield (i, a.sons[i], b.sons[i])
|
||||
|
||||
iterator underspecifiedPairs*(a, b: PType; start = 0; without = 0): (PType, PType) =
|
||||
# XXX Figure out with what typekinds this is called.
|
||||
for i in start ..< min(a.sons.len, b.sons.len) + without:
|
||||
yield (a.sons[i], b.sons[i])
|
||||
|
||||
proc signatureLen*(t: PType): int {.inline.} =
|
||||
result = t.sons.len
|
||||
|
||||
proc paramsLen*(t: PType): int {.inline.} =
|
||||
result = t.sons.len - 1
|
||||
|
||||
proc genericParamsLen*(t: PType): int {.inline.} =
|
||||
assert t.kind == tyGenericInst
|
||||
result = t.sons.len - 2 # without 'head' and 'body'
|
||||
|
||||
proc genericInvocationParamsLen*(t: PType): int {.inline.} =
|
||||
assert t.kind == tyGenericInvocation
|
||||
result = t.sons.len - 1 # without 'head'
|
||||
|
||||
proc kidsLen*(t: PType): int {.inline.} =
|
||||
result = t.sons.len
|
||||
|
||||
proc genericParamHasConstraints*(t: PType): bool {.inline.} = t.sons.len > 0
|
||||
|
||||
proc hasElementType*(t: PType): bool {.inline.} = t.sons.len > 0
|
||||
proc isEmptyTupleType*(t: PType): bool {.inline.} = t.sons.len == 0
|
||||
proc isSingletonTupleType*(t: PType): bool {.inline.} = t.sons.len == 1
|
||||
|
||||
proc genericConstraint*(t: PType): PType {.inline.} = t.sons[0]
|
||||
|
||||
iterator genericInstParams*(t: PType): (bool, PType) =
|
||||
for i in 1..<t.sons.len-1:
|
||||
yield (i!=1, t.sons[i])
|
||||
|
||||
iterator genericInstParamPairs*(a, b: PType): (int, PType, PType) =
|
||||
for i in 1..<min(a.sons.len, b.sons.len)-1:
|
||||
yield (i-1, a.sons[i], b.sons[i])
|
||||
|
||||
iterator genericInvocationParams*(t: PType): (bool, PType) =
|
||||
for i in 1..<t.sons.len:
|
||||
yield (i!=1, t.sons[i])
|
||||
|
||||
iterator genericInvocationAndBodyElements*(a, b: PType): (PType, PType) =
|
||||
for i in 1..<a.sons.len:
|
||||
yield (a.sons[i], b.sons[i-1])
|
||||
|
||||
iterator genericInvocationParamPairs*(a, b: PType): (bool, PType, PType) =
|
||||
for i in 1..<a.sons.len:
|
||||
if i >= b.sons.len:
|
||||
yield (false, nil, nil)
|
||||
else:
|
||||
yield (true, a.sons[i], b.sons[i])
|
||||
|
||||
iterator genericBodyParams*(t: PType): (int, PType) =
|
||||
for i in 0..<t.sons.len-1:
|
||||
yield (i, t.sons[i])
|
||||
|
||||
iterator userTypeClassInstParams*(t: PType): (bool, PType) =
|
||||
for i in 1..<t.sons.len-1:
|
||||
yield (i!=1, t.sons[i])
|
||||
|
||||
iterator ikids*(t: PType): (int, PType) =
|
||||
for i in 0..<t.sons.len: yield (i, t.sons[i])
|
||||
|
||||
const
|
||||
FirstParamAt* = 1
|
||||
FirstGenericParamAt* = 1
|
||||
|
||||
iterator paramTypes*(t: PType): (int, PType) =
|
||||
for i in FirstParamAt..<t.sons.len: yield (i, t.sons[i])
|
||||
|
||||
iterator paramTypePairs*(a, b: PType): (PType, PType) =
|
||||
for i in FirstParamAt..<a.sons.len: yield (a.sons[i], b.sons[i])
|
||||
|
||||
template paramTypeToNodeIndex*(x: int): int = x
|
||||
|
||||
iterator kids*(t: PType): PType =
|
||||
for i in 0..<t.sons.len: yield t.sons[i]
|
||||
|
||||
iterator signature*(t: PType): PType =
|
||||
# yields return type + parameter types
|
||||
for i in 0..<t.sons.len: yield t.sons[i]
|
||||
|
||||
proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType =
|
||||
result = PType(kind: kind, owner: owner, size: defaultSize,
|
||||
align: defaultAlignment, itemId: id,
|
||||
lockLevel: UnspecifiedLockLevel,
|
||||
uniqueId: id)
|
||||
when false:
|
||||
if result.itemId.module == 55 and result.itemId.item == 2:
|
||||
@@ -1689,7 +1508,7 @@ proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||
if a.storage == low(typeof(a.storage)): a.storage = b.storage
|
||||
a.flags.incl b.flags
|
||||
if a.lode == nil: a.lode = b.lode
|
||||
if a.r == "": a.r = b.r
|
||||
if a.r == nil: a.r = b.r
|
||||
|
||||
proc newSons*(father: Indexable, length: int) =
|
||||
setLen(father.sons, length)
|
||||
@@ -1701,10 +1520,11 @@ proc assignType*(dest, src: PType) =
|
||||
dest.n = src.n
|
||||
dest.size = src.size
|
||||
dest.align = src.align
|
||||
dest.lockLevel = src.lockLevel
|
||||
# this fixes 'type TLock = TSysLock':
|
||||
if src.sym != nil:
|
||||
if dest.sym != nil:
|
||||
dest.sym.flags.incl src.sym.flags-{sfUsed, sfExported}
|
||||
dest.sym.flags.incl src.sym.flags-{sfExported}
|
||||
if dest.sym.annex == nil: dest.sym.annex = src.sym.annex
|
||||
mergeLoc(dest.sym.loc, src.sym.loc)
|
||||
else:
|
||||
@@ -1720,8 +1540,8 @@ proc copyType*(t: PType, id: ItemId, owner: PSym): PType =
|
||||
proc exactReplica*(t: PType): PType =
|
||||
result = copyType(t, t.itemId, t.owner)
|
||||
|
||||
proc copySym*(s: PSym; idgen: IdGenerator): PSym =
|
||||
result = newSym(s.kind, s.name, idgen, s.owner, s.info, s.options)
|
||||
proc copySym*(s: PSym; id: ItemId): PSym =
|
||||
result = newSym(s.kind, s.name, id, s.owner, s.info, s.options)
|
||||
#result.ast = nil # BUGFIX; was: s.ast which made problems
|
||||
result.typ = s.typ
|
||||
result.flags = s.flags
|
||||
@@ -1736,9 +1556,9 @@ proc copySym*(s: PSym; idgen: IdGenerator): PSym =
|
||||
result.bitsize = s.bitsize
|
||||
result.alignment = s.alignment
|
||||
|
||||
proc createModuleAlias*(s: PSym, idgen: IdGenerator, newIdent: PIdent, info: TLineInfo;
|
||||
proc createModuleAlias*(s: PSym, id: ItemId, newIdent: PIdent, info: TLineInfo;
|
||||
options: TOptions): PSym =
|
||||
result = newSym(s.kind, newIdent, idgen, s.owner, info, options)
|
||||
result = newSym(s.kind, newIdent, id, s.owner, info, options)
|
||||
# keep ID!
|
||||
result.ast = s.ast
|
||||
#result.id = s.id # XXX figure out what to do with the ID.
|
||||
@@ -1784,7 +1604,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType =
|
||||
result = t
|
||||
var i = maxIters
|
||||
while result.kind in kinds:
|
||||
result = last(result)
|
||||
result = lastSon(result)
|
||||
dec i
|
||||
if i == 0: return nil
|
||||
|
||||
@@ -1793,7 +1613,7 @@ proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType =
|
||||
result = t
|
||||
while result != nil and result.kind in kinds:
|
||||
if result.len == 0: return nil
|
||||
result = last(result)
|
||||
result = lastSon(result)
|
||||
|
||||
proc isGCedMem*(t: PType): bool {.inline.} =
|
||||
result = t.kind in {tyString, tyRef, tySequence} or
|
||||
@@ -1858,8 +1678,6 @@ proc copyNode*(src: PNode): PNode =
|
||||
of nkIdent: result.ident = src.ident
|
||||
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
|
||||
else: discard
|
||||
when defined(nimsuggest):
|
||||
result.endInfo = src.endInfo
|
||||
|
||||
template transitionNodeKindCommon(k: TNodeKind) =
|
||||
let obj {.inject.} = n[]
|
||||
@@ -1876,10 +1694,6 @@ proc transitionIntKind*(n: PNode, kind: range[nkCharLit..nkUInt64Lit]) =
|
||||
transitionNodeKindCommon(kind)
|
||||
n.intVal = obj.intVal
|
||||
|
||||
proc transitionIntToFloatKind*(n: PNode, kind: range[nkFloatLit..nkFloat128Lit]) =
|
||||
transitionNodeKindCommon(kind)
|
||||
n.floatVal = BiggestFloat(obj.intVal)
|
||||
|
||||
proc transitionNoneToSym*(n: PNode) =
|
||||
transitionNodeKindCommon(nkSym)
|
||||
|
||||
@@ -1912,8 +1726,6 @@ template copyNodeImpl(dst, src, processSonsStmt) =
|
||||
if src == nil: return
|
||||
dst = newNode(src.kind)
|
||||
dst.info = src.info
|
||||
when defined(nimsuggest):
|
||||
result.endInfo = src.endInfo
|
||||
dst.typ = src.typ
|
||||
dst.flags = src.flags * PersistentNodeFlags
|
||||
dst.comment = src.comment
|
||||
@@ -2062,15 +1874,13 @@ proc skipGenericOwner*(s: PSym): PSym =
|
||||
## Generic instantiations are owned by their originating generic
|
||||
## symbol. This proc skips such owners and goes straight to the owner
|
||||
## of the generic itself (the module or the enclosing proc).
|
||||
result = if s.kind == skModule:
|
||||
s
|
||||
elif s.kind in skProcKinds and sfFromGeneric in s.flags and s.owner.kind != skModule:
|
||||
result = if s.kind in skProcKinds and sfFromGeneric in s.flags:
|
||||
s.owner.owner
|
||||
else:
|
||||
s.owner
|
||||
|
||||
proc originatingModule*(s: PSym): PSym =
|
||||
result = s
|
||||
result = s.owner
|
||||
while result.kind != skModule: result = result.owner
|
||||
|
||||
proc isRoutine*(s: PSym): bool {.inline.} =
|
||||
@@ -2080,6 +1890,20 @@ proc isCompileTimeProc*(s: PSym): bool {.inline.} =
|
||||
result = s.kind == skMacro or
|
||||
s.kind in {skProc, skFunc} and sfCompileTime in s.flags
|
||||
|
||||
proc isRunnableExamples*(n: PNode): bool =
|
||||
# Templates and generics don't perform symbol lookups.
|
||||
result = n.kind == nkSym and n.sym.magic == mRunnableExamples or
|
||||
n.kind == nkIdent and n.ident.s == "runnableExamples"
|
||||
|
||||
proc requiredParams*(s: PSym): int =
|
||||
# Returns the number of required params (without default values)
|
||||
# XXX: Perhaps we can store this in the `offset` field of the
|
||||
# symbol instead?
|
||||
for i in 1..<s.typ.len:
|
||||
if s.typ.n[i].sym.ast != nil:
|
||||
return i - 1
|
||||
return s.typ.len - 1
|
||||
|
||||
proc hasPattern*(s: PSym): bool {.inline.} =
|
||||
result = isRoutine(s) and s.ast[patternPos].kind != nkEmpty
|
||||
|
||||
@@ -2132,7 +1956,7 @@ proc toObject*(typ: PType): PType =
|
||||
## cases should be a ``tyObject``).
|
||||
## Otherwise ``typ`` is simply returned as-is.
|
||||
let t = typ.skipTypes({tyAlias, tyGenericInst})
|
||||
if t.kind == tyRef: t.elementType
|
||||
if t.kind == tyRef: t.lastSon
|
||||
else: typ
|
||||
|
||||
proc toObjectFromRefPtrGeneric*(typ: PType): PType =
|
||||
@@ -2149,11 +1973,11 @@ proc toObjectFromRefPtrGeneric*(typ: PType): PType =
|
||||
result = typ
|
||||
while true:
|
||||
case result.kind
|
||||
of tyGenericBody: result = result.last
|
||||
of tyGenericBody: result = result.lastSon
|
||||
of tyRef, tyPtr, tyGenericInst, tyGenericInvocation, tyAlias: result = result[0]
|
||||
# automatic dereferencing is deep, refs #18298.
|
||||
else: break
|
||||
# result does not have to be object type
|
||||
assert result.sym != nil
|
||||
|
||||
proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
||||
assert t != nil
|
||||
@@ -2167,7 +1991,7 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool =
|
||||
result = true
|
||||
|
||||
proc isInfixAs*(n: PNode): bool =
|
||||
return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == ord(wAs)
|
||||
return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as"
|
||||
|
||||
proc skipColon*(n: PNode): PNode =
|
||||
result = n
|
||||
@@ -2175,12 +1999,10 @@ proc skipColon*(n: PNode): PNode =
|
||||
result = n[1]
|
||||
|
||||
proc findUnresolvedStatic*(n: PNode): PNode =
|
||||
# n.typ == nil: see issue #14802
|
||||
if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil:
|
||||
return n
|
||||
if n.typ != nil and n.typ.kind == tyTypeDesc:
|
||||
let t = skipTypes(n.typ, {tyTypeDesc})
|
||||
if t.kind == tyGenericParam and t.len == 0:
|
||||
return n
|
||||
|
||||
for son in n:
|
||||
let n = son.findUnresolvedStatic
|
||||
if n != nil: return n
|
||||
@@ -2209,21 +2031,12 @@ template detailedInfo*(sym: PSym): string =
|
||||
proc isInlineIterator*(typ: PType): bool {.inline.} =
|
||||
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv != ccClosure
|
||||
|
||||
proc isIterator*(typ: PType): bool {.inline.} =
|
||||
typ.kind == tyProc and tfIterator in typ.flags
|
||||
|
||||
proc isClosureIterator*(typ: PType): bool {.inline.} =
|
||||
typ.kind == tyProc and tfIterator in typ.flags and typ.callConv == ccClosure
|
||||
|
||||
proc isClosure*(typ: PType): bool {.inline.} =
|
||||
typ.kind == tyProc and typ.callConv == ccClosure
|
||||
|
||||
proc isNimcall*(s: PSym): bool {.inline.} =
|
||||
s.typ.callConv == ccNimCall
|
||||
|
||||
proc isExplicitCallConv*(s: PSym): bool {.inline.} =
|
||||
tfExplicitCallConv in s.typ.flags
|
||||
|
||||
proc isSinkParam*(s: PSym): bool {.inline.} =
|
||||
s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags)
|
||||
|
||||
@@ -2288,13 +2101,3 @@ proc skipAddr*(n: PNode): PNode {.inline.} =
|
||||
proc isNewStyleConcept*(n: PNode): bool {.inline.} =
|
||||
assert n.kind == nkTypeClassTy
|
||||
result = n[0].kind == nkEmpty
|
||||
|
||||
proc isOutParam*(t: PType): bool {.inline.} = tfIsOutParam in t.flags
|
||||
|
||||
const
|
||||
nodesToIgnoreSet* = {nkNone..pred(nkSym), succ(nkSym)..nkNilLit,
|
||||
nkTypeSection, nkProcDef, nkConverterDef,
|
||||
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
|
||||
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
|
||||
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
|
||||
nkTypeOfExpr, nkMixinStmt, nkBindStmt}
|
||||
|
||||
@@ -12,14 +12,9 @@
|
||||
# the data structures here are used in various places of the compiler.
|
||||
|
||||
import
|
||||
ast, hashes, intsets, options, lineinfos, ropes, idents, rodutils,
|
||||
ast, hashes, intsets, strutils, options, lineinfos, ropes, idents, rodutils,
|
||||
msgs
|
||||
|
||||
import strutils except addf
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
proc hashNode*(p: RootRef): Hash
|
||||
proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
|
||||
# Convert a tree into its YAML representation; this is used by the
|
||||
@@ -226,10 +221,11 @@ proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym =
|
||||
## Named parameters are special because a named parameter can be
|
||||
## gensym'ed and then they have '\`<number>' suffix that we need to
|
||||
## ignore, see compiler / evaltempl.nim, snippet:
|
||||
## ```
|
||||
##
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id),
|
||||
## if c.instLines: actual.info else: templ.info)
|
||||
## ```
|
||||
for i in 1..<list.len:
|
||||
let it = list[i].sym
|
||||
if it.name.id == ident.id or
|
||||
@@ -257,7 +253,7 @@ proc makeYamlString*(s: string): Rope =
|
||||
# this could trigger InternalError(111). See the ropes module for
|
||||
# further information.
|
||||
const MaxLineLength = 64
|
||||
result = ""
|
||||
result = nil
|
||||
var res = "\""
|
||||
for i in 0..<s.len:
|
||||
if (i + 1) mod MaxLineLength == 0:
|
||||
@@ -273,9 +269,9 @@ proc flagsToStr[T](flags: set[T]): Rope =
|
||||
if flags == {}:
|
||||
result = rope("[]")
|
||||
else:
|
||||
result = ""
|
||||
result = nil
|
||||
for x in items(flags):
|
||||
if result != "": result.add(", ")
|
||||
if result != nil: result.add(", ")
|
||||
result.add(makeYamlString($x))
|
||||
result = "[" & result & "]"
|
||||
|
||||
@@ -330,7 +326,7 @@ proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
|
||||
sonsRope = rope("null")
|
||||
elif containsOrIncl(marker, n.id):
|
||||
sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope(
|
||||
strutils.toHex(cast[int](n), sizeof(n) * 2))]
|
||||
strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
|
||||
else:
|
||||
if n.len > 0:
|
||||
sonsRope = rope("[")
|
||||
@@ -816,21 +812,16 @@ type
|
||||
name*: PIdent
|
||||
|
||||
proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym =
|
||||
# hot spots
|
||||
var h = ti.h and high(tab.data)
|
||||
var start = h
|
||||
var p {.cursor.} = tab.data[h]
|
||||
while p != nil:
|
||||
if p.name.id == ti.name.id: break
|
||||
result = tab.data[h]
|
||||
while result != nil:
|
||||
if result.name.id == ti.name.id: break
|
||||
h = nextTry(h, high(tab.data))
|
||||
if h == start:
|
||||
p = nil
|
||||
result = nil
|
||||
break
|
||||
p = tab.data[h]
|
||||
if p != nil:
|
||||
result = p # increase the count
|
||||
else:
|
||||
result = nil
|
||||
result = tab.data[h]
|
||||
ti.h = nextTry(h, high(tab.data))
|
||||
|
||||
proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym =
|
||||
@@ -890,7 +881,7 @@ proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym =
|
||||
result = nextIter(ti, tab)
|
||||
|
||||
iterator items*(tab: TStrTable): PSym =
|
||||
var it: TTabIter = default(TTabIter)
|
||||
var it: TTabIter
|
||||
var s = initTabIter(it, tab)
|
||||
while s != nil:
|
||||
yield s
|
||||
@@ -1054,6 +1045,14 @@ proc iiTablePut(t: var TIITable, key, val: int) =
|
||||
iiTableRawInsert(t.data, key, val)
|
||||
inc(t.counter)
|
||||
|
||||
proc isAddrNode*(n: PNode): bool =
|
||||
case n.kind
|
||||
of nkAddr, nkHiddenAddr: true
|
||||
of nkCallKinds:
|
||||
if n[0].kind == nkSym and n[0].sym.magic == mAddr: true
|
||||
else: false
|
||||
else: false
|
||||
|
||||
proc listSymbolNames*(symbols: openArray[PSym]): string =
|
||||
for sym in symbols:
|
||||
if result.len > 0:
|
||||
|
||||
@@ -24,12 +24,6 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
result.add " declared in " & toFileLineCol(conf, typ.sym.info)
|
||||
result.add "]"
|
||||
|
||||
proc addTypeNodeDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
|
||||
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)
|
||||
|
||||
|
||||
@@ -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,9 +10,6 @@
|
||||
# this unit handles Nim sets; it implements bit sets
|
||||
# the code here should be reused in the Nim standard library
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
ElemType = byte
|
||||
TBitSet* = seq[ElemType] # we use byte here to avoid issues with
|
||||
@@ -26,40 +23,53 @@ const
|
||||
template modElemSize(arg: untyped): untyped = arg and 7
|
||||
template divElemSize(arg: untyped): untyped = arg shr 3
|
||||
|
||||
proc bitSetIn*(x: TBitSet, e: BiggestInt): bool =
|
||||
proc bitSetInit*(b: var TBitSet, length: int)
|
||||
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
|
||||
proc bitSetCard*(x: TBitSet): BiggestInt
|
||||
# implementation
|
||||
|
||||
proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
|
||||
result = (x[int(e.divElemSize)] and (One shl e.modElemSize)) != Zero
|
||||
|
||||
proc bitSetIncl*(x: var TBitSet, elem: BiggestInt) =
|
||||
proc bitSetIncl(x: var TBitSet, elem: BiggestInt) =
|
||||
assert(elem >= 0)
|
||||
x[int(elem.divElemSize)] = x[int(elem.divElemSize)] or
|
||||
(One shl elem.modElemSize)
|
||||
|
||||
proc bitSetExcl*(x: var TBitSet, elem: BiggestInt) =
|
||||
proc bitSetExcl(x: var TBitSet, elem: BiggestInt) =
|
||||
x[int(elem.divElemSize)] = x[int(elem.divElemSize)] and
|
||||
not(One shl elem.modElemSize)
|
||||
|
||||
proc bitSetInit*(b: var TBitSet, length: int) =
|
||||
proc bitSetInit(b: var TBitSet, length: int) =
|
||||
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]
|
||||
|
||||
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]
|
||||
|
||||
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]
|
||||
|
||||
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]
|
||||
|
||||
proc bitSetEquals*(x, y: TBitSet): bool =
|
||||
proc bitSetEquals(x, y: TBitSet): bool =
|
||||
for i in 0..high(x):
|
||||
if x[i] != y[i]:
|
||||
return false
|
||||
result = true
|
||||
|
||||
proc bitSetContains*(x, y: TBitSet): bool =
|
||||
proc bitSetContains(x, y: TBitSet): bool =
|
||||
for i in 0..high(x):
|
||||
if (x[i] and not y[i]) != Zero:
|
||||
return false
|
||||
@@ -86,6 +96,6 @@ const populationCount: array[uint8, uint8] = block:
|
||||
|
||||
arr
|
||||
|
||||
proc bitSetCard*(x: TBitSet): BiggestInt =
|
||||
proc bitSetCard(x: TBitSet): BiggestInt =
|
||||
for it in x:
|
||||
result.inc int(populationCount[it])
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
## 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)
|
||||
@@ -68,10 +65,7 @@ proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
|
||||
result.links[j] = h.links[Mhalf + j]
|
||||
else:
|
||||
for j in 0..<Mhalf:
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
result.vals[j] = move h.vals[Mhalf + j]
|
||||
else:
|
||||
shallowCopy(result.vals[j], h.vals[Mhalf + j])
|
||||
shallowCopy(result.vals[j], h.vals[Mhalf + j])
|
||||
|
||||
proc split[Key, Val](h: Node[Key, Val]): Node[Key, Val] =
|
||||
## split node in half
|
||||
@@ -91,10 +85,7 @@ proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
|
||||
if less(key, h.keys[j]): break
|
||||
inc j
|
||||
for i in countdown(h.entries, j+1):
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
h.vals[i] = move h.vals[i-1]
|
||||
else:
|
||||
shallowCopy(h.vals[i], h.vals[i-1])
|
||||
shallowCopy(h.vals[i], h.vals[i-1])
|
||||
h.vals[j] = val
|
||||
else:
|
||||
var newLink: Node[Key, Val] = nil
|
||||
|
||||
@@ -14,17 +14,15 @@ proc canRaiseDisp(p: BProc; n: PNode): bool =
|
||||
if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
|
||||
result = false
|
||||
elif optPanics in p.config.globalOptions or
|
||||
(n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and
|
||||
sfSystemRaisesDefect notin n.sym.flags):
|
||||
(n.kind == nkSym and sfSystemModule in getModule(n.sym).flags):
|
||||
# we know we can be strict:
|
||||
result = canRaise(n)
|
||||
else:
|
||||
# we have to be *very* conservative:
|
||||
result = canRaiseConservative(n)
|
||||
|
||||
proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
|
||||
proc preventNrvo(p: BProc; le, ri: PNode): bool =
|
||||
proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
|
||||
result = false
|
||||
var n = le
|
||||
while true:
|
||||
# do NOT follow nkHiddenDeref here!
|
||||
@@ -56,11 +54,6 @@ proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
|
||||
if canRaise(ri[0]) and
|
||||
locationEscapes(p, le, p.nestedTryStmts.len > 0):
|
||||
message(p.config, le.info, warnObservableStores, $le)
|
||||
# bug #19613 prevent dangerous aliasing too:
|
||||
if dest != nil and dest != le:
|
||||
for i in 1..<ri.len:
|
||||
let r = ri[i]
|
||||
if isPartOf(dest, r) != arNo: return true
|
||||
|
||||
proc hasNoInit(call: PNode): bool {.inline.} =
|
||||
result = call[0].kind == nkSym and sfNoInit in call[0].sym.flags
|
||||
@@ -79,36 +72,32 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
callee, params: Rope) =
|
||||
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
||||
genLineDir(p, ri)
|
||||
var pl = callee & "(" & params
|
||||
var pl = callee & ~"(" & params
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
if typ[0] != nil:
|
||||
var flags: TAssignmentFlags = {}
|
||||
if typ[0].kind in {tyOpenArray, tyVarargs}:
|
||||
# perhaps generate no temp if the call doesn't have side effects
|
||||
flags.incl needTempForOpenArray
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
if params.len != 0: pl.add(", ")
|
||||
if isInvalidReturnType(p.config, typ[0]):
|
||||
if params != nil: pl.add(~", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
|
||||
if d.k in {locTemp, locNone} or not preventNrvo(p, le, ri):
|
||||
# Great, we can use 'd':
|
||||
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
|
||||
elif d.k notin {locTemp} and not hasNoInit(ri):
|
||||
# reset before pass as 'result' var:
|
||||
discard "resetLoc(p, d)"
|
||||
pl.add(addrLoc(p.config, d))
|
||||
pl.add(");\n")
|
||||
pl.add(~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ[0], tmp, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
pl.add(");\n")
|
||||
pl.add(~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
pl.add(")")
|
||||
pl.add(~")")
|
||||
if p.module.compileToCpp:
|
||||
if lfSingleUse in d.flags:
|
||||
# do not generate spurious temporaries for C++! For C we're better off
|
||||
@@ -134,7 +123,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, flags) # no need for deep copying
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
@@ -142,45 +131,34 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
|
||||
var list: TLoc
|
||||
initLoc(list, locCall, d.lode, OnUnknown)
|
||||
list.r = pl
|
||||
genAssignment(p, tmp, list, flags) # no need for deep copying
|
||||
genAssignment(p, tmp, list, {}) # no need for deep copying
|
||||
if canRaise: raiseExit(p)
|
||||
genAssignment(p, d, tmp, {})
|
||||
else:
|
||||
pl.add(");\n")
|
||||
pl.add(~");$n")
|
||||
line(p, cpsStmts, pl)
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType)
|
||||
proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
|
||||
|
||||
proc reifiedOpenArray(n: PNode): bool {.inline.} =
|
||||
var x = n
|
||||
while true:
|
||||
case x.kind
|
||||
of {nkAddr, nkHiddenAddr, nkHiddenDeref}:
|
||||
x = x[0]
|
||||
of nkHiddenStdConv:
|
||||
x = x[1]
|
||||
else:
|
||||
break
|
||||
while x.kind in {nkAddr, nkHiddenAddr, nkHiddenStdConv, nkHiddenDeref}:
|
||||
x = x[0]
|
||||
if x.kind == nkSym and x.sym.kind == skParam:
|
||||
result = false
|
||||
else:
|
||||
result = true
|
||||
|
||||
proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareForMutation = false): (Rope, Rope) =
|
||||
proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope, Rope) =
|
||||
var a, b, c: TLoc
|
||||
initLocExpr(p, q[1], a)
|
||||
initLocExpr(p, q[2], b)
|
||||
initLocExpr(p, q[3], c)
|
||||
# bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
|
||||
# are mapped into ctPtrToArray, the dereference of which is skipped
|
||||
# in the `genDeref`. We need to skip these ptrs here
|
||||
let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef})
|
||||
# but first produce the required index checks:
|
||||
if optBoundsCheck in p.options:
|
||||
genBoundsCheck(p, a, b, c, ty)
|
||||
if prepareForMutation:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
genBoundsCheck(p, a, b, c)
|
||||
let ty = skipTypes(a.t, abstractVar+{tyPtr})
|
||||
let dest = getTypeDesc(p.module, destType)
|
||||
let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
|
||||
case ty.kind
|
||||
@@ -190,10 +168,8 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||
result = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest],
|
||||
lengthExpr)
|
||||
else:
|
||||
var lit = newRopeAppender()
|
||||
intLiteral(first, lit)
|
||||
result = ("($4*)($1)+(($2)-($3))" %
|
||||
[rdLoc(a), rdLoc(b), lit, dest],
|
||||
[rdLoc(a), rdLoc(b), intLiteral(first), dest],
|
||||
lengthExpr)
|
||||
of tyOpenArray, tyVarargs:
|
||||
if reifiedOpenArray(q[1]):
|
||||
@@ -211,17 +187,15 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareF
|
||||
optSeqDestructors in p.config.globalOptions:
|
||||
linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
|
||||
if atyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||
result = ("(($5) ? (($4*)(*$1)$3+($2)) : NIM_NIL)" %
|
||||
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
|
||||
result = ("($4*)(*$1)$3+($2)" % [rdLoc(a), rdLoc(b), dataField(p), dest],
|
||||
lengthExpr)
|
||||
else:
|
||||
result = ("(($5) ? (($4*)$1$3+($2)) : NIM_NIL)" %
|
||||
[rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
|
||||
result = ("($4*)$1$3+($2)" % [rdLoc(a), rdLoc(b), dataField(p), dest],
|
||||
lengthExpr)
|
||||
else:
|
||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
|
||||
proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
|
||||
proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
|
||||
var q = skipConv(n)
|
||||
var skipped = false
|
||||
while q.kind == nkStmtListExpr and q.len > 0:
|
||||
@@ -236,19 +210,19 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
|
||||
genStmts(p, q[i])
|
||||
q = q.lastSon
|
||||
let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0])
|
||||
result.add x & ", " & y
|
||||
result = x & ", " & y
|
||||
else:
|
||||
var a: TLoc
|
||||
initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a)
|
||||
case skipTypes(a.t, abstractVar+{tyStatic}).kind
|
||||
case skipTypes(a.t, abstractVar).kind
|
||||
of tyOpenArray, tyVarargs:
|
||||
if reifiedOpenArray(n):
|
||||
if a.t.kind in {tyVar, tyLent}:
|
||||
result.add "$1->Field0, $1->Field1" % [rdLoc(a)]
|
||||
result = "$1->Field0, $1->Field1" % [rdLoc(a)]
|
||||
else:
|
||||
result.add "$1.Field0, $1.Field1" % [rdLoc(a)]
|
||||
result = "$1.Field0, $1.Field1" % [rdLoc(a)]
|
||||
else:
|
||||
result.add "$1, $1Len_0" % [rdLoc(a)]
|
||||
result = "$1, $1Len_0" % [rdLoc(a)]
|
||||
of tyString, tySequence:
|
||||
let ntyp = skipTypes(n.typ, abstractInst)
|
||||
if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and
|
||||
@@ -257,24 +231,19 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
|
||||
if ntyp.kind in {tyVar} and not compileToCpp(p.module):
|
||||
var t: TLoc
|
||||
t.r = "(*$1)" % [a.rdLoc]
|
||||
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, t), dataField(p),
|
||||
dataFieldAccessor(p, "*" & a.rdLoc)]
|
||||
result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
|
||||
else:
|
||||
result.add "($4) ? ($1$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
|
||||
result = "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)]
|
||||
of tyArray:
|
||||
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
|
||||
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
|
||||
of tyPtr, tyRef:
|
||||
case lastSon(a.t).kind
|
||||
of tyString, tySequence:
|
||||
var t: TLoc
|
||||
t.r = "(*$1)" % [a.rdLoc]
|
||||
result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
|
||||
[a.rdLoc, lenExpr(p, t), dataField(p),
|
||||
dataFieldAccessor(p, "*" & a.rdLoc)]
|
||||
result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
|
||||
of tyArray:
|
||||
result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
|
||||
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
|
||||
else:
|
||||
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
|
||||
@@ -283,7 +252,7 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
|
||||
# Bug https://github.com/status-im/nimbus-eth2/issues/1549
|
||||
# Aliasing is preferred over stack overflows.
|
||||
# Also don't regress for non ARC-builds, too risky.
|
||||
if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
|
||||
if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcOrc} and
|
||||
getSize(p.config, a.lode.typ) < 1024:
|
||||
getTemp(p, a.lode.typ, result, needsInit=false)
|
||||
genAssignment(p, result, a, {})
|
||||
@@ -294,66 +263,50 @@ proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
|
||||
getTemp(p, a.lode.typ, result, needsInit=false)
|
||||
genAssignment(p, result, a, {})
|
||||
|
||||
proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} =
|
||||
proc genArgStringToCString(p: BProc, n: PNode, needsTmp: bool): Rope {.inline.} =
|
||||
var a: TLoc
|
||||
initLocExpr(p, n[0], a)
|
||||
appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
|
||||
ropecg(p.module, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
|
||||
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) =
|
||||
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rope =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
genArgStringToCString(p, n, result, needsTmp)
|
||||
result = genArgStringToCString(p, n, needsTmp)
|
||||
elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
|
||||
var n = if n.kind != nkHiddenAddr: n else: n[0]
|
||||
openArrayLoc(p, param.typ, n, result)
|
||||
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]) and
|
||||
(optByRef notin param.options or not p.module.compileToCpp):
|
||||
result = openArrayLoc(p, param.typ, n)
|
||||
elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
|
||||
initLocExpr(p, n, a)
|
||||
if n.kind in {nkCharLit..nkNilLit}:
|
||||
addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
|
||||
result = addrLoc(p.config, literalsNeedsTmp(p, a))
|
||||
else:
|
||||
addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
|
||||
result = addrLoc(p.config, withTmpIfNeeded(p, a, needsTmp))
|
||||
elif p.module.compileToCpp and param.typ.kind in {tyVar} and
|
||||
n.kind == nkHiddenAddr:
|
||||
# bug #23748: we need to introduce a temporary here. The expression type
|
||||
# will be a reference in C++ and we cannot create a temporary reference
|
||||
# variable. Thus, we create a temporary pointer variable instead.
|
||||
let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray
|
||||
if needsIndirect:
|
||||
n.typ = n.typ.exactReplica
|
||||
n.typ.flags.incl tfVarIsPtr
|
||||
initLocExprSingleUse(p, n, a)
|
||||
a = withTmpIfNeeded(p, a, needsTmp)
|
||||
if needsIndirect: a.flags.incl lfIndirect
|
||||
initLocExprSingleUse(p, n[0], a)
|
||||
# if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
|
||||
# means '*T'. See posix.nim for lots of examples that do that in the wild.
|
||||
let callee = call[0]
|
||||
if callee.kind == nkSym and
|
||||
{sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
|
||||
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {} and
|
||||
needsIndirect:
|
||||
addAddrLoc(p.config, a, result)
|
||||
{lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
|
||||
result = addrLoc(p.config, a)
|
||||
else:
|
||||
addRdLoc(a, result)
|
||||
result = rdLoc(a)
|
||||
else:
|
||||
initLocExprSingleUse(p, n, a)
|
||||
if param.typ.kind in abstractPtrs:
|
||||
let typ = skipTypes(param.typ, abstractPtrs)
|
||||
if typ.sym != nil and sfImportc in typ.sym.flags:
|
||||
a.r = "(($1) ($2))" %
|
||||
[getTypeDesc(p.module, param.typ), rdCharLoc(a)]
|
||||
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
|
||||
result = rdLoc(withTmpIfNeeded(p, a, needsTmp))
|
||||
#assert result != nil
|
||||
|
||||
proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) =
|
||||
proc genArgNoParam(p: BProc, n: PNode, needsTmp = false): Rope =
|
||||
var a: TLoc
|
||||
if n.kind == nkStringToCString:
|
||||
genArgStringToCString(p, n, result, needsTmp)
|
||||
result = genArgStringToCString(p, n, needsTmp)
|
||||
else:
|
||||
initLocExprSingleUse(p, n, a)
|
||||
addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
|
||||
result = rdLoc(withTmpIfNeeded(p, a, needsTmp))
|
||||
|
||||
import aliasanalysis
|
||||
from dfa import aliases, AliasKind
|
||||
|
||||
proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
|
||||
for p in potentialWrites:
|
||||
@@ -375,7 +328,7 @@ proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
|
||||
of nkLiterals, nkIdent, nkFormalParams: discard
|
||||
of nkSym:
|
||||
if mutate: result.add n
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
of nkAsgn, nkFastAsgn:
|
||||
getPotentialWrites(n[0], true, result)
|
||||
getPotentialWrites(n[1], mutate, result)
|
||||
of nkAddr, nkHiddenAddr:
|
||||
@@ -407,7 +360,7 @@ proc getPotentialReads(n: PNode; result: var seq[PNode]) =
|
||||
for s in n:
|
||||
getPotentialReads(s, result)
|
||||
|
||||
proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
|
||||
proc genParams(p: BProc, ri: PNode, typ: PType): Rope =
|
||||
# We must generate temporaries in cases like #14396
|
||||
# to keep the strict Left-To-Right evaluation
|
||||
var needTmp = newSeq[bool](ri.len - 1)
|
||||
@@ -423,27 +376,20 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
|
||||
if not needTmp[i - 1]:
|
||||
needTmp[i - 1] = potentialAlias(n, potentialWrites)
|
||||
getPotentialWrites(ri[i], false, potentialWrites)
|
||||
when false:
|
||||
# this optimization is wrong, see bug #23748
|
||||
if ri[i].kind in {nkHiddenAddr, nkAddr}:
|
||||
# Optimization: don't use a temp, if we would only take the address anyway
|
||||
needTmp[i - 1] = false
|
||||
if ri[i].kind == nkHiddenAddr:
|
||||
# Optimization: don't use a temp, if we would only take the adress anyway
|
||||
needTmp[i - 1] = false
|
||||
|
||||
var oldLen = result.len
|
||||
for i in 1..<ri.len:
|
||||
if i < typ.len:
|
||||
assert(typ.n[i].kind == nkSym)
|
||||
let paramType = typ.n[i]
|
||||
if not paramType.typ.isCompileTimeOnly:
|
||||
if oldLen != result.len:
|
||||
result.add(", ")
|
||||
oldLen = result.len
|
||||
genArg(p, ri[i], paramType.sym, ri, result, needTmp[i-1])
|
||||
if result != nil: result.add(~", ")
|
||||
result.add(genArg(p, ri[i], paramType.sym, ri, needTmp[i-1]))
|
||||
else:
|
||||
if oldLen != result.len:
|
||||
result.add(", ")
|
||||
oldLen = result.len
|
||||
genArgNoParam(p, ri[i], result, needTmp[i-1])
|
||||
if result != nil: result.add(~", ")
|
||||
result.add(genArgNoParam(p, ri[i], needTmp[i-1]))
|
||||
|
||||
proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
|
||||
if sym.flags * {sfImportc, sfNonReloadable} == {} and sym.loc.k == locProc and
|
||||
@@ -459,8 +405,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
assert(typ.kind == tyProc)
|
||||
assert(typ.len == typ.n.len)
|
||||
|
||||
var params = newRopeAppender()
|
||||
genParams(p, ri, typ, params)
|
||||
var params = genParams(p, ri, typ)
|
||||
|
||||
var callee = rdLoc(op)
|
||||
if p.hcrOn and ri[0].kind == nkSym:
|
||||
@@ -470,7 +415,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
|
||||
proc addComma(r: Rope): Rope =
|
||||
if r.len == 0: r else: r & ", "
|
||||
if r == nil: r else: r & ~", "
|
||||
|
||||
const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
|
||||
const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
|
||||
@@ -483,8 +428,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
assert(typ.kind == tyProc)
|
||||
assert(typ.len == typ.n.len)
|
||||
|
||||
var pl = newRopeAppender()
|
||||
genParams(p, ri, typ, pl)
|
||||
var pl = genParams(p, ri, typ)
|
||||
|
||||
template genCallPattern {.dirty.} =
|
||||
if tfIterator in typ.flags:
|
||||
@@ -495,10 +439,10 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
let rawProc = getClosureType(p.module, typ, clHalf)
|
||||
let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
|
||||
if typ[0] != nil:
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
if ri.len > 1: pl.add(", ")
|
||||
if isInvalidReturnType(p.config, typ[0]):
|
||||
if ri.len > 1: pl.add(~", ")
|
||||
# beware of 'result = p(result)'. We may need to allocate a temporary:
|
||||
if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
|
||||
if d.k in {locTemp, locNone} or not preventNrvo(p, le, ri):
|
||||
# Great, we can use 'd':
|
||||
if d.k == locNone:
|
||||
getTemp(p, typ[0], d, needsInit=true)
|
||||
@@ -507,7 +451,6 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
discard "resetLoc(p, d)"
|
||||
pl.add(addrLoc(p.config, d))
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ[0], tmp, needsInit=true)
|
||||
@@ -543,30 +486,24 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
genCallPattern()
|
||||
if canRaise: raiseExit(p)
|
||||
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
|
||||
argsCounter: var int) =
|
||||
proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
|
||||
if i < typ.len:
|
||||
# 'var T' is 'T&' in C++. This means we ignore the request of
|
||||
# any nkHiddenAddr when it's a 'var T'.
|
||||
let paramType = typ.n[i]
|
||||
assert(paramType.kind == nkSym)
|
||||
if paramType.typ.isCompileTimeOnly:
|
||||
discard
|
||||
result = nil
|
||||
elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i][0], result)
|
||||
inc argsCounter
|
||||
result = genArgNoParam(p, ri[i][0])
|
||||
else:
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i], result) #, typ.n[i].sym)
|
||||
inc argsCounter
|
||||
result = genArgNoParam(p, ri[i]) #, typ.n[i].sym)
|
||||
else:
|
||||
if tfVarargs notin typ.flags:
|
||||
localError(p.config, ri.info, "wrong argument count")
|
||||
result = nil
|
||||
else:
|
||||
if argsCounter > 0: result.add ", "
|
||||
genArgNoParam(p, ri[i], result)
|
||||
inc argsCounter
|
||||
result = genArgNoParam(p, ri[i])
|
||||
|
||||
discard """
|
||||
Dot call syntax in C++
|
||||
@@ -623,7 +560,7 @@ proc skipAddrDeref(node: PNode): PNode =
|
||||
else:
|
||||
result = node
|
||||
|
||||
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
|
||||
proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
|
||||
# for better or worse c2nim translates the 'this' argument to a 'var T'.
|
||||
# However manual wrappers may also use 'ptr T'. In any case we support both
|
||||
# for convenience.
|
||||
@@ -637,72 +574,75 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope) =
|
||||
if t.kind in {tyVar}:
|
||||
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
|
||||
if x.typ.kind == tyPtr:
|
||||
genArgNoParam(p, x, result)
|
||||
result = genArgNoParam(p, x)
|
||||
result.add("->")
|
||||
elif x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].typ.kind == tyPtr:
|
||||
genArgNoParam(p, x[0], result)
|
||||
result = genArgNoParam(p, x[0])
|
||||
result.add("->")
|
||||
else:
|
||||
genArgNoParam(p, x, result)
|
||||
result = genArgNoParam(p, x)
|
||||
result.add(".")
|
||||
elif t.kind == tyPtr:
|
||||
if ri.kind in {nkAddr, nkHiddenAddr}:
|
||||
genArgNoParam(p, ri[0], result)
|
||||
result = genArgNoParam(p, ri[0])
|
||||
result.add(".")
|
||||
else:
|
||||
genArgNoParam(p, ri, result)
|
||||
result = genArgNoParam(p, ri)
|
||||
result.add("->")
|
||||
else:
|
||||
ri = skipAddrDeref(ri)
|
||||
if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0]
|
||||
genArgNoParam(p, ri, result) #, typ.n[i].sym)
|
||||
result = genArgNoParam(p, ri) #, typ.n[i].sym)
|
||||
result.add(".")
|
||||
|
||||
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Rope) =
|
||||
proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
|
||||
var i = 0
|
||||
var j = 1
|
||||
while i < pat.len:
|
||||
case pat[i]
|
||||
of '@':
|
||||
var argsCounter = 0
|
||||
var first = true
|
||||
for k in j..<ri.len:
|
||||
genOtherArg(p, ri, k, typ, result, argsCounter)
|
||||
let arg = genOtherArg(p, ri, k, typ)
|
||||
if arg.len > 0:
|
||||
if not first:
|
||||
result.add(~", ")
|
||||
first = false
|
||||
result.add arg
|
||||
inc i
|
||||
of '#':
|
||||
if i+1 < pat.len and pat[i+1] in {'+', '@'}:
|
||||
let ri = ri[j]
|
||||
if ri.kind in nkCallKinds:
|
||||
let typ = skipTypes(ri[0].typ, abstractInst)
|
||||
if pat[i+1] == '+': genArgNoParam(p, ri[0], result)
|
||||
result.add("(")
|
||||
if pat[i+1] == '+': result.add genArgNoParam(p, ri[0])
|
||||
result.add(~"(")
|
||||
if 1 < ri.len:
|
||||
var argsCounterB = 0
|
||||
genOtherArg(p, ri, 1, typ, result, argsCounterB)
|
||||
result.add genOtherArg(p, ri, 1, typ)
|
||||
for k in j+1..<ri.len:
|
||||
var argsCounterB = 0
|
||||
genOtherArg(p, ri, k, typ, result, argsCounterB)
|
||||
result.add(")")
|
||||
result.add(~", ")
|
||||
result.add genOtherArg(p, ri, k, typ)
|
||||
result.add(~")")
|
||||
else:
|
||||
localError(p.config, ri.info, "call expression expected for C++ pattern")
|
||||
inc i
|
||||
elif i+1 < pat.len and pat[i+1] == '.':
|
||||
genThisArg(p, ri, j, typ, result)
|
||||
result.add genThisArg(p, ri, j, typ)
|
||||
inc i
|
||||
elif i+1 < pat.len and pat[i+1] == '[':
|
||||
var arg = ri[j].skipAddrDeref
|
||||
while arg.kind in {nkAddr, nkHiddenAddr, nkObjDownConv}: arg = arg[0]
|
||||
genArgNoParam(p, arg, result)
|
||||
result.add genArgNoParam(p, arg)
|
||||
#result.add debugTree(arg, 0, 10)
|
||||
else:
|
||||
var argsCounter = 0
|
||||
genOtherArg(p, ri, j, typ, result, argsCounter)
|
||||
result.add genOtherArg(p, ri, j, typ)
|
||||
inc j
|
||||
inc i
|
||||
of '\'':
|
||||
var idx, stars: int
|
||||
if scanCppGenericSlot(pat, i, idx, stars):
|
||||
var t = resolveStarsInCppType(typ, idx, stars)
|
||||
if t == nil: result.add("void")
|
||||
if t == nil: result.add(~"void")
|
||||
else: result.add(getTypeDesc(p.module, t))
|
||||
else:
|
||||
let start = i
|
||||
@@ -720,11 +660,10 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
assert(typ.kind == tyProc)
|
||||
assert(typ.len == typ.n.len)
|
||||
# don't call '$' here for efficiency:
|
||||
let pat = $ri[0].sym.loc.r
|
||||
let pat = ri[0].sym.loc.r.data
|
||||
internalAssert p.config, pat.len > 0
|
||||
if pat.contains({'#', '(', '@', '\''}):
|
||||
var pl = newRopeAppender()
|
||||
genPatternCall(p, ri, pat, typ, pl)
|
||||
var pl = genPatternCall(p, ri, pat, typ)
|
||||
# simpler version of 'fixupCall' that works with the pl+params combination:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
if typ[0] != nil:
|
||||
@@ -743,79 +682,80 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add(";\n")
|
||||
pl.add(~";$n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var pl = newRopeAppender()
|
||||
var argsCounter = 0
|
||||
var pl: Rope = nil
|
||||
#var param = typ.n[1].sym
|
||||
if 1 < ri.len:
|
||||
genThisArg(p, ri, 1, typ, pl)
|
||||
pl.add(genThisArg(p, ri, 1, typ))
|
||||
pl.add(op.r)
|
||||
var params = newRopeAppender()
|
||||
var params: Rope
|
||||
for i in 2..<ri.len:
|
||||
if params != nil: params.add(~", ")
|
||||
assert(typ.len == typ.n.len)
|
||||
genOtherArg(p, ri, i, typ, params, argsCounter)
|
||||
params.add(genOtherArg(p, ri, i, typ))
|
||||
fixupCall(p, le, ri, d, pl, params)
|
||||
|
||||
proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
# generates a crappy ObjC call
|
||||
var op: TLoc
|
||||
initLocExpr(p, ri[0], op)
|
||||
var pl = "["
|
||||
var pl = ~"["
|
||||
# getUniqueType() is too expensive here:
|
||||
var typ = skipTypes(ri[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
assert(typ.len == typ.n.len)
|
||||
|
||||
# don't call '$' here for efficiency:
|
||||
let pat = $ri[0].sym.loc.r
|
||||
let pat = ri[0].sym.loc.r.data
|
||||
internalAssert p.config, pat.len > 0
|
||||
var start = 3
|
||||
if ' ' in pat:
|
||||
start = 1
|
||||
pl.add(op.r)
|
||||
if ri.len > 1:
|
||||
pl.add(": ")
|
||||
genArg(p, ri[1], typ.n[1].sym, ri, pl)
|
||||
pl.add(~": ")
|
||||
pl.add(genArg(p, ri[1], typ.n[1].sym, ri))
|
||||
start = 2
|
||||
else:
|
||||
if ri.len > 1:
|
||||
genArg(p, ri[1], typ.n[1].sym, ri, pl)
|
||||
pl.add(" ")
|
||||
pl.add(genArg(p, ri[1], typ.n[1].sym, ri))
|
||||
pl.add(~" ")
|
||||
pl.add(op.r)
|
||||
if ri.len > 2:
|
||||
pl.add(": ")
|
||||
genArg(p, ri[2], typ.n[2].sym, ri, pl)
|
||||
pl.add(~": ")
|
||||
pl.add(genArg(p, ri[2], typ.n[2].sym, ri))
|
||||
for i in start..<ri.len:
|
||||
assert(typ.len == typ.n.len)
|
||||
if i >= typ.len:
|
||||
internalError(p.config, ri.info, "varargs for objective C method?")
|
||||
assert(typ.n[i].kind == nkSym)
|
||||
var param = typ.n[i].sym
|
||||
pl.add(" ")
|
||||
pl.add(~" ")
|
||||
pl.add(param.name.s)
|
||||
pl.add(": ")
|
||||
genArg(p, ri[i], param, ri, pl)
|
||||
pl.add(~": ")
|
||||
pl.add(genArg(p, ri[i], param, ri))
|
||||
if typ[0] != nil:
|
||||
if isInvalidReturnType(p.config, typ):
|
||||
if ri.len > 1: pl.add(" ")
|
||||
if isInvalidReturnType(p.config, typ[0]):
|
||||
if ri.len > 1: pl.add(~" ")
|
||||
# beware of 'result = p(result)'. We always allocate a temporary:
|
||||
if d.k in {locTemp, locNone}:
|
||||
# We already got a temp. Great, special case it:
|
||||
if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
|
||||
pl.add("Result: ")
|
||||
pl.add(~"Result: ")
|
||||
pl.add(addrLoc(p.config, d))
|
||||
pl.add("];\n")
|
||||
pl.add(~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
else:
|
||||
var tmp: TLoc
|
||||
getTemp(p, typ[0], tmp, needsInit=true)
|
||||
pl.add(addrLoc(p.config, tmp))
|
||||
pl.add("];\n")
|
||||
pl.add(~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
genAssignment(p, d, tmp, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add("]")
|
||||
pl.add(~"]")
|
||||
if d.k == locNone: getTemp(p, typ[0], d)
|
||||
assert(d.t != nil) # generate an assignment to d:
|
||||
var list: TLoc
|
||||
@@ -823,7 +763,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||
list.r = pl
|
||||
genAssignment(p, d, list, {}) # no need for deep copying
|
||||
else:
|
||||
pl.add("];\n")
|
||||
pl.add(~"];$n")
|
||||
line(p, cpsStmts, pl)
|
||||
|
||||
proc notYetAlive(n: PNode): bool {.inline.} =
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,7 +21,7 @@ template detectVersion(field, corename) =
|
||||
if core == nil or core.kind != skConst:
|
||||
m.g.field = 1
|
||||
else:
|
||||
m.g.field = toInt(ast.getInt(core.astdef))
|
||||
m.g.field = toInt(ast.getInt(core.ast))
|
||||
result = m.g.field
|
||||
|
||||
proc detectStrVersion(m: BModule): int =
|
||||
@@ -32,87 +32,82 @@ proc detectSeqVersion(m: BModule): int =
|
||||
|
||||
# ----- 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 genStringLiteralDataOnlyV1(m: BModule, s: string): Rope =
|
||||
discard cgsym(m, "TGenericSeq")
|
||||
result = getTempName(m)
|
||||
m.s[cfsData].addf("STRING_LITERAL($1, $2, $3);$n",
|
||||
[result, makeCString(s), rope(s.len)])
|
||||
|
||||
proc genStringLiteralV1(m: BModule; n: PNode; result: var Rope) =
|
||||
proc genStringLiteralV1(m: BModule; n: PNode): Rope =
|
||||
if s.isNil:
|
||||
appcg(m, result, "((#NimStringDesc*) NIM_NIL)", [])
|
||||
result = ropecg(m, "((#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 ")"
|
||||
result = ropecg(m, "((#NimStringDesc*) &$1)",
|
||||
[genStringLiteralDataOnlyV1(m, n.strVal)])
|
||||
else:
|
||||
appcg(m, result, "((#NimStringDesc*) &$1$2)",
|
||||
result = ropecg(m, "((#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" &
|
||||
m.s[cfsData].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) =
|
||||
proc genStringLiteralV2(m: BModule; n: PNode; isConst: bool): 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")
|
||||
result = getTempName(m)
|
||||
discard cgsym(m, "NimStrPayload")
|
||||
discard 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: "")])
|
||||
m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[result, 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),
|
||||
result = getTempName(m)
|
||||
m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n",
|
||||
[result, rope(n.strVal.len), m.tmpBase & rope(id),
|
||||
rope(if isConst: "const" else: "")])
|
||||
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool; result: var Rope) =
|
||||
proc genStringLiteralV2Const(m: BModule; n: PNode; isConst: bool): 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")
|
||||
discard cgsym(m, "NimStrPayload")
|
||||
discard 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]
|
||||
result = "{$1, (NimStrPayload*)&$2}" % [rope(n.strVal.len), pureLit]
|
||||
|
||||
# ------ Version selector ---------------------------------------------------
|
||||
|
||||
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo;
|
||||
isConst: bool; result: var Rope) =
|
||||
isConst: bool): Rope =
|
||||
case detectStrVersion(m)
|
||||
of 0, 1: genStringLiteralDataOnlyV1(m, s, result)
|
||||
of 0, 1: result = genStringLiteralDataOnlyV1(m, s)
|
||||
of 2:
|
||||
let tmp = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, s, tmp, isConst)
|
||||
result.add tmp
|
||||
result = getTempName(m)
|
||||
genStringLiteralDataOnlyV2(m, s, result, isConst)
|
||||
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 genNilStringLiteral(m: BModule; info: TLineInfo): Rope =
|
||||
result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", [])
|
||||
|
||||
proc genStringLiteral(m: BModule; n: PNode; result: var Rope) =
|
||||
proc genStringLiteral(m: BModule; n: PNode): Rope =
|
||||
case detectStrVersion(m)
|
||||
of 0, 1: genStringLiteralV1(m, n, result)
|
||||
of 2: genStringLiteralV2(m, n, isConst = true, result)
|
||||
of 0, 1: result = genStringLiteralV1(m, n)
|
||||
of 2: result = genStringLiteralV2(m, n, isConst = true)
|
||||
else:
|
||||
localError(m.config, n.info, "cannot determine how to produce code for string literal")
|
||||
|
||||
@@ -19,11 +19,13 @@ import
|
||||
|
||||
const
|
||||
CFileSectionNames: array[TCFileSection, string] = [
|
||||
cfsMergeInfo: "",
|
||||
cfsHeaders: "NIM_merge_HEADERS",
|
||||
cfsFrameDefines: "NIM_merge_FRAME_DEFINES",
|
||||
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",
|
||||
@@ -32,8 +34,11 @@ const
|
||||
cfsInitProc: "NIM_merge_INIT_PROC",
|
||||
cfsDatInitProc: "NIM_merge_DATINIT_PROC",
|
||||
cfsTypeInit1: "NIM_merge_TYPE_INIT1",
|
||||
cfsTypeInit2: "NIM_merge_TYPE_INIT2",
|
||||
cfsTypeInit3: "NIM_merge_TYPE_INIT3",
|
||||
cfsDynLibInit: "NIM_merge_DYNLIB_INIT"
|
||||
cfsDebugInit: "NIM_merge_DEBUG_INIT",
|
||||
cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
|
||||
cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
|
||||
]
|
||||
CProcSectionNames: array[TCProcSection, string] = [
|
||||
cpsLocals: "NIM_merge_PROC_LOCALS",
|
||||
|
||||
@@ -24,7 +24,7 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
|
||||
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.r == nil: 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])
|
||||
@@ -42,7 +42,7 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
if field.loc.r == "": fillObjectFields(p.module, typ)
|
||||
if field.loc.r == nil: 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)
|
||||
@@ -83,24 +83,11 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
|
||||
lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
|
||||
else:
|
||||
lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
|
||||
of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
|
||||
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, tyOpenArray, tyForward, tyVarargs,
|
||||
tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
|
||||
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
|
||||
tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}:
|
||||
else:
|
||||
discard
|
||||
|
||||
proc specializeReset(p: BProc, a: TLoc) =
|
||||
|
||||
@@ -8,46 +8,37 @@
|
||||
#
|
||||
|
||||
# included from cgen.nim
|
||||
|
||||
const
|
||||
RangeExpandLimit = 256 # do not generate ranges
|
||||
# over 'RangeExpandLimit' elements
|
||||
stringCaseThreshold = 8
|
||||
# above X strings a hash-switch for strings is generated
|
||||
|
||||
proc registerTraverseProc(p: BProc, v: PSym) =
|
||||
var traverseProc = ""
|
||||
if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcRefc} and
|
||||
proc getTraverseProc(p: BProc, v: PSym): Rope =
|
||||
if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcV2, gcRefc} and
|
||||
optOwnedRefs notin p.config.globalOptions and
|
||||
containsManagedMemory(v.loc.t):
|
||||
containsGarbageCollectedRef(v.loc.t):
|
||||
# we register a specialized marked proc here; this has the advantage
|
||||
# that it works out of the box for thread local storage then :-)
|
||||
traverseProc = genTraverseProcForGlobal(p.module, v, v.info)
|
||||
result = genTraverseProcForGlobal(p.module, v, v.info)
|
||||
|
||||
if traverseProc.len != 0 and not p.hcrOn:
|
||||
if sfThread in v.flags:
|
||||
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
||||
"$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc])
|
||||
else:
|
||||
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
||||
"$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc])
|
||||
proc registerTraverseProc(p: BProc, v: PSym, traverseProc: Rope) =
|
||||
if sfThread in v.flags:
|
||||
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
||||
"$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc])
|
||||
else:
|
||||
appcg(p.module, p.module.preInitProc.procSec(cpsInit),
|
||||
"$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc])
|
||||
|
||||
proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
|
||||
if n.kind == nkEmpty:
|
||||
result = false
|
||||
elif n.kind in nkCallKinds and n[0] != nil and n[0].typ != nil and n[0].typ.skipTypes(abstractInst).kind == tyProc:
|
||||
if n[0].kind == nkSym and sfConstructor in n[0].sym.flags:
|
||||
result = true
|
||||
elif isInvalidReturnType(conf, n[0].typ, true):
|
||||
# var v = f()
|
||||
# is transformed into: var v; f(addr v)
|
||||
# where 'f' **does not** initialize the result!
|
||||
result = false
|
||||
else:
|
||||
result = true
|
||||
elif isInvalidReturnType(conf, n.typ, false):
|
||||
result = false
|
||||
else:
|
||||
result = true
|
||||
if n.kind == nkEmpty: return false
|
||||
if isInvalidReturnType(conf, n.typ):
|
||||
# var v = f()
|
||||
# is transformed into: var v; f(addr v)
|
||||
# where 'f' **does not** initialize the result!
|
||||
return false
|
||||
result = true
|
||||
|
||||
proc inExceptBlockLen(p: BProc): int =
|
||||
for x in p.nestedTryStmts:
|
||||
@@ -56,9 +47,7 @@ proc inExceptBlockLen(p: BProc): int =
|
||||
proc startBlockInternal(p: BProc): int {.discardable.} =
|
||||
inc(p.labels)
|
||||
result = p.blocks.len
|
||||
|
||||
p.blocks.add initBlock()
|
||||
|
||||
setLen(p.blocks, result + 1)
|
||||
p.blocks[result].id = p.labels
|
||||
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
|
||||
p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16
|
||||
@@ -82,7 +71,7 @@ proc genVarTuple(p: BProc, n: PNode) =
|
||||
|
||||
# check only the first son
|
||||
var forHcr = treatGlobalDifferentlyForHCR(p.module, n[0].sym)
|
||||
let hcrCond = if forHcr: getTempName(p.module) else: ""
|
||||
let hcrCond = if forHcr: getTempName(p.module) else: nil
|
||||
var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]]
|
||||
# determine if the tuple is constructed at top-level scope or inside of a block (if/while/block)
|
||||
let isGlobalInBlock = forHcr and p.blocks.len > 2
|
||||
@@ -101,10 +90,13 @@ proc genVarTuple(p: BProc, n: PNode) =
|
||||
let vn = n[i]
|
||||
let v = vn.sym
|
||||
if sfCompileTime in v.flags: continue
|
||||
var traverseProc: Rope
|
||||
if sfGlobal in v.flags:
|
||||
assignGlobalVar(p, vn, "")
|
||||
assignGlobalVar(p, vn, nil)
|
||||
genObjectInit(p, cpsInit, v.typ, v.loc, constructObj)
|
||||
registerTraverseProc(p, v)
|
||||
traverseProc = getTraverseProc(p, v)
|
||||
if traverseProc != nil and not p.hcrOn:
|
||||
registerTraverseProc(p, v, traverseProc)
|
||||
else:
|
||||
assignLocalVar(p, vn)
|
||||
initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
|
||||
@@ -116,7 +108,7 @@ proc genVarTuple(p: BProc, n: PNode) =
|
||||
field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
|
||||
putLocIntoDest(p, v.loc, field)
|
||||
if forHcr or isGlobalInBlock:
|
||||
hcrGlobals.add((loc: v.loc, tp: "NULL"))
|
||||
hcrGlobals.add((loc: v.loc, tp: if traverseProc == nil: ~"NULL" else: traverseProc))
|
||||
|
||||
if forHcr:
|
||||
# end the block where the tuple gets initialized
|
||||
@@ -146,12 +138,12 @@ proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
|
||||
a.flags.incl(lfEnforceDeref)
|
||||
expr(p, ri, a)
|
||||
|
||||
proc assignLabel(b: var TBlock; result: var Rope) {.inline.} =
|
||||
proc assignLabel(b: var TBlock): Rope {.inline.} =
|
||||
b.label = "LA" & b.id.rope
|
||||
result.add b.label
|
||||
result = b.label
|
||||
|
||||
proc blockBody(b: var TBlock; result: var Rope) =
|
||||
result.add b.sections[cpsLocals]
|
||||
proc blockBody(b: var TBlock): Rope =
|
||||
result = b.sections[cpsLocals]
|
||||
if b.frameLen > 0:
|
||||
result.addf("FR_.len+=$1;$n", [b.frameLen.rope])
|
||||
result.add(b.sections[cpsInit])
|
||||
@@ -160,7 +152,7 @@ proc blockBody(b: var TBlock; result: var Rope) =
|
||||
proc endBlock(p: BProc, blockEnd: Rope) =
|
||||
let topBlock = p.blocks.len-1
|
||||
# the block is merged into the parent block
|
||||
p.blocks[topBlock].blockBody(p.blocks[topBlock-1].sections[cpsStmts])
|
||||
p.blocks[topBlock-1].sections[cpsStmts].add(p.blocks[topBlock].blockBody)
|
||||
setLen(p.blocks, topBlock)
|
||||
# this is done after the block is popped so $n is
|
||||
# properly indented when pretty printing is enabled
|
||||
@@ -172,7 +164,7 @@ proc endBlock(p: BProc) =
|
||||
var blockEnd: Rope
|
||||
if frameLen > 0:
|
||||
blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope])
|
||||
if p.blocks[topBlock].label.len != 0:
|
||||
if p.blocks[topBlock].label != nil:
|
||||
blockEnd.addf("} $1: ;$n", [p.blocks[topBlock].label])
|
||||
else:
|
||||
blockEnd.addf("}$n", [])
|
||||
@@ -280,41 +272,26 @@ proc genGotoVar(p: BProc; value: PNode) =
|
||||
else:
|
||||
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
|
||||
|
||||
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; result: var Rope)
|
||||
proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope
|
||||
|
||||
proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
|
||||
proc potentialValueInit(p: BProc; v: PSym; value: PNode): Rope =
|
||||
if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn:
|
||||
discard "nothing to do"
|
||||
result = nil
|
||||
elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value, p.module.compileToCpp) and
|
||||
p.withinLoop == 0 and not containsGarbageCollectedRef(v.typ):
|
||||
#echo "New code produced for ", v.name.s, " ", p.config $ value.info
|
||||
genBracedInit(p, value, isConst = false, v.typ, result)
|
||||
|
||||
proc genCppVarForCtor(p: BProc, v: PSym; vn, value: PNode; decl: var Rope) =
|
||||
var params = newRopeAppender()
|
||||
var argsCounter = 0
|
||||
let typ = skipTypes(value[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
for i in 1..<value.len:
|
||||
assert(typ.len == typ.n.len)
|
||||
genOtherArg(p, value, i, typ, params, argsCounter)
|
||||
if params.len == 0:
|
||||
decl = runtimeFormat("$#;\n", [decl])
|
||||
result = genBracedInit(p, value, isConst = false, v.typ)
|
||||
else:
|
||||
decl = runtimeFormat("$#($#);\n", [decl, params])
|
||||
result = nil
|
||||
|
||||
proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||
if sfGoto in v.flags:
|
||||
# translate 'var state {.goto.} = X' into 'goto LX':
|
||||
genGotoVar(p, value)
|
||||
return
|
||||
let imm = isAssignedImmediately(p.config, value)
|
||||
let isCppCtorCall = p.module.compileToCpp and imm and
|
||||
value.kind in nkCallKinds and value[0].kind == nkSym and
|
||||
v.typ.kind != tyPtr and sfConstructor in value[0].sym.flags
|
||||
var targetProc = p
|
||||
var valueAsRope = ""
|
||||
potentialValueInit(p, v, value, valueAsRope)
|
||||
var traverseProc: Rope
|
||||
let valueAsRope = potentialValueInit(p, v, value)
|
||||
if sfGlobal in v.flags:
|
||||
if v.flags * {sfImportc, sfExportc} == {sfImportc} and
|
||||
value.kind == nkEmpty and
|
||||
@@ -323,19 +300,16 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||
if sfPure in v.flags:
|
||||
# v.owner.kind != skModule:
|
||||
targetProc = p.module.preInitProc
|
||||
if isCppCtorCall and not containsHiddenPointer(v.typ):
|
||||
callGlobalVarCppCtor(targetProc, v, vn, value)
|
||||
else:
|
||||
assignGlobalVar(targetProc, vn, valueAsRope)
|
||||
|
||||
assignGlobalVar(targetProc, vn, valueAsRope)
|
||||
# XXX: be careful here.
|
||||
# Global variables should not be zeromem-ed within loops
|
||||
# (see bug #20).
|
||||
# That's why we are doing the construction inside the preInitProc.
|
||||
# genObjectInit relies on the C runtime's guarantees that
|
||||
# global variables will be initialized to zero.
|
||||
if valueAsRope.len == 0:
|
||||
if valueAsRope == nil:
|
||||
var loc = v.loc
|
||||
|
||||
# When the native TLS is unavailable, a global thread-local variable needs
|
||||
# one more layer of indirection in order to access the TLS block.
|
||||
# Only do this for complex types that may need a call to `objectInit`
|
||||
@@ -347,28 +321,40 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||
# if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
|
||||
if sfExportc in v.flags and p.module.g.generatedHeader != nil:
|
||||
genVarPrototype(p.module.g.generatedHeader, vn)
|
||||
registerTraverseProc(p, v)
|
||||
traverseProc = getTraverseProc(p, v)
|
||||
if traverseProc != nil and not p.hcrOn:
|
||||
registerTraverseProc(p, v, traverseProc)
|
||||
else:
|
||||
let imm = isAssignedImmediately(p.config, value)
|
||||
if imm and p.module.compileToCpp and p.splitDecls == 0 and
|
||||
not containsHiddenPointer(v.typ) and
|
||||
nimErrorFlagAccessed notin p.flags:
|
||||
not containsHiddenPointer(v.typ):
|
||||
# C++ really doesn't like things like 'Foo f; f = x' as that invokes a
|
||||
# parameterless constructor followed by an assignment operator. So we
|
||||
# generate better code here: 'Foo f = x;'
|
||||
genLineDir(p, vn)
|
||||
var decl = localVarDecl(p, vn)
|
||||
let decl = localVarDecl(p, vn)
|
||||
var tmp: TLoc
|
||||
if isCppCtorCall:
|
||||
genCppVarForCtor(p, v, vn, value, decl)
|
||||
line(p, cpsStmts, decl)
|
||||
if value.kind in nkCallKinds and value[0].kind == nkSym and
|
||||
sfConstructor in value[0].sym.flags:
|
||||
var params: Rope
|
||||
let typ = skipTypes(value[0].typ, abstractInst)
|
||||
assert(typ.kind == tyProc)
|
||||
for i in 1..<value.len:
|
||||
if params != nil: params.add(~", ")
|
||||
assert(typ.len == typ.n.len)
|
||||
params.add(genOtherArg(p, value, i, typ))
|
||||
if params == nil:
|
||||
lineF(p, cpsStmts, "$#;$n", [decl])
|
||||
else:
|
||||
lineF(p, cpsStmts, "$#($#);$n", [decl, params])
|
||||
else:
|
||||
initLocExprSingleUse(p, value, tmp)
|
||||
lineF(p, cpsStmts, "$# = $#;\n", [decl, tmp.rdLoc])
|
||||
lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc])
|
||||
return
|
||||
assignLocalVar(p, vn)
|
||||
initLocalVar(p, v, imm)
|
||||
|
||||
let traverseProc = "NULL"
|
||||
if traverseProc == nil: traverseProc = ~"NULL"
|
||||
# If the var is in a block (control flow like if/while or a block) in global scope just
|
||||
# register the so called "global" so it can be used later on. There is no need to close
|
||||
# and reopen of if (nim_hcr_do_init_) blocks because we are in one already anyway.
|
||||
@@ -388,10 +374,9 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
|
||||
lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N",
|
||||
[v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
|
||||
startBlock(targetProc)
|
||||
if value.kind != nkEmpty and valueAsRope.len == 0:
|
||||
if value.kind != nkEmpty and valueAsRope == nil:
|
||||
genLineDir(targetProc, vn)
|
||||
if not isCppCtorCall:
|
||||
loadInto(targetProc, vn, value, v.loc)
|
||||
loadInto(targetProc, vn, value, v.loc)
|
||||
if forHcr:
|
||||
endBlock(targetProc)
|
||||
|
||||
@@ -413,7 +398,7 @@ proc genClosureVar(p: BProc, a: PNode) =
|
||||
genLineDir(p, a)
|
||||
if immediateAsgn:
|
||||
loadInto(p, a[0], a[2], v)
|
||||
elif sfNoInit notin a[0][1].sym.flags:
|
||||
else:
|
||||
constructLoc(p, v)
|
||||
|
||||
proc genVarStmt(p: BProc, n: PNode) =
|
||||
@@ -568,9 +553,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
|
||||
return
|
||||
|
||||
let val = getOrdValue(it[j])
|
||||
var lit = newRopeAppender()
|
||||
intLiteral(toInt64(val)+id+1, lit)
|
||||
lineF(p, cpsStmts, "TMP$#_:$n", [lit])
|
||||
lineF(p, cpsStmts, "TMP$#_:$n", [intLiteral(toInt64(val)+id+1)])
|
||||
|
||||
genStmts(p, it.lastSon)
|
||||
|
||||
@@ -624,9 +607,8 @@ proc genWhileStmt(p: BProc, t: PNode) =
|
||||
p.blocks[p.breakIdx].isLoop = true
|
||||
initLocExpr(p, t[0], a)
|
||||
if (t[0].kind != nkIntLit) or (t[0].intVal == 0):
|
||||
lineF(p, cpsStmts, "if (!$1) goto ", [rdLoc(a)])
|
||||
assignLabel(p.blocks[p.breakIdx], p.s(cpsStmts))
|
||||
appcg(p, cpsStmts, ";$n", [])
|
||||
let label = assignLabel(p.blocks[p.breakIdx])
|
||||
lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), label])
|
||||
genStmts(p, loopBody)
|
||||
|
||||
if optProfiler in p.options:
|
||||
@@ -666,7 +648,7 @@ proc genParForStmt(p: BProc, t: PNode) =
|
||||
#initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack)
|
||||
#discard mangleName(forLoopVar)
|
||||
let call = t[1]
|
||||
assert(call.len == 4 or call.len == 5)
|
||||
assert(call.len in {4, 5})
|
||||
initLocExpr(p, call[1], rangeA)
|
||||
initLocExpr(p, call[2], rangeB)
|
||||
|
||||
@@ -713,12 +695,12 @@ proc genBreakStmt(p: BProc, t: PNode) =
|
||||
while idx >= 0 and not p.blocks[idx].isLoop: dec idx
|
||||
if idx < 0 or not p.blocks[idx].isLoop:
|
||||
internalError(p.config, t.info, "no loop to break")
|
||||
p.blocks[idx].label = "LA" & p.blocks[idx].id.rope
|
||||
let label = assignLabel(p.blocks[idx])
|
||||
blockLeaveActions(p,
|
||||
p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
|
||||
p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts)
|
||||
genLineDir(p, t)
|
||||
lineF(p, cpsStmts, "goto $1;$n", [p.blocks[idx].label])
|
||||
lineF(p, cpsStmts, "goto $1;$n", [label])
|
||||
|
||||
proc raiseExit(p: BProc) =
|
||||
assert p.config.exc == excGoto
|
||||
@@ -740,19 +722,21 @@ proc finallyActions(p: BProc) =
|
||||
if finallyBlock != nil:
|
||||
genSimpleBlock(p, finallyBlock[0])
|
||||
|
||||
proc raiseInstr(p: BProc; result: var Rope) =
|
||||
proc raiseInstr(p: BProc): Rope =
|
||||
if p.config.exc == excGoto:
|
||||
let L = p.nestedTryStmts.len
|
||||
if L == 0:
|
||||
p.flags.incl beforeRetNeeded
|
||||
# easy case, simply goto 'ret':
|
||||
result.add ropecg(p.module, "goto BeforeRet_;$n", [])
|
||||
result = ropecg(p.module, "goto BeforeRet_;$n", [])
|
||||
else:
|
||||
# raise inside an 'except' must go to the finally block,
|
||||
# raise outside an 'except' block must go to the 'except' list.
|
||||
result.add ropecg(p.module, "goto LA$1_;$n",
|
||||
result = ropecg(p.module, "goto LA$1_;$n",
|
||||
[p.nestedTryStmts[L-1].label])
|
||||
# + ord(p.nestedTryStmts[L-1].inExcept)])
|
||||
else:
|
||||
result = nil
|
||||
|
||||
proc genRaiseStmt(p: BProc, t: PNode) =
|
||||
if t[0].kind != nkEmpty:
|
||||
@@ -779,8 +763,14 @@ proc genRaiseStmt(p: BProc, t: PNode) =
|
||||
else:
|
||||
finallyActions(p)
|
||||
genLineDir(p, t)
|
||||
linefmt(p, cpsStmts, "#reraiseException();$n", [])
|
||||
raiseInstr(p, p.s(cpsStmts))
|
||||
# reraise the last exception:
|
||||
if p.config.exc == excCpp:
|
||||
line(p, cpsStmts, ~"throw;$n")
|
||||
else:
|
||||
linefmt(p, cpsStmts, "#reraiseException();$n", [])
|
||||
let gotoInstr = raiseInstr(p)
|
||||
if gotoInstr != nil:
|
||||
line(p, cpsStmts, gotoInstr)
|
||||
|
||||
template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
|
||||
rangeFormat, eqFormat: FormatStr, labl: TLabel) =
|
||||
@@ -840,27 +830,17 @@ template genCaseGeneric(p: BProc, t: PNode, d: var TLoc,
|
||||
fixLabel(p, lend)
|
||||
|
||||
proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
|
||||
stringKind: TTypeKind,
|
||||
branches: var openArray[Rope]) =
|
||||
var x: TLoc
|
||||
for i in 0..<b.len - 1:
|
||||
assert(b[i].kind != nkRange)
|
||||
initLocExpr(p, b[i], x)
|
||||
var j: int
|
||||
case b[i].kind
|
||||
of nkStrLit..nkTripleStrLit:
|
||||
j = int(hashString(p.config, b[i].strVal) and high(branches))
|
||||
of nkNilLit: j = 0
|
||||
else:
|
||||
assert false, "invalid string case branch node kind"
|
||||
if stringKind == tyCstring:
|
||||
appcg(p.module, branches[j], "if (#eqCstrings($1, $2)) goto $3;$n",
|
||||
[rdLoc(e), rdLoc(x), labl])
|
||||
else:
|
||||
appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n",
|
||||
assert(b[i].kind in {nkStrLit..nkTripleStrLit})
|
||||
var j = int(hashString(p.config, b[i].strVal) and high(branches))
|
||||
appcg(p.module, branches[j], "if (#eqStrings($1, $2)) goto $3;$n",
|
||||
[rdLoc(e), rdLoc(x), labl])
|
||||
|
||||
proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
|
||||
proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
|
||||
# count how many constant strings there are in the case:
|
||||
var strings = 0
|
||||
for i in 1..<t.len:
|
||||
@@ -876,23 +856,17 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
|
||||
inc(p.labels)
|
||||
if t[i].kind == nkOfBranch:
|
||||
genCaseStringBranch(p, t[i], a, "LA" & rope(p.labels) & "_",
|
||||
stringKind, branches)
|
||||
branches)
|
||||
else:
|
||||
# else statement: nothing to do yet
|
||||
# but we reserved a label, which we use later
|
||||
discard
|
||||
if stringKind == tyCstring:
|
||||
linefmt(p, cpsStmts, "switch (#hashCstring($1) & $2) {$n",
|
||||
[rdLoc(a), bitMask])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n",
|
||||
[rdLoc(a), bitMask])
|
||||
linefmt(p, cpsStmts, "switch (#hashString($1) & $2) {$n",
|
||||
[rdLoc(a), bitMask])
|
||||
for j in 0..high(branches):
|
||||
if branches[j] != "":
|
||||
var lit = newRopeAppender()
|
||||
intLiteral(j, lit)
|
||||
if branches[j] != nil:
|
||||
lineF(p, cpsStmts, "case $1: $n$2break;$n",
|
||||
[lit, branches[j]])
|
||||
[intLiteral(j), branches[j]])
|
||||
lineF(p, cpsStmts, "}$n", []) # else statement:
|
||||
if t[^1].kind != nkOfBranch:
|
||||
lineF(p, cpsStmts, "goto LA$1_;$n", [rope(p.labels)])
|
||||
@@ -900,10 +874,7 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
|
||||
var lend = genCaseSecondPass(p, t, d, labId, t.len-1)
|
||||
fixLabel(p, lend)
|
||||
else:
|
||||
if stringKind == tyCstring:
|
||||
genCaseGeneric(p, t, d, "", "if (#eqCstrings($1, $2)) goto $3;$n")
|
||||
else:
|
||||
genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
|
||||
genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
|
||||
|
||||
proc branchHasTooBigRange(b: PNode): bool =
|
||||
for it in b:
|
||||
@@ -926,22 +897,16 @@ proc genCaseRange(p: BProc, branch: PNode) =
|
||||
for j in 0..<branch.len-1:
|
||||
if branch[j].kind == nkRange:
|
||||
if hasSwitchRange in CC[p.config.cCompiler].props:
|
||||
var litA = newRopeAppender()
|
||||
var litB = newRopeAppender()
|
||||
genLiteral(p, branch[j][0], litA)
|
||||
genLiteral(p, branch[j][1], litB)
|
||||
lineF(p, cpsStmts, "case $1 ... $2:$n", [litA, litB])
|
||||
lineF(p, cpsStmts, "case $1 ... $2:$n", [
|
||||
genLiteral(p, branch[j][0]),
|
||||
genLiteral(p, branch[j][1])])
|
||||
else:
|
||||
var v = copyNode(branch[j][0])
|
||||
while v.intVal <= branch[j][1].intVal:
|
||||
var litA = newRopeAppender()
|
||||
genLiteral(p, v, litA)
|
||||
lineF(p, cpsStmts, "case $1:$n", [litA])
|
||||
lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, v)])
|
||||
inc(v.intVal)
|
||||
else:
|
||||
var litA = newRopeAppender()
|
||||
genLiteral(p, branch[j], litA)
|
||||
lineF(p, cpsStmts, "case $1:$n", [litA])
|
||||
lineF(p, cpsStmts, "case $1:$n", [genLiteral(p, branch[j])])
|
||||
|
||||
proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
|
||||
# analyse 'case' statement:
|
||||
@@ -953,7 +918,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
|
||||
var lend = if splitPoint > 0: genIfForCaseUntil(p, n, d,
|
||||
rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n",
|
||||
eqFormat = "if ($1 == $2) goto $3;$n",
|
||||
splitPoint, a) else: ""
|
||||
splitPoint, a) else: nil
|
||||
|
||||
# generate switch part (might be empty):
|
||||
if splitPoint+1 < n.len:
|
||||
@@ -971,13 +936,10 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
|
||||
hasDefault = true
|
||||
exprBlock(p, branch.lastSon, d)
|
||||
lineF(p, cpsStmts, "break;$n", [])
|
||||
if not hasDefault:
|
||||
if hasBuiltinUnreachable in CC[p.config.cCompiler].props:
|
||||
lineF(p, cpsStmts, "default: __builtin_unreachable();$n", [])
|
||||
elif hasAssume in CC[p.config.cCompiler].props:
|
||||
lineF(p, cpsStmts, "default: __assume(0);$n", [])
|
||||
if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault:
|
||||
lineF(p, cpsStmts, "default: __assume(0);$n", [])
|
||||
lineF(p, cpsStmts, "}$n", [])
|
||||
if lend != "": fixLabel(p, lend)
|
||||
if lend != nil: fixLabel(p, lend)
|
||||
|
||||
proc genCase(p: BProc, t: PNode, d: var TLoc) =
|
||||
genLineDir(p, t)
|
||||
@@ -985,9 +947,7 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) =
|
||||
getTemp(p, t.typ, d)
|
||||
case skipTypes(t[0].typ, abstractVarRange).kind
|
||||
of tyString:
|
||||
genStringCase(p, t, tyString, d)
|
||||
of tyCstring:
|
||||
genStringCase(p, t, tyCstring, d)
|
||||
genStringCase(p, t, d)
|
||||
of tyFloat..tyFloat128:
|
||||
genCaseGeneric(p, t, d, "if ($1 >= $2 && $1 <= $3) goto $4;$n",
|
||||
"if ($1 == $2) goto $3;$n")
|
||||
@@ -1008,7 +968,7 @@ proc genRestoreFrameAfterException(p: BProc) =
|
||||
proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
#[ code to generate:
|
||||
|
||||
std::exception_ptr error;
|
||||
std::exception_ptr error = nullptr;
|
||||
try {
|
||||
body;
|
||||
} catch (Exception e) {
|
||||
@@ -1039,7 +999,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
inc(p.labels, 2)
|
||||
let etmp = p.labels
|
||||
|
||||
lineCg(p, cpsStmts, "std::exception_ptr T$1_;$n", [etmp])
|
||||
p.procSec(cpsInit).add(ropecg(p.module, "\tstd::exception_ptr T$1_ = nullptr;", [etmp]))
|
||||
|
||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||
p.nestedTryStmts.add((fin, false, 0.Natural))
|
||||
@@ -1073,11 +1033,12 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
if hasIf: lineF(p, cpsStmts, "else ", [])
|
||||
startBlock(p)
|
||||
# we handled the error:
|
||||
linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp])
|
||||
expr(p, t[i][0], d)
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||
endBlock(p)
|
||||
else:
|
||||
var orExpr = newRopeAppender()
|
||||
var orExpr = Rope(nil)
|
||||
var exvar = PNode(nil)
|
||||
for j in 0..<t[i].len - 1:
|
||||
var typeNode = t[i][j]
|
||||
@@ -1088,24 +1049,22 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
if isImportedException(typeNode.typ, p.config):
|
||||
hasImportedCppExceptions = true
|
||||
else:
|
||||
if orExpr.len != 0: orExpr.add("||")
|
||||
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
let checkFor = $getObjDepth(typeNode.typ)
|
||||
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(typeNode.typ, p.config)))])
|
||||
if orExpr != nil: orExpr.add("||")
|
||||
let checkFor = if optTinyRtti in p.config.globalOptions:
|
||||
genTypeInfo2Name(p.module, typeNode.typ)
|
||||
else:
|
||||
let checkFor = genTypeInfoV1(p.module, typeNode.typ, typeNode.info)
|
||||
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||
genTypeInfoV1(p.module, typeNode.typ, typeNode.info)
|
||||
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||
|
||||
if orExpr.len != 0:
|
||||
if orExpr != nil:
|
||||
if hasIf:
|
||||
startBlock(p, "else if ($1) {$n", [orExpr])
|
||||
else:
|
||||
startBlock(p, "if ($1) {$n", [orExpr])
|
||||
hasIf = true
|
||||
if exvar != nil:
|
||||
fillLocalName(p, exvar.sym)
|
||||
fillLoc(exvar.sym.loc, locTemp, exvar, OnStack)
|
||||
fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnStack)
|
||||
linefmt(p, cpsStmts, "$1 $2 = T$3_;$n", [getTypeDesc(p.module, exvar.sym.typ),
|
||||
rdLoc(exvar.sym.loc), rope(etmp+1)])
|
||||
# we handled the error:
|
||||
@@ -1135,7 +1094,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
|
||||
if t[i].len == 1:
|
||||
# general except section:
|
||||
startBlock(p, "catch (...) {$n", [])
|
||||
startBlock(p, "catch (...) {", [])
|
||||
genExceptBranchBody(t[i][0])
|
||||
endBlock(p)
|
||||
catchAllPresent = true
|
||||
@@ -1146,8 +1105,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
typeNode = t[i][j][1]
|
||||
if isImportedException(typeNode.typ, p.config):
|
||||
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
|
||||
fillLocalName(p, exvar.sym)
|
||||
fillLoc(exvar.sym.loc, locTemp, exvar, OnStack)
|
||||
fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnStack)
|
||||
startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, typeNode.typ), rdLoc(exvar.sym.loc))
|
||||
genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type
|
||||
endBlock(p)
|
||||
@@ -1161,7 +1119,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||
# general finally block:
|
||||
if t.len > 0 and t[^1].kind == nkFinally:
|
||||
if not catchAllPresent:
|
||||
startBlock(p, "catch (...) {$n", [])
|
||||
startBlock(p, "catch (...) {", [])
|
||||
genRestoreFrameAfterException(p)
|
||||
linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp])
|
||||
endBlock(p)
|
||||
@@ -1200,7 +1158,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
|
||||
if not isEmptyType(t.typ) and d.k == locNone:
|
||||
getTemp(p, t.typ, d)
|
||||
genLineDir(p, t)
|
||||
cgsym(p.module, "popCurrentExceptionEx")
|
||||
discard cgsym(p.module, "popCurrentExceptionEx")
|
||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||
p.nestedTryStmts.add((fin, false, 0.Natural))
|
||||
startBlock(p, "try {$n")
|
||||
@@ -1226,8 +1184,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
|
||||
for j in 0..<t[i].len-1:
|
||||
if t[i][j].isInfixAs():
|
||||
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
|
||||
fillLocalName(p, exvar.sym)
|
||||
fillLoc(exvar.sym.loc, locTemp, exvar, OnUnknown)
|
||||
fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnUnknown)
|
||||
startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc))
|
||||
else:
|
||||
startBlock(p, "catch ($1&) {$n", getTypeDesc(p.module, t[i][j].typ))
|
||||
@@ -1242,7 +1199,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
|
||||
# finally requires catch all presence
|
||||
startBlock(p, "catch (...) {$n")
|
||||
genStmts(p, t[^1][0])
|
||||
line(p, cpsStmts, "throw;\n")
|
||||
line(p, cpsStmts, ~"throw;$n")
|
||||
endBlock(p)
|
||||
|
||||
genSimpleBlock(p, t[^1][0])
|
||||
@@ -1304,18 +1261,16 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
|
||||
linefmt(p, cpsStmts, "*nimErr_ = NIM_FALSE;$n", [])
|
||||
expr(p, t[i][0], d)
|
||||
else:
|
||||
var orExpr = newRopeAppender()
|
||||
var orExpr: Rope = nil
|
||||
for j in 0..<t[i].len - 1:
|
||||
assert(t[i][j].kind == nkType)
|
||||
if orExpr.len != 0: orExpr.add("||")
|
||||
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
let checkFor = $getObjDepth(t[i][j].typ)
|
||||
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)",
|
||||
[memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
|
||||
if orExpr != nil: orExpr.add("||")
|
||||
let checkFor = if optTinyRtti in p.config.globalOptions:
|
||||
genTypeInfo2Name(p.module, t[i][j].typ)
|
||||
else:
|
||||
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
|
||||
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||
genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
|
||||
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||
|
||||
if i > 1: line(p, cpsStmts, "else ")
|
||||
startBlock(p, "if ($1) {$n", [orExpr])
|
||||
@@ -1391,7 +1346,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||
else:
|
||||
p.flags.incl noSafePoints
|
||||
genLineDir(p, t)
|
||||
cgsym(p.module, "Exception")
|
||||
discard cgsym(p.module, "Exception")
|
||||
var safePoint: Rope
|
||||
if not quirkyExceptions:
|
||||
safePoint = getTempName(p.module)
|
||||
@@ -1401,24 +1356,8 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
||||
elif isDefined(p.config, "nimSigSetjmp"):
|
||||
linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", [safePoint])
|
||||
elif isDefined(p.config, "nimBuiltinSetjmp"):
|
||||
linefmt(p, cpsStmts, "$1.status = __builtin_setjmp($1.context);$n", [safePoint])
|
||||
elif isDefined(p.config, "nimRawSetjmp"):
|
||||
if isDefined(p.config, "mswindows"):
|
||||
if isDefined(p.config, "vcc") or isDefined(p.config, "clangcl"):
|
||||
# For the vcc compiler, use `setjmp()` with one argument.
|
||||
# See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setjmp?view=msvc-170
|
||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
||||
else:
|
||||
# The Windows `_setjmp()` takes two arguments, with the second being an
|
||||
# undocumented buffer used by the SEH mechanism for stack unwinding.
|
||||
# Mingw-w64 has been trying to get it right for years, but it's still
|
||||
# prone to stack corruption during unwinding, so we disable that by setting
|
||||
# it to NULL.
|
||||
# More details: https://github.com/status-im/nimbus-eth2/issues/3121
|
||||
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context, 0);$n", [safePoint])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint])
|
||||
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint])
|
||||
else:
|
||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
||||
lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint])
|
||||
@@ -1450,17 +1389,16 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||
linefmt(p, cpsStmts, "#popCurrentException();$n", [])
|
||||
endBlock(p)
|
||||
else:
|
||||
var orExpr = newRopeAppender()
|
||||
var orExpr: Rope = nil
|
||||
for j in 0..<t[i].len - 1:
|
||||
assert(t[i][j].kind == nkType)
|
||||
if orExpr.len != 0: orExpr.add("||")
|
||||
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||
if optTinyRtti in p.config.globalOptions:
|
||||
let checkFor = $getObjDepth(t[i][j].typ)
|
||||
appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
|
||||
if orExpr != nil: orExpr.add("||")
|
||||
let checkFor = if optTinyRtti in p.config.globalOptions:
|
||||
genTypeInfo2Name(p.module, t[i][j].typ)
|
||||
else:
|
||||
let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
|
||||
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||
genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
|
||||
let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
|
||||
appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
|
||||
|
||||
if i > 1: line(p, cpsStmts, "else ")
|
||||
startBlock(p, "if ($1) {$n", [orExpr])
|
||||
@@ -1486,7 +1424,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||
if not quirkyExceptions:
|
||||
linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint])
|
||||
|
||||
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
|
||||
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
|
||||
var res = ""
|
||||
for it in t.sons:
|
||||
case it.kind
|
||||
@@ -1502,8 +1440,13 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
|
||||
res.add($getTypeDesc(p.module, sym.typ))
|
||||
else:
|
||||
discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
|
||||
fillBackendName(p.module, sym)
|
||||
res.add($sym.loc.r)
|
||||
var r = sym.loc.r
|
||||
if r == nil:
|
||||
# if no name has already been given,
|
||||
# it doesn't matter much:
|
||||
r = mangleName(p.module, sym)
|
||||
sym.loc.r = r # but be consequent!
|
||||
res.add($r)
|
||||
of nkTypeOfExpr:
|
||||
res.add($getTypeDesc(p.module, it.typ))
|
||||
else:
|
||||
@@ -1528,13 +1471,12 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
|
||||
result.add("\\n\"\n")
|
||||
else:
|
||||
res.add("\L")
|
||||
result.add res.rope
|
||||
result = res.rope
|
||||
|
||||
proc genAsmStmt(p: BProc, t: PNode) =
|
||||
assert(t.kind == nkAsmStmt)
|
||||
genLineDir(p, t)
|
||||
var s = newRopeAppender()
|
||||
genAsmOrEmitStmt(p, t, isAsmStmt=true, s)
|
||||
var s = genAsmOrEmitStmt(p, t, isAsmStmt=true)
|
||||
# see bug #2362, "top level asm statements" seem to be a mis-feature
|
||||
# but even if we don't do this, the example in #2362 cannot possibly
|
||||
# work:
|
||||
@@ -1542,20 +1484,18 @@ proc genAsmStmt(p: BProc, t: PNode) =
|
||||
# top level asm statement?
|
||||
p.module.s[cfsProcHeaders].add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
|
||||
else:
|
||||
addIndent p, p.s(cpsStmts)
|
||||
p.s(cpsStmts).add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s])
|
||||
p.s(cpsStmts).add indentLine(p, runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s]))
|
||||
|
||||
proc determineSection(n: PNode): TCFileSection =
|
||||
result = cfsProcHeaders
|
||||
if n.len >= 1 and n[0].kind in {nkStrLit..nkTripleStrLit}:
|
||||
let sec = n[0].strVal
|
||||
if sec.startsWith("/*TYPESECTION*/"): result = cfsForwardTypes # TODO WORKAROUND
|
||||
if sec.startsWith("/*TYPESECTION*/"): result = cfsTypes
|
||||
elif sec.startsWith("/*VARSECTION*/"): result = cfsVars
|
||||
elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders
|
||||
|
||||
proc genEmit(p: BProc, t: PNode) =
|
||||
var s = newRopeAppender()
|
||||
genAsmOrEmitStmt(p, t[1], false, s)
|
||||
var s = genAsmOrEmitStmt(p, t[1])
|
||||
if p.prc == nil:
|
||||
# top level emit pragma?
|
||||
let section = determineSection(t[1])
|
||||
@@ -1566,14 +1506,9 @@ proc genEmit(p: BProc, t: PNode) =
|
||||
line(p, cpsStmts, s)
|
||||
|
||||
proc genPragma(p: BProc, n: PNode) =
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
for it in n.sons:
|
||||
case whichPragma(it)
|
||||
of wEmit: genEmit(p, it)
|
||||
of wPush:
|
||||
processPushBackendOption(p.optionsStack, p.options, n, i+1)
|
||||
of wPop:
|
||||
processPopBackendOption(p.optionsStack, p.options)
|
||||
else: discard
|
||||
|
||||
|
||||
@@ -1585,14 +1520,10 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
|
||||
if not containsOrIncl(p.module.declaredThings, field.id):
|
||||
appcg(p.module, cfsVars, "extern $1",
|
||||
[discriminatorTableDecl(p.module, t, field)])
|
||||
var lit = newRopeAppender()
|
||||
intLiteral(toInt64(lengthOrd(p.config, field.typ))+1, lit)
|
||||
lineCg(p, cpsStmts,
|
||||
"#FieldDiscriminantCheck((NI)(NU)($1), (NI)(NU)($2), $3, $4);$n",
|
||||
[rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),
|
||||
lit])
|
||||
if p.config.exc == excGoto:
|
||||
raiseExit(p)
|
||||
intLiteral(toInt64(lengthOrd(p.config, field.typ))+1)])
|
||||
|
||||
when false:
|
||||
proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =
|
||||
@@ -1617,7 +1548,7 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) =
|
||||
initLocExpr(p, e[0], a)
|
||||
getTemp(p, a.t, tmp)
|
||||
expr(p, e[1], tmp)
|
||||
if p.inUncheckedAssignSection == 0:
|
||||
if optTinyRtti notin p.config.globalOptions:
|
||||
let field = dotExpr[1].sym
|
||||
genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field)
|
||||
message(p.config, e.info, warnCaseTransition)
|
||||
@@ -1634,11 +1565,10 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
|
||||
let le = e[0]
|
||||
let ri = e[1]
|
||||
var a: TLoc
|
||||
discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), dkVar)
|
||||
discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), skVar)
|
||||
initLoc(a, locNone, le, OnUnknown)
|
||||
a.flags.incl(lfEnforceDeref)
|
||||
a.flags.incl(lfPrepareForMutation)
|
||||
genLineDir(p, le) # it can be a nkBracketExpr, which may raise
|
||||
expr(p, le, a)
|
||||
a.flags.excl(lfPrepareForMutation)
|
||||
if fastAsgn: incl(a.flags, lfNoDeepCopy)
|
||||
|
||||
@@ -44,13 +44,13 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
|
||||
m.s[cfsVars].addf(" $1;$n", [s.loc.r])
|
||||
|
||||
proc generateThreadLocalStorage(m: BModule) =
|
||||
if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
|
||||
if m.g.nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
|
||||
for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t)
|
||||
finishTypeDescriptions(m)
|
||||
m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv])
|
||||
|
||||
proc generateThreadVarsSize(m: BModule) =
|
||||
if m.g.nimtv != "":
|
||||
if m.g.nimtv != nil:
|
||||
let externc = if m.config.backend == backendCpp or
|
||||
sfCompileToCpp in m.module.flags: "extern \"C\" "
|
||||
else: ""
|
||||
|
||||
@@ -34,7 +34,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||
if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
|
||||
var p = c.p
|
||||
let disc = n[0].sym
|
||||
if disc.loc.r == "": fillObjectFields(c.p.module, typ)
|
||||
if disc.loc.r == nil: 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])
|
||||
@@ -51,7 +51,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
|
||||
of nkSym:
|
||||
let field = n.sym
|
||||
if field.typ.kind == tyVoid: return
|
||||
if field.loc.r == "": fillObjectFields(c.p.module, typ)
|
||||
if field.loc.r == nil: fillObjectFields(c.p.module, typ)
|
||||
if field.loc.t == nil:
|
||||
internalError(c.p.config, n.info, "genTraverseProc()")
|
||||
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
|
||||
@@ -76,8 +76,7 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
let arraySize = lengthOrd(c.p.config, typ[0])
|
||||
var i: TLoc
|
||||
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
|
||||
var oldCode = p.s(cpsStmts)
|
||||
freeze oldCode
|
||||
let oldCode = p.s(cpsStmts)
|
||||
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
|
||||
[i.r, arraySize])
|
||||
let oldLen = p.s(cpsStmts).len
|
||||
@@ -121,8 +120,7 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
|
||||
assert typ.kind == tySequence
|
||||
var i: TLoc
|
||||
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
|
||||
var oldCode = p.s(cpsStmts)
|
||||
freeze oldCode
|
||||
let oldCode = p.s(cpsStmts)
|
||||
var a: TLoc
|
||||
a.r = accessor
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,11 +13,6 @@ import
|
||||
ast, types, hashes, strutils, msgs, wordrecg,
|
||||
platform, trees, options, cgendata
|
||||
|
||||
import std/[hashes, strutils, formatfloat]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
|
||||
case n.kind
|
||||
of nkStmtList:
|
||||
@@ -55,7 +50,7 @@ proc hashString*(conf: ConfigRef; s: string): BiggestInt =
|
||||
a = a + (a shl 3)
|
||||
a = a xor (a shr 11)
|
||||
a = a + (a shl 15)
|
||||
result = cast[Hash](uint(a))
|
||||
result = cast[Hash](a)
|
||||
|
||||
template getUniqueType*(key: PType): PType = key
|
||||
|
||||
@@ -124,19 +119,13 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
|
||||
var pt = skipTypes(s.typ, typedescInst)
|
||||
assert skResult != s.kind
|
||||
|
||||
#note precedence: params override types
|
||||
if optByRef in s.options: return true
|
||||
elif sfByCopy in s.flags: return false
|
||||
elif tfByRef in pt.flags: return true
|
||||
if tfByRef in pt.flags: return true
|
||||
elif tfByCopy in pt.flags: return false
|
||||
case pt.kind
|
||||
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 s.typ.kind == tySink and conf.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
|
||||
# bug #23354:
|
||||
result = false
|
||||
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):
|
||||
@@ -153,64 +142,3 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
|
||||
result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or
|
||||
pt.kind == tySet and mapSetType(conf, pt) == ctArray)
|
||||
|
||||
proc encodeName*(name: string): string =
|
||||
result = mangle(name)
|
||||
result = $result.len & result
|
||||
|
||||
proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
|
||||
result = if name == "": s.name.s else: name
|
||||
result.add "__"
|
||||
result.add m.g.graph.ifaces[s.itemId.module].uniqueName
|
||||
result.add "_u"
|
||||
result.add $s.itemId.item
|
||||
|
||||
proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
|
||||
#Module::Type
|
||||
var name = s.name.s
|
||||
if makeUnique:
|
||||
name = makeUnique(m, s, name)
|
||||
"N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
|
||||
|
||||
proc elementType*(n: PType): PType {.inline.} = n.sons[^1]
|
||||
|
||||
proc encodeType*(m: BModule; t: PType): string =
|
||||
result = ""
|
||||
var kindName = ($t.kind)[2..^1]
|
||||
kindName[0] = toLower($kindName[0])[0]
|
||||
case t.kind
|
||||
of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam:
|
||||
result = encodeSym(m, t.sym)
|
||||
of tyGenericInst, tyUserTypeClassInst, tyGenericBody:
|
||||
result = encodeName(t[0].sym.name.s)
|
||||
result.add "I"
|
||||
for i in 1..<t.len - 1:
|
||||
result.add encodeType(m, t[i])
|
||||
result.add "E"
|
||||
of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
|
||||
tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
|
||||
result =
|
||||
case t.kind:
|
||||
of tySequence: encodeName("seq")
|
||||
else: encodeName(kindName)
|
||||
result.add "I"
|
||||
for i in 0..<t.len:
|
||||
let s = t[i]
|
||||
if s.isNil: continue
|
||||
result.add encodeType(m, s)
|
||||
result.add "E"
|
||||
of tyRange:
|
||||
var val = "range_"
|
||||
if t.n[0].typ.kind in {tyFloat..tyFloat128}:
|
||||
val.addFloat t.n[0].floatVal
|
||||
val.add "_"
|
||||
val.addFloat t.n[1].floatVal
|
||||
else:
|
||||
val.add $t.n[0].intVal & "_" & $t.n[1].intVal
|
||||
result = encodeName(val)
|
||||
of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
|
||||
result = encodeName(kindName)
|
||||
of tyAlias, tyInferred, tyOwned:
|
||||
result = encodeType(m, t.elementType)
|
||||
else:
|
||||
assert false, "encodeType " & $t.kind
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ import
|
||||
type
|
||||
TLabel* = Rope # for the C generator a label is just a rope
|
||||
TCFileSection* = enum # the sections a generated C file consists of
|
||||
cfsMergeInfo, # section containing merge information
|
||||
cfsHeaders, # section for C include file headers
|
||||
cfsFrameDefines # section for nim frame macros
|
||||
cfsForwardTypes, # section for C forward typedefs
|
||||
@@ -23,17 +24,21 @@ type
|
||||
cfsSeqTypes, # section for sequence types only
|
||||
# this is needed for strange type generation
|
||||
# reasons
|
||||
cfsFieldInfo, # section for field information
|
||||
cfsTypeInfo, # section for type information (ag ABI checks)
|
||||
cfsProcHeaders, # section for C procs prototypes
|
||||
cfsStrData, # section for constant string literals
|
||||
cfsData, # section for C constant data
|
||||
cfsVars, # section for C variable declarations
|
||||
cfsProcs, # section for C procs that are not inline
|
||||
cfsInitProc, # section for the C init proc
|
||||
cfsDatInitProc, # section for the C datInit proc
|
||||
cfsTypeInit1, # section 1 for declarations of type information
|
||||
cfsTypeInit2, # section 2 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
|
||||
cfsDynLibDeinit # section for deinitialization of dynamic
|
||||
# libraries
|
||||
TCTypeKind* = enum # describes the type kind of a C type
|
||||
ctVoid, ctChar, ctBool,
|
||||
ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
|
||||
@@ -86,7 +91,6 @@ type
|
||||
options*: TOptions # options that should be used for code
|
||||
# generation; this is the same as prc.options
|
||||
# unless prc == nil
|
||||
optionsStack*: seq[TOptions]
|
||||
module*: BModule # used to prevent excessive parameter passing
|
||||
withinLoop*: int # > 0 if we are within a loop
|
||||
splitDecls*: int # > 0 if we are in some context for C++ that
|
||||
@@ -95,7 +99,6 @@ type
|
||||
withinTryWithExcept*: int # required for goto based exception handling
|
||||
withinBlockLeaveActions*: int # complex to explain
|
||||
sigConflicts*: CountTable[string]
|
||||
inUncheckedAssignSection*: int
|
||||
|
||||
TTypeSeq* = seq[PType]
|
||||
TypeCache* = Table[SigHash, Rope]
|
||||
@@ -135,7 +138,6 @@ type
|
||||
# unconditionally...
|
||||
# nimtvDeps is VERY hard to cache because it's
|
||||
# not a list of IDs nor can it be made to be one.
|
||||
mangledPrcs*: HashSet[string]
|
||||
|
||||
TCGen = object of PPassContext # represents a C source file
|
||||
s*: TCFileSections # sections of the C file
|
||||
@@ -149,7 +151,7 @@ type
|
||||
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 occurrence in code
|
||||
# 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
|
||||
declaredProtos*: IntSet # prototypes we have declared in this .c file
|
||||
@@ -174,7 +176,6 @@ type
|
||||
|
||||
template config*(m: BModule): ConfigRef = m.g.config
|
||||
template config*(p: BProc): ConfigRef = p.module.g.config
|
||||
template vccAndC*(p: BProc): bool = p.module.config.cCompiler == ccVcc and p.module.config.backend == backendC
|
||||
|
||||
proc includeHeader*(this: BModule; header: string) =
|
||||
if not this.headerFiles.contains header:
|
||||
@@ -188,23 +189,16 @@ proc procSec*(p: BProc, s: TCProcSection): var Rope {.inline.} =
|
||||
# top level proc sections
|
||||
result = p.blocks[0].sections[s]
|
||||
|
||||
proc initBlock*(): TBlock =
|
||||
result = TBlock()
|
||||
for i in low(result.sections)..high(result.sections):
|
||||
result.sections[i] = newRopeAppender()
|
||||
|
||||
proc newProc*(prc: PSym, module: BModule): BProc =
|
||||
result = BProc(
|
||||
prc: prc,
|
||||
module: module,
|
||||
optionsStack: if module.initProc != nil: module.initProc.optionsStack
|
||||
else: @[],
|
||||
options: if prc != nil: prc.options
|
||||
else: module.config.options,
|
||||
blocks: @[initBlock()],
|
||||
sigConflicts: initCountTable[string]())
|
||||
if optQuirky in result.options:
|
||||
result.flags = {nimErrorFlagDisabled}
|
||||
new(result)
|
||||
result.prc = prc
|
||||
result.module = module
|
||||
result.options = if prc != nil: prc.options
|
||||
else: module.config.options
|
||||
newSeq(result.blocks, 1)
|
||||
result.nestedTryStmts = @[]
|
||||
result.finallySafePoints = @[]
|
||||
result.sigConflicts = initCountTable[string]()
|
||||
|
||||
proc newModuleList*(g: ModuleGraph): BModuleList =
|
||||
BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](),
|
||||
|
||||
@@ -11,11 +11,7 @@
|
||||
|
||||
import
|
||||
intsets, options, ast, msgs, idents, renderer, types, magicsys,
|
||||
sempass2, modulegraphs, lineinfos
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
sempass2, strutils, modulegraphs, lineinfos
|
||||
|
||||
proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
|
||||
var dest = skipTypes(d, abstractPtrs)
|
||||
@@ -50,7 +46,6 @@ proc methodCall*(n: PNode; conf: ConfigRef): PNode =
|
||||
# replace ordinary method by dispatcher method:
|
||||
let disp = getDispatcher(result[0].sym)
|
||||
if disp != nil:
|
||||
result[0].typ = disp.typ
|
||||
result[0].sym = disp
|
||||
# change the arguments to up/downcasts to fit the dispatcher's parameters:
|
||||
for i in 1..<result.len:
|
||||
@@ -95,8 +90,7 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
|
||||
return No
|
||||
if result == Yes:
|
||||
# check for return type:
|
||||
# ignore flags of return types; # bug #22673
|
||||
if not sameTypeOrNil(a.typ[0], b.typ[0], {IgnoreFlags}):
|
||||
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]
|
||||
@@ -114,7 +108,7 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
|
||||
s.ast[dispatcherPos] = dispatcher
|
||||
|
||||
proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
var disp = copySym(s, idgen)
|
||||
var disp = copySym(s, nextSymId(idgen))
|
||||
incl(disp.flags, sfDispatcher)
|
||||
excl(disp.flags, sfExported)
|
||||
let old = disp.typ
|
||||
@@ -125,10 +119,10 @@ proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
|
||||
disp.ast = copyTree(s.ast)
|
||||
disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
|
||||
disp.loc.r = ""
|
||||
disp.loc.r = nil
|
||||
if s.typ[0] != nil:
|
||||
if disp.ast.len > resultPos:
|
||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
|
||||
disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen))
|
||||
else:
|
||||
# We've encountered a method prototype without a filled-in
|
||||
# resultPos slot. We put a placeholder in there that will
|
||||
@@ -149,6 +143,23 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
|
||||
disp.ast[resultPos].kind == nkEmpty:
|
||||
disp.ast[resultPos] = copyTree(meth.ast[resultPos])
|
||||
|
||||
# The following code works only with lock levels, so we disable
|
||||
# it when they're not available.
|
||||
when declared(TLockLevel):
|
||||
proc `<`(a, b: TLockLevel): bool {.borrow.}
|
||||
proc `==`(a, b: TLockLevel): bool {.borrow.}
|
||||
if disp.typ.lockLevel == UnspecifiedLockLevel:
|
||||
disp.typ.lockLevel = meth.typ.lockLevel
|
||||
elif meth.typ.lockLevel != UnspecifiedLockLevel and
|
||||
meth.typ.lockLevel != disp.typ.lockLevel:
|
||||
message(conf, meth.info, warnLockLevel,
|
||||
"method has lock level $1, but another method has $2" %
|
||||
[$meth.typ.lockLevel, $disp.typ.lockLevel])
|
||||
# XXX The following code silences a duplicate warning in
|
||||
# checkMethodeffects() in sempass2.nim for now.
|
||||
if disp.typ.lockLevel < meth.typ.lockLevel:
|
||||
disp.typ.lockLevel = meth.typ.lockLevel
|
||||
|
||||
proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
|
||||
var witness: PSym
|
||||
for i in 0..<g.methods.len:
|
||||
@@ -214,7 +225,7 @@ proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
|
||||
a[j] = v
|
||||
if h == 1: break
|
||||
|
||||
proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
|
||||
proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PSym =
|
||||
var base = methods[0].ast[dispatcherPos].sym
|
||||
result = base
|
||||
var paramLen = base.typ.len
|
||||
@@ -273,7 +284,7 @@ proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idg
|
||||
nilchecks.flags.incl nfTransf # should not be further transformed
|
||||
result.ast[bodyPos] = nilchecks
|
||||
|
||||
proc generateMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator): PNode =
|
||||
proc generateMethodDispatchers*(g: ModuleGraph): PNode =
|
||||
result = newNode(nkStmtList)
|
||||
for bucket in 0..<g.methods.len:
|
||||
var relevantCols = initIntSet()
|
||||
@@ -283,4 +294,4 @@ proc generateMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator): PNode =
|
||||
# if multi-methods are not enabled, we are interested only in the first field
|
||||
break
|
||||
sortBucket(g.methods[bucket].methods, relevantCols)
|
||||
result.add newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols, idgen))
|
||||
result.add newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols))
|
||||
|
||||
@@ -121,10 +121,7 @@
|
||||
# yield 2
|
||||
# if :unrollFinally: # This node is created by `newEndFinallyNode`
|
||||
# if :curExc.isNil:
|
||||
# if nearestFinally == 0:
|
||||
# return :tmpResult
|
||||
# else:
|
||||
# :state = nearestFinally # bubble up
|
||||
# return :tmpResult
|
||||
# else:
|
||||
# closureIterSetupExc(nil)
|
||||
# raise
|
||||
@@ -136,9 +133,6 @@ import
|
||||
renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos,
|
||||
tables, options
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
Ctx = object
|
||||
g: ModuleGraph
|
||||
@@ -163,7 +157,7 @@ type
|
||||
|
||||
const
|
||||
nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
|
||||
nkCommentStmt, nkMixinStmt, nkBindStmt, nkTypeOfExpr} + procDefs
|
||||
nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
|
||||
|
||||
proc newStateAccess(ctx: var Ctx): PNode =
|
||||
if ctx.stateVarSym.isNil:
|
||||
@@ -183,7 +177,7 @@ proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode =
|
||||
ctx.newStateAssgn(newIntTypeNode(stateNo, ctx.g.getSysType(TLineInfo(), tyInt)))
|
||||
|
||||
proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
|
||||
result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.idgen, ctx.fn, ctx.fn.info)
|
||||
result = newSym(skVar, getIdent(ctx.g.cache, name), nextSymId(ctx.idgen), ctx.fn, ctx.fn.info)
|
||||
result.typ = typ
|
||||
assert(not typ.isNil)
|
||||
|
||||
@@ -718,7 +712,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
|
||||
n[^1] = ex
|
||||
result.add(n)
|
||||
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
of nkAsgn, nkFastAsgn:
|
||||
var ns = false
|
||||
for i in 0..<n.len:
|
||||
n[i] = ctx.lowerStmtListExprs(n[i], ns)
|
||||
@@ -810,10 +804,7 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
|
||||
# Generate the following code:
|
||||
# if :unrollFinally:
|
||||
# if :curExc.isNil:
|
||||
# if nearestFinally == 0:
|
||||
# return :tmpResult
|
||||
# else:
|
||||
# :state = nearestFinally # bubble up
|
||||
# return :tmpResult
|
||||
# else:
|
||||
# raise
|
||||
let curExc = ctx.newCurExcAccess()
|
||||
@@ -822,20 +813,11 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
|
||||
let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode)
|
||||
cmp.typ = ctx.g.getSysType(info, tyBool)
|
||||
|
||||
let retStmt =
|
||||
if ctx.nearestFinally == 0:
|
||||
# last finally, we can return
|
||||
let retValue = if ctx.fn.typ[0].isNil:
|
||||
ctx.g.emptyNode
|
||||
else:
|
||||
newTree(nkFastAsgn,
|
||||
newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen), info),
|
||||
ctx.newTmpResultAccess())
|
||||
newTree(nkReturnStmt, retValue)
|
||||
else:
|
||||
# bubble up to next finally
|
||||
newTree(nkGotoState, ctx.g.newIntLit(info, ctx.nearestFinally))
|
||||
let asgn = newTree(nkFastAsgn,
|
||||
newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen), info),
|
||||
ctx.newTmpResultAccess())
|
||||
|
||||
let retStmt = newTree(nkReturnStmt, asgn)
|
||||
let branch = newTree(nkElifBranch, cmp, retStmt)
|
||||
|
||||
let nullifyExc = newTree(nkCall, newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")), nilnode)
|
||||
@@ -855,9 +837,7 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
|
||||
case n.kind
|
||||
of nkReturnStmt:
|
||||
# We're somewhere in try, transform to finally unrolling
|
||||
if ctx.nearestFinally == 0:
|
||||
# return is within the finally
|
||||
return
|
||||
assert(ctx.nearestFinally != 0)
|
||||
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
@@ -870,7 +850,7 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
|
||||
if n[0].kind != nkEmpty:
|
||||
let asgnTmpResult = newNodeI(nkAsgn, n.info)
|
||||
asgnTmpResult.add(ctx.newTmpResultAccess())
|
||||
let x = if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: n[0][1] else: n[0]
|
||||
let x = if n[0].kind in {nkAsgn, nkFastAsgn}: n[0][1] else: n[0]
|
||||
asgnTmpResult.add(x)
|
||||
result.add(asgnTmpResult)
|
||||
|
||||
@@ -881,13 +861,6 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
|
||||
|
||||
of nkSkip:
|
||||
discard
|
||||
of nkTryStmt:
|
||||
if n.hasYields:
|
||||
# the inner try will handle these transformations
|
||||
discard
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
n[i] = ctx.transformReturnsInTry(n[i])
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
n[i] = ctx.transformReturnsInTry(n[i])
|
||||
@@ -1157,7 +1130,7 @@ proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: P
|
||||
result = newType(tyArray, nextTypeId(idgen), owner)
|
||||
|
||||
let rng = newType(tyRange, nextTypeId(idgen), owner)
|
||||
rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n - 1))
|
||||
rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n))
|
||||
rng.rawAddSon(t)
|
||||
|
||||
result.rawAddSon(rng)
|
||||
@@ -1350,7 +1323,7 @@ proc freshVars(n: PNode; c: var FreshVarsContext): PNode =
|
||||
let idefs = copyNode(it)
|
||||
for v in 0..it.len-3:
|
||||
if it[v].kind == nkSym:
|
||||
let x = copySym(it[v].sym, c.idgen)
|
||||
let x = copySym(it[v].sym, nextSymId(c.idgen))
|
||||
c.tab[it[v].sym.id] = x
|
||||
idefs.add newSymNode(x)
|
||||
else:
|
||||
@@ -1374,24 +1347,20 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode =
|
||||
# detect: 'finally: raises X' which is currently not supported. We produce
|
||||
# an error for this case for now. All this will be done properly with Yuriy's
|
||||
# patch.
|
||||
|
||||
result = n
|
||||
case n.kind
|
||||
of nkTryStmt:
|
||||
let f = n.lastSon
|
||||
var didAddSomething = false
|
||||
if f.kind == nkFinally:
|
||||
c.finallys.add f.lastSon
|
||||
didAddSomething = true
|
||||
|
||||
for i in 0 ..< n.len:
|
||||
result[i] = preprocess(c, n[i])
|
||||
|
||||
if didAddSomething:
|
||||
if f.kind == nkFinally:
|
||||
discard c.finallys.pop()
|
||||
|
||||
of nkWhileStmt, nkBlockStmt:
|
||||
if not n.hasYields: return n
|
||||
c.blocks.add((n, c.finallys.len))
|
||||
for i in 0 ..< n.len:
|
||||
result[i] = preprocess(c, n[i])
|
||||
@@ -1415,7 +1384,7 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
for i in countdown(c.finallys.high, fin):
|
||||
var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info, idgen: c.idgen)
|
||||
result.add freshVars(copyTree(c.finallys[i]), vars)
|
||||
result.add freshVars(preprocess(c, c.finallys[i]), vars)
|
||||
c.idgen = vars.idgen
|
||||
result.add n
|
||||
of nkSkip: discard
|
||||
@@ -1433,9 +1402,9 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
|
||||
# Lambda lifting was not done yet. Use temporary :state sym, which will
|
||||
# be handled specially by lambda lifting. Local temp vars (if needed)
|
||||
# should follow the same logic.
|
||||
ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), idgen, fn, fn.info)
|
||||
ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), nextSymId(idgen), fn, fn.info)
|
||||
ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
|
||||
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), idgen, fn, fn.info)
|
||||
ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), nextSymId(idgen), fn, fn.info)
|
||||
var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen)
|
||||
var n = preprocess(pc, n.toStmtList)
|
||||
#echo "transformed into ", n
|
||||
@@ -1468,10 +1437,9 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
|
||||
result = ctx.transformStateAssignments(result)
|
||||
result = ctx.wrapIntoStateLoop(result)
|
||||
|
||||
when false:
|
||||
echo "TRANSFORM TO STATES: "
|
||||
echo renderTree(result)
|
||||
# echo "TRANSFORM TO STATES: "
|
||||
# echo renderTree(result)
|
||||
|
||||
echo "exception table:"
|
||||
for i, e in ctx.exceptionTable:
|
||||
echo i, " -> ", e
|
||||
# echo "exception table:"
|
||||
# for i, e in ctx.exceptionTable:
|
||||
# echo i, " -> ", e
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest
|
||||
## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest, nimfix
|
||||
|
||||
import
|
||||
options, idents, nimconf, extccomp, commands, msgs,
|
||||
@@ -55,11 +55,6 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi
|
||||
if conf.cmd == cmdNimscript:
|
||||
incl(conf.globalOptions, optWasNimscript)
|
||||
loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
|
||||
# restores `conf.notes` after loading config files
|
||||
# because it has overwrites the notes when compiling the system module which
|
||||
# is a foreign module compared to the project
|
||||
if conf.cmd in cmdBackends:
|
||||
conf.notes = conf.mainPackageNotes
|
||||
|
||||
if not self.suggestMode:
|
||||
let scriptFile = conf.projectFull.changeFileExt("nims")
|
||||
@@ -69,8 +64,7 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi
|
||||
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)
|
||||
extccomp.initVars(conf)
|
||||
self.processCmdLine(passCmd2, "", conf)
|
||||
if conf.cmd == cmdNone:
|
||||
rawMessage(conf, errGenerated, "command missing")
|
||||
|
||||
@@ -31,9 +31,6 @@ import
|
||||
|
||||
from ast import setUseIc, eqTypeFlags, tfGcSafe, tfNoSideEffect
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
# but some have deps to imported modules. Yay.
|
||||
bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc")
|
||||
bootSwitch(usedFFI, hasFFI, "-d:nimHasLibFFI")
|
||||
@@ -116,7 +113,7 @@ const
|
||||
errInvalidCmdLineOption = "invalid command line option: '$1'"
|
||||
errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found"
|
||||
errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found"
|
||||
errOffHintsError = "'off', 'hint', 'error' or 'usages' expected, but '$1' found"
|
||||
errOffHintsError = "'off', 'hint' or 'error' expected, but '$1' found"
|
||||
|
||||
proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) =
|
||||
if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-")
|
||||
@@ -205,11 +202,7 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
|
||||
# unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
|
||||
let x = findStr(noteMin, noteMax, id, errUnknown)
|
||||
if x != errUnknown: notes = {TNoteKind(x)}
|
||||
else:
|
||||
if isSomeHint:
|
||||
message(conf, info, hintUnknownHint, id)
|
||||
else:
|
||||
localError(conf, info, "unknown $#: $#" % [name, id])
|
||||
else: localError(conf, info, "unknown $#: $#" % [name, id])
|
||||
case id.normalize
|
||||
of "all": # other note groups would be easy to support via additional cases
|
||||
notes = if isSomeHint: {hintMin..hintMax} else: {warnMin..warnMax}
|
||||
@@ -242,10 +235,10 @@ proc processCompile(conf: ConfigRef; filename: string) =
|
||||
extccomp.addExternalFileToCompile(conf, found)
|
||||
|
||||
const
|
||||
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'atomicArc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
|
||||
errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
|
||||
errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
|
||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
|
||||
errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
|
||||
errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
|
||||
errInvalidExceptionSystem = "'goto', 'setjump', 'cpp' or 'quirky' expected, but '$1' found"
|
||||
|
||||
template warningOptionNoop(switch: string) =
|
||||
warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
|
||||
@@ -255,7 +248,7 @@ template deprecatedAlias(oldName, newName: string) =
|
||||
|
||||
proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool =
|
||||
case switch.normalize
|
||||
of "gc", "mm":
|
||||
of "gc":
|
||||
case arg.normalize
|
||||
of "boehm": result = conf.selectedGC == gcBoehm
|
||||
of "refc": result = conf.selectedGC == gcRefc
|
||||
@@ -266,7 +259,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
|
||||
of "go": result = conf.selectedGC == gcGo
|
||||
of "none": result = conf.selectedGC == gcNone
|
||||
of "stack", "regions": result = conf.selectedGC == gcRegions
|
||||
of "atomicarc": result = conf.selectedGC == gcAtomicArc
|
||||
of "v2", "generational": warningOptionNoop(arg)
|
||||
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
||||
of "opt":
|
||||
case arg.normalize
|
||||
@@ -331,7 +324,6 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
of "run", "r": result = contains(conf.globalOptions, optRun)
|
||||
of "symbolfiles": result = conf.symbolFiles != disabledSf
|
||||
of "genscript": result = contains(conf.globalOptions, optGenScript)
|
||||
of "gencdeps": result = contains(conf.globalOptions, optGenCDeps)
|
||||
of "threads": result = contains(conf.globalOptions, optThreads)
|
||||
of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
|
||||
of "implicitstatic": result = contains(conf.options, optImplicitStatic)
|
||||
@@ -340,8 +332,6 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
|
||||
result = contains(conf.options, optTrMacros)
|
||||
of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
|
||||
of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch)
|
||||
of "panics": result = contains(conf.globalOptions, optPanics)
|
||||
of "jsbigint64": result = contains(conf.globalOptions, optJsBigInt64)
|
||||
else: invalidCmdLineOption(conf, passCmd1, switch, info)
|
||||
|
||||
proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
|
||||
@@ -456,8 +446,6 @@ proc parseCommand*(command: string): Command =
|
||||
of "doc2", "doc": cmdDoc
|
||||
of "doc2tex": cmdDoc2tex
|
||||
of "rst2html": cmdRst2html
|
||||
of "md2tex": cmdMd2tex
|
||||
of "md2html": cmdMd2html
|
||||
of "rst2tex": cmdRst2tex
|
||||
of "jsondoc0": cmdJsondoc0
|
||||
of "jsondoc2", "jsondoc": cmdJsondoc
|
||||
@@ -489,8 +477,7 @@ proc setCommandEarly*(conf: ConfigRef, command: string) =
|
||||
# command early customizations
|
||||
# must be handled here to honor subsequent `--hint:x:on|off`
|
||||
case conf.cmd
|
||||
of cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex:
|
||||
# xxx see whether to add others: cmdGendepend, etc.
|
||||
of cmdRst2html, cmdRst2tex: # xxx see whether to add others: cmdGendepend, etc.
|
||||
conf.foreignPackageNotes = {hintSuccessX}
|
||||
else:
|
||||
conf.foreignPackageNotes = foreignPackageNotesDefault
|
||||
@@ -510,89 +497,6 @@ proc specialDefine(conf: ConfigRef, key: string; pass: TCmdLinePass) =
|
||||
optOverflowCheck, optAssert, optStackTrace, optLineTrace, optLineDir}
|
||||
conf.globalOptions.excl {optCDebug}
|
||||
|
||||
proc initOrcDefines*(conf: ConfigRef) =
|
||||
conf.selectedGC = gcOrc
|
||||
defineSymbol(conf.symbols, "gcorc")
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
incl conf.globalOptions, optSeqDestructors
|
||||
incl conf.globalOptions, optTinyRtti
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
defineSymbol(conf.symbols, "nimV2")
|
||||
if conf.exc == excNone and conf.backend != backendCpp:
|
||||
conf.exc = excGoto
|
||||
|
||||
proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef) =
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
incl conf.globalOptions, optSeqDestructors
|
||||
incl conf.globalOptions, optTinyRtti
|
||||
if pass in {passCmd2, passPP}:
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
defineSymbol(conf.symbols, "nimV2")
|
||||
if conf.exc == excNone and conf.backend != backendCpp:
|
||||
conf.exc = excGoto
|
||||
|
||||
proc unregisterArcOrc*(conf: ConfigRef) =
|
||||
undefSymbol(conf.symbols, "gcdestructors")
|
||||
undefSymbol(conf.symbols, "gcarc")
|
||||
undefSymbol(conf.symbols, "gcorc")
|
||||
undefSymbol(conf.symbols, "gcatomicarc")
|
||||
undefSymbol(conf.symbols, "nimSeqsV2")
|
||||
undefSymbol(conf.symbols, "nimV2")
|
||||
excl conf.globalOptions, optSeqDestructors
|
||||
excl conf.globalOptions, optTinyRtti
|
||||
|
||||
proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass,
|
||||
info: TLineInfo; conf: ConfigRef) =
|
||||
if conf.backend == backendJs: return # for: bug #16033
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
case arg.normalize
|
||||
of "boehm":
|
||||
unregisterArcOrc(conf)
|
||||
conf.selectedGC = gcBoehm
|
||||
defineSymbol(conf.symbols, "boehmgc")
|
||||
incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS
|
||||
of "refc":
|
||||
unregisterArcOrc(conf)
|
||||
defineSymbol(conf.symbols, "gcrefc")
|
||||
conf.selectedGC = gcRefc
|
||||
of "markandsweep":
|
||||
unregisterArcOrc(conf)
|
||||
conf.selectedGC = gcMarkAndSweep
|
||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||
of "destructors", "arc":
|
||||
conf.selectedGC = gcArc
|
||||
defineSymbol(conf.symbols, "gcarc")
|
||||
registerArcOrc(pass, conf)
|
||||
of "orc":
|
||||
conf.selectedGC = gcOrc
|
||||
defineSymbol(conf.symbols, "gcorc")
|
||||
registerArcOrc(pass, conf)
|
||||
of "atomicarc":
|
||||
conf.selectedGC = gcAtomicArc
|
||||
defineSymbol(conf.symbols, "gcatomicarc")
|
||||
registerArcOrc(pass, conf)
|
||||
of "hooks":
|
||||
conf.selectedGC = gcHooks
|
||||
defineSymbol(conf.symbols, "gchooks")
|
||||
incl conf.globalOptions, optSeqDestructors
|
||||
processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
of "go":
|
||||
unregisterArcOrc(conf)
|
||||
conf.selectedGC = gcGo
|
||||
defineSymbol(conf.symbols, "gogc")
|
||||
of "none":
|
||||
unregisterArcOrc(conf)
|
||||
conf.selectedGC = gcNone
|
||||
defineSymbol(conf.symbols, "nogc")
|
||||
of "stack", "regions":
|
||||
unregisterArcOrc(conf)
|
||||
conf.selectedGC = gcRegions
|
||||
defineSymbol(conf.symbols, "gcregions")
|
||||
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
||||
|
||||
proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
conf: ConfigRef) =
|
||||
var
|
||||
@@ -618,8 +522,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
var path = processPath(conf, arg, info, notRelativeToProj=true)
|
||||
let nimbleDir = AbsoluteDir getEnv("NIMBLE_DIR")
|
||||
if not nimbleDir.isEmpty and pass == passPP:
|
||||
path = nimbleDir / RelativeDir"pkgs2"
|
||||
nimblePath(conf, path, info)
|
||||
path = nimbleDir / RelativeDir"pkgs"
|
||||
nimblePath(conf, path, info)
|
||||
of "nonimblepath", "nobabelpath":
|
||||
@@ -659,11 +561,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "backend", "b":
|
||||
let backend = parseEnum(arg.normalize, TBackend.default)
|
||||
if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg)
|
||||
if backend == backendJs: # bug #21209
|
||||
conf.globalOptions.excl {optThreadAnalysis, optThreads}
|
||||
if optRun in conf.globalOptions:
|
||||
# for now, -r uses nodejs, so define nodejs
|
||||
defineSymbol(conf.symbols, "nodejs")
|
||||
conf.backend = backend
|
||||
of "doccmd": conf.docCmd = arg
|
||||
of "define", "d":
|
||||
@@ -700,10 +597,59 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "project":
|
||||
processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info)
|
||||
of "gc":
|
||||
warningDeprecated(conf, info, "`gc:option` is deprecated; use `mm:option` instead")
|
||||
processMemoryManagementOption(switch, arg, pass, info, conf)
|
||||
of "mm":
|
||||
processMemoryManagementOption(switch, arg, pass, info, conf)
|
||||
if conf.backend == backendJs: return # for: bug #16033
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
case arg.normalize
|
||||
of "boehm":
|
||||
conf.selectedGC = gcBoehm
|
||||
defineSymbol(conf.symbols, "boehmgc")
|
||||
incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS
|
||||
of "refc":
|
||||
conf.selectedGC = gcRefc
|
||||
of "markandsweep":
|
||||
conf.selectedGC = gcMarkAndSweep
|
||||
defineSymbol(conf.symbols, "gcmarkandsweep")
|
||||
of "destructors", "arc":
|
||||
conf.selectedGC = gcArc
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
defineSymbol(conf.symbols, "gcarc")
|
||||
incl conf.globalOptions, optSeqDestructors
|
||||
incl conf.globalOptions, optTinyRtti
|
||||
if pass in {passCmd2, passPP}:
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
defineSymbol(conf.symbols, "nimV2")
|
||||
if conf.exc == excNone and conf.backend != backendCpp:
|
||||
conf.exc = excGoto
|
||||
of "orc":
|
||||
conf.selectedGC = gcOrc
|
||||
defineSymbol(conf.symbols, "gcdestructors")
|
||||
defineSymbol(conf.symbols, "gcorc")
|
||||
incl conf.globalOptions, optSeqDestructors
|
||||
incl conf.globalOptions, optTinyRtti
|
||||
if pass in {passCmd2, passPP}:
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
defineSymbol(conf.symbols, "nimV2")
|
||||
if conf.exc == excNone and conf.backend != backendCpp:
|
||||
conf.exc = excGoto
|
||||
of "hooks":
|
||||
conf.selectedGC = gcHooks
|
||||
defineSymbol(conf.symbols, "gchooks")
|
||||
incl conf.globalOptions, optSeqDestructors
|
||||
processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
defineSymbol(conf.symbols, "nimSeqsV2")
|
||||
of "go":
|
||||
conf.selectedGC = gcGo
|
||||
defineSymbol(conf.symbols, "gogc")
|
||||
of "none":
|
||||
conf.selectedGC = gcNone
|
||||
defineSymbol(conf.symbols, "nogc")
|
||||
of "stack", "regions":
|
||||
conf.selectedGC = gcRegions
|
||||
defineSymbol(conf.symbols, "gcregions")
|
||||
of "v2": warningOptionNoop(arg)
|
||||
else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
|
||||
of "warnings", "w":
|
||||
if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf)
|
||||
of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf)
|
||||
@@ -771,7 +717,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info)
|
||||
of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info)
|
||||
of "threads":
|
||||
if conf.backend == backendJs or conf.cmd == cmdNimscript: discard
|
||||
if conf.backend == backendJs: discard
|
||||
else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
|
||||
#if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
|
||||
of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
|
||||
@@ -811,7 +757,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
defineSymbol(conf.symbols, "dll")
|
||||
of "staticlib":
|
||||
incl(conf.globalOptions, optGenStaticLib)
|
||||
incl(conf.globalOptions, optNoMain)
|
||||
excl(conf.globalOptions, optGenGuiApp)
|
||||
defineSymbol(conf.symbols, "library")
|
||||
defineSymbol(conf.symbols, "staticlib")
|
||||
@@ -831,20 +776,12 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "clib":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
conf.cLinkedLibs.add arg
|
||||
conf.cLinkedLibs.add processPath(conf, arg, info).string
|
||||
of "header":
|
||||
if conf != nil: conf.headerFile = arg
|
||||
incl(conf.globalOptions, optGenIndex)
|
||||
of "nimbasepattern":
|
||||
if conf != nil: conf.nimbasePattern = arg
|
||||
of "index":
|
||||
case arg.normalize
|
||||
of "", "on": conf.globalOptions.incl {optGenIndex}
|
||||
of "only": conf.globalOptions.incl {optGenIndexOnly, optGenIndex}
|
||||
of "off": conf.globalOptions.excl {optGenIndex, optGenIndexOnly}
|
||||
else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
|
||||
of "noimportdoc":
|
||||
processOnOffSwitchG(conf, {optNoImportdoc}, arg, pass, info)
|
||||
processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
|
||||
of "import":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
if pass in {passCmd2, passPP}:
|
||||
@@ -877,9 +814,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
setTarget(conf.target, conf.target.targetOS, cpu)
|
||||
of "run", "r":
|
||||
processOnOffSwitchG(conf, {optRun}, arg, pass, info)
|
||||
if conf.backend == backendJs:
|
||||
# for now, -r uses nodejs, so define nodejs
|
||||
defineSymbol(conf.symbols, "nodejs")
|
||||
of "maxloopiterationsvm":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.maxLoopIterationsVM = parseInt(arg)
|
||||
@@ -894,7 +828,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "verbosity":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
let verbosity = parseInt(arg)
|
||||
if verbosity notin 0..3:
|
||||
if verbosity notin {0..3}:
|
||||
localError(conf, info, "invalid verbosity level: '$1'" % arg)
|
||||
conf.verbosity = verbosity
|
||||
var verb = NotesVerbosity[conf.verbosity]
|
||||
@@ -942,8 +876,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
if switch.normalize == "gendeps": deprecatedAlias(switch, "genscript")
|
||||
processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
|
||||
processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
|
||||
of "gencdeps":
|
||||
processOnOffSwitchG(conf, {optGenCDeps}, arg, pass, info)
|
||||
of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
|
||||
of "lib":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
@@ -953,9 +885,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
splitSwitch(conf, arg, key, val, pass, info)
|
||||
os.putEnv(key, val)
|
||||
of "cc":
|
||||
if conf.backend != backendJs: # bug #19330
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
setCC(conf, arg, info)
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
setCC(conf, arg, info)
|
||||
of "track":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
track(conf, arg, info)
|
||||
@@ -1027,9 +958,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
conf.exc = low(ExceptionSystem)
|
||||
defineSymbol(conf.symbols, "noCppExceptions")
|
||||
of "shownonexports":
|
||||
expectNoArg(conf, switch, arg, pass, info)
|
||||
showNonExportedFields(conf)
|
||||
of "exceptions":
|
||||
case arg.normalize
|
||||
of "cpp": conf.exc = excCpp
|
||||
@@ -1084,6 +1012,25 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
of "expandarc":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
conf.arcToExpand[arg] = "T"
|
||||
of "useversion":
|
||||
expectArg(conf, switch, arg, pass, info)
|
||||
case arg
|
||||
of "1.0":
|
||||
defineSymbol(conf.symbols, "NimMajor", "1")
|
||||
defineSymbol(conf.symbols, "NimMinor", "0")
|
||||
# old behaviors go here:
|
||||
defineSymbol(conf.symbols, "nimOldRelativePathBehavior")
|
||||
undefSymbol(conf.symbols, "nimDoesntTrackDefects")
|
||||
ast.eqTypeFlags.excl {tfGcSafe, tfNoSideEffect}
|
||||
conf.globalOptions.incl optNimV1Emulation
|
||||
of "1.2":
|
||||
defineSymbol(conf.symbols, "NimMajor", "1")
|
||||
defineSymbol(conf.symbols, "NimMinor", "2")
|
||||
conf.globalOptions.incl optNimV12Emulation
|
||||
else:
|
||||
localError(conf, info, "unknown Nim version; currently supported values are: `1.0`, `1.2`")
|
||||
# always be compatible with 1.x.100:
|
||||
defineSymbol(conf.symbols, "NimPatch", "100")
|
||||
of "benchmarkvm":
|
||||
processOnOffSwitchG(conf, {optBenchmarkVM}, arg, pass, info)
|
||||
of "profilevm":
|
||||
@@ -1097,8 +1044,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
processOnOffSwitchG(conf, {optPanics}, arg, pass, info)
|
||||
if optPanics in conf.globalOptions:
|
||||
defineSymbol(conf.symbols, "nimPanics")
|
||||
of "jsbigint64":
|
||||
processOnOffSwitchG(conf, {optJsBigInt64}, arg, pass, info)
|
||||
of "sourcemap": # xxx document in --fullhelp
|
||||
conf.globalOptions.incl optSourcemap
|
||||
conf.options.incl optLineDir
|
||||
@@ -1106,8 +1051,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
|
||||
processOnOffSwitchG(conf, {optEnableDeepCopy}, arg, pass, info)
|
||||
of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -)
|
||||
handleStdinInput(conf)
|
||||
of "nilseqs", "nilchecks", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch)
|
||||
of "nimmainprefix": conf.nimMainPrefix = arg
|
||||
of "nilseqs", "nilchecks", "mainmodule", "m", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch)
|
||||
else:
|
||||
if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg)
|
||||
else: invalidCmdLineOption(conf, pass, switch, info)
|
||||
@@ -1144,8 +1088,6 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser;
|
||||
else:
|
||||
if pass == passCmd1: config.commandArgs.add p.key
|
||||
if argsCount == 1:
|
||||
if p.key.endsWith(".nims"):
|
||||
incl(config.globalOptions, optWasNimscript)
|
||||
# support UNIX style filenames everywhere for portable build scripts:
|
||||
if config.projectName.len == 0:
|
||||
config.projectName = unixToNativePath(p.key)
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
include "../lib/system/compilation.nim"
|
||||
version = $NimMajor & "." & $NimMinor & "." & $NimPatch
|
||||
author = "Andreas Rumpf"
|
||||
description = "Compiler package providing the compiler sources as a library."
|
||||
license = "MIT"
|
||||
skipDirs = @["."]
|
||||
installDirs = @["compiler"]
|
||||
|
||||
import os
|
||||
|
||||
var compilerDir = ""
|
||||
|
||||
before install:
|
||||
rmDir("compiler")
|
||||
|
||||
let
|
||||
files = listFiles(".")
|
||||
dirs = listDirs(".")
|
||||
|
||||
mkDir("compiler")
|
||||
|
||||
for f in files:
|
||||
cpFile(f, "compiler" / f)
|
||||
|
||||
for d in dirs:
|
||||
cpDir(d, "compiler" / d)
|
||||
|
||||
requires "nim"
|
||||
@@ -15,9 +15,6 @@ import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types,
|
||||
|
||||
from magicsys import addSonSkipIntLit
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
const
|
||||
logBindings = false
|
||||
|
||||
@@ -27,7 +24,7 @@ const
|
||||
proc declareSelf(c: PContext; info: TLineInfo) =
|
||||
## Adds the magical 'Self' symbols to the current scope.
|
||||
let ow = getCurrOwner(c)
|
||||
let s = newSym(skType, getIdent(c.cache, "Self"), c.idgen, ow, info)
|
||||
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)
|
||||
@@ -61,8 +58,6 @@ proc semConceptDecl(c: PContext; n: PNode): PNode =
|
||||
for i in 0..<n.len-1:
|
||||
result[i] = n[i]
|
||||
result[^1] = semConceptDecl(c, n[^1])
|
||||
of nkCommentStmt:
|
||||
result = n
|
||||
else:
|
||||
localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n))
|
||||
result = n
|
||||
@@ -274,7 +269,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
|
||||
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)
|
||||
let candidates = searchInScopesFilterBy(c, n[namePos].sym.name, kinds)
|
||||
for candidate in candidates:
|
||||
#echo "considering ", typeToString(candidate.typ), " ", candidate.magic
|
||||
m.magic = candidate.magic
|
||||
@@ -306,19 +301,17 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
|
||||
result = matchSyms(c, n, {skMethod}, m)
|
||||
of nkIteratorDef:
|
||||
result = matchSyms(c, n, {skIterator}, m)
|
||||
of nkCommentStmt:
|
||||
result = true
|
||||
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 fulfill the
|
||||
## 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
|
||||
## '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)
|
||||
|
||||
@@ -25,7 +25,7 @@ proc undefSymbol*(symbols: StringTableRef; symbol: string) =
|
||||
# result = if isDefined(symbol): gSymbols[symbol] else: nil
|
||||
|
||||
iterator definedSymbolNames*(symbols: StringTableRef): string =
|
||||
for key in keys(symbols):
|
||||
for key, val in pairs(symbols):
|
||||
yield key
|
||||
|
||||
proc countDefinedSymbols*(symbols: StringTableRef): int =
|
||||
@@ -45,8 +45,10 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimNewTypedesc") # deadcode
|
||||
defineSymbol("nimrequiresnimframe") # deadcode
|
||||
defineSymbol("nimparsebiggestfloatmagic") # deadcode
|
||||
defineSymbol("nimalias") # deadcode
|
||||
defineSymbol("nimlocks") # deadcode
|
||||
defineSymbol("nimnode") # 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
|
||||
@@ -68,7 +70,7 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimVmExportFixed") # deadcode
|
||||
defineSymbol("nimHasSymOwnerInMacro") # deadcode
|
||||
defineSymbol("nimNewRuntime") # deadcode
|
||||
defineSymbol("nimIncrSeqV3") # deadcode
|
||||
defineSymbol("nimIncrSeqV3") # xxx: turn this into deadcode
|
||||
defineSymbol("nimAshr") # deadcode
|
||||
defineSymbol("nimNoNilSeqs") # deadcode
|
||||
defineSymbol("nimNoNilSeqs2") # deadcode
|
||||
@@ -82,24 +84,10 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
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
|
||||
|
||||
|
||||
|
||||
# > 0.20.0
|
||||
defineSymbol("nimNoZeroExtendMagic")
|
||||
defineSymbol("nimMacrosGetNodeId")
|
||||
for f in Feature:
|
||||
defineSymbol("nimHas" & $f)
|
||||
|
||||
@@ -110,18 +98,31 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
|
||||
defineSymbol("nimFixedOwned")
|
||||
defineSymbol("nimHasStyleChecks")
|
||||
defineSymbol("nimToOpenArrayCString")
|
||||
defineSymbol("nimHasUsed")
|
||||
defineSymbol("nimFixedForwardGeneric")
|
||||
defineSymbol("nimnomagic64")
|
||||
defineSymbol("nimNewShiftOps")
|
||||
defineSymbol("nimHasCursor")
|
||||
defineSymbol("nimAlignPragma")
|
||||
defineSymbol("nimHasExceptionsQuery")
|
||||
defineSymbol("nimHasIsNamedTuple")
|
||||
defineSymbol("nimHashOrdinalFixed")
|
||||
|
||||
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("nimHasLibFFIEnabled")
|
||||
|
||||
defineSymbol("nimHasStacktraceMsgs") # deadcode
|
||||
defineSymbol("nimHasSinkInference")
|
||||
defineSymbol("nimNewIntegerOps")
|
||||
defineSymbol("nimHasInvariant")
|
||||
defineSymbol("nimHasStacktraceMsgs")
|
||||
defineSymbol("nimDoesntTrackDefects")
|
||||
defineSymbol("nimHasLentIterators") # deadcode
|
||||
defineSymbol("nimHasDeclaredMagic") # deadcode
|
||||
defineSymbol("nimHasStacktracesModule") # deadcode
|
||||
defineSymbol("nimHasLentIterators")
|
||||
defineSymbol("nimHasDeclaredMagic")
|
||||
defineSymbol("nimHasStacktracesModule")
|
||||
defineSymbol("nimHasEffectTraitsModule")
|
||||
defineSymbol("nimHasCastPragmaBlocks")
|
||||
defineSymbol("nimHasDeclaredLocs")
|
||||
@@ -132,34 +133,8 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasCustomLiterals")
|
||||
defineSymbol("nimHasUnifiedTuple")
|
||||
defineSymbol("nimHasIterable")
|
||||
defineSymbol("nimHasTypeofVoid") # deadcode
|
||||
defineSymbol("nimHasDragonBox") # deadcode
|
||||
defineSymbol("nimHasTypeofVoid")
|
||||
defineSymbol("nimHasDragonBox")
|
||||
defineSymbol("nimHasHintAll")
|
||||
defineSymbol("nimHasTrace")
|
||||
defineSymbol("nimHasEffectsOf")
|
||||
|
||||
defineSymbol("nimHasEnforceNoRaises")
|
||||
defineSymbol("nimHasTopDownInference")
|
||||
defineSymbol("nimHasTemplateRedefinitionPragma")
|
||||
defineSymbol("nimHasCstringCase")
|
||||
defineSymbol("nimHasCallsitePragma")
|
||||
|
||||
defineSymbol("nimHasWarnCastSizes") # deadcode
|
||||
defineSymbol("nimHasOutParams")
|
||||
defineSymbol("nimHasSystemRaisesDefect")
|
||||
defineSymbol("nimHasWarnUnnamedBreak")
|
||||
defineSymbol("nimHasGenericDefine")
|
||||
defineSymbol("nimHasDefineAliases")
|
||||
defineSymbol("nimHasWarnBareExcept")
|
||||
defineSymbol("nimHasDup")
|
||||
defineSymbol("nimHasChecksums")
|
||||
defineSymbol("nimHasSendable")
|
||||
defineSymbol("nimAllowNonVarDestructor")
|
||||
defineSymbol("nimHasQuirky")
|
||||
defineSymbol("nimHasEnsureMove")
|
||||
defineSymbol("nimHasNoReturnError")
|
||||
|
||||
defineSymbol("nimHasCastExtendedVm")
|
||||
defineSymbol("nimHasGenericsOpenSym2")
|
||||
defineSymbol("nimHasNolineTooLong")
|
||||
defineSymbol("nimHasGenericsOpenSym3")
|
||||
|
||||
@@ -54,19 +54,3 @@ proc isCompilerDebug*(): bool =
|
||||
{.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,17 +9,10 @@
|
||||
|
||||
# This module implements a dependency file generator.
|
||||
|
||||
import options, ast, ropes, pathutils, msgs, lineinfos
|
||||
|
||||
import modulegraphs
|
||||
|
||||
import std/[os, parseutils]
|
||||
import strutils except addf
|
||||
import std/private/globs
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
import
|
||||
options, ast, ropes, idents, passes, modulepaths, pathutils
|
||||
|
||||
from modulegraphs import ModuleGraph, PPassContext
|
||||
|
||||
type
|
||||
TGen = object of PPassContext
|
||||
@@ -35,64 +28,18 @@ proc addDependencyAux(b: Backend; importing, imported: string) =
|
||||
b.dotGraph.addf("\"$1\" -> \"$2\";$n", [rope(importing), rope(imported)])
|
||||
# s1 -> s2_4[label="[0-9]"];
|
||||
|
||||
proc toNimblePath(s: string, isStdlib: bool): string =
|
||||
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:
|
||||
sub[^1] = '2'
|
||||
sub.add '/'
|
||||
start = s.find(sub) # /pkgs2
|
||||
if start < 0:
|
||||
return s
|
||||
|
||||
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 =
|
||||
proc addDotDependency(c: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
let g = PGen(c)
|
||||
let b = Backend(g.graph.backend)
|
||||
case n.kind
|
||||
of nkImportStmt:
|
||||
for i in 0..<n.len:
|
||||
addDependency(c, g, b, n[i])
|
||||
var imported = getModuleName(g.config, n[i])
|
||||
addDependencyAux(b, g.module.name.s, imported)
|
||||
of nkFromStmt, nkImportExceptStmt:
|
||||
addDependency(c, g, b, n[0])
|
||||
var imported = getModuleName(g.config, n[0])
|
||||
addDependencyAux(b, g.module.name.s, imported)
|
||||
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
|
||||
for i in 0..<n.len: discard addDotDependency(c, n[i])
|
||||
else:
|
||||
@@ -104,12 +51,18 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
|
||||
rope(project.splitFile.name), b.dotGraph],
|
||||
changeFileExt(project, "dot"))
|
||||
|
||||
proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
when not defined(nimHasSinkInference):
|
||||
{.pragma: nosinks.}
|
||||
|
||||
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
|
||||
var g: PGen
|
||||
new(g)
|
||||
g.module = module
|
||||
g.config = graph.config
|
||||
g.graph = graph
|
||||
if graph.backend == nil:
|
||||
graph.backend = Backend(dotGraph: "")
|
||||
graph.backend = Backend(dotGraph: nil)
|
||||
result = g
|
||||
|
||||
const gendependPass* = makePass(open = myOpen, process = addDotDependency)
|
||||
|
||||
|
||||
554
compiler/dfa.nim
554
compiler/dfa.nim
@@ -9,33 +9,37 @@
|
||||
|
||||
## 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
|
||||
## make this easier to handle: There are only 2 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
|
||||
## taken). Exhaustive case statements are translated
|
||||
## so that the last branch is transformed into an 'else' branch.
|
||||
## ``return`` and ``break`` are all covered by 'goto'.
|
||||
##
|
||||
## Control flow through exception handling:
|
||||
## Contrary to popular belief, exception handling doesn't cause
|
||||
## many problems for this DFA representation, ``raise`` is a statement
|
||||
## that ``goes to`` the outer ``finally`` or ``except`` if there is one,
|
||||
## otherwise it is the same as ``return``. Every call is treated as
|
||||
## a call that can potentially ``raise``. However, without a surrounding
|
||||
## ``try`` we don't emit these ``fork ReturnLabel`` instructions in order
|
||||
## to speed up the dataflow analysis passes.
|
||||
##
|
||||
## 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 ast, intsets, lineinfos, renderer
|
||||
import std/private/asciitables
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
InstrKind* = enum
|
||||
goto, loop, fork, def, use
|
||||
goto, fork, def, use
|
||||
Instr* = object
|
||||
n*: PNode # contains the def/use location.
|
||||
case kind*: InstrKind
|
||||
of goto, fork, loop: dest*: int
|
||||
of def, use:
|
||||
n*: PNode # contains the def/use location.
|
||||
of goto, fork: dest*: int
|
||||
else: discard
|
||||
|
||||
ControlFlowGraph* = seq[Instr]
|
||||
|
||||
@@ -52,10 +56,9 @@ type
|
||||
|
||||
Con = object
|
||||
code: ControlFlowGraph
|
||||
inTryStmt, interestingInstructions: int
|
||||
inTryStmt: int
|
||||
blocks: seq[TBlock]
|
||||
owner: PSym
|
||||
root: PSym
|
||||
|
||||
proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
|
||||
# for debugging purposes
|
||||
@@ -63,7 +66,7 @@ proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
|
||||
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}:
|
||||
if c[i].kind in {goto, fork}:
|
||||
jumpTargets.incl(i+c[i].dest)
|
||||
var i = start
|
||||
while i <= last:
|
||||
@@ -74,12 +77,12 @@ proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
|
||||
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:
|
||||
of goto, fork:
|
||||
result.add "L"
|
||||
result.addInt c[i].dest+i
|
||||
result.add("\t#")
|
||||
result.add($c[i].n.info.line)
|
||||
result.add("\n")
|
||||
inc i
|
||||
if i in jumpTargets: result.add("L" & $i & ": End\n")
|
||||
|
||||
@@ -87,13 +90,181 @@ 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 =
|
||||
proc forkI(c: var Con; n: PNode): TPosition =
|
||||
result = TPosition(c.code.len)
|
||||
c.code.add Instr(kind: fork, dest: 0)
|
||||
c.code.add Instr(n: n, kind: fork, dest: 0)
|
||||
|
||||
proc gotoI(c: var Con): TPosition =
|
||||
proc gotoI(c: var Con; n: PNode): TPosition =
|
||||
result = TPosition(c.code.len)
|
||||
c.code.add Instr(kind: goto, dest: 0)
|
||||
c.code.add Instr(n: n, kind: goto, dest: 0)
|
||||
|
||||
#[
|
||||
|
||||
Join is no more
|
||||
===============
|
||||
Instead of generating join instructions we adapt our traversal of the CFG.
|
||||
|
||||
When encountering a fork we split into two paths, we follow the path
|
||||
starting at "pc + 1" until it encounters the joinpoint: "pc + forkInstr.dest".
|
||||
If we encounter gotos that would jump further than the current joinpoint,
|
||||
as can happen with gotos generated by unstructured controlflow such as break, raise or return,
|
||||
we simply suspend following the current path, and follow the other path until the new joinpoint
|
||||
which is simply the instruction pointer returned to us by the now suspended path.
|
||||
If the path we are following now, also encounters a goto that exceeds the joinpoint
|
||||
we repeat the process; suspending the current path and evaluating the other one with a new joinpoint.
|
||||
If we eventually reach a common joinpoint we join the two paths.
|
||||
This new "ping-pong" approach has the obvious advantage of not requiring join instructions, as such
|
||||
cutting down on the CFG size but is also mandatory for correctly handling complicated cases
|
||||
of unstructured controlflow.
|
||||
|
||||
|
||||
Design of join
|
||||
==============
|
||||
|
||||
block:
|
||||
if cond: break
|
||||
def(x)
|
||||
|
||||
use(x)
|
||||
|
||||
Generates:
|
||||
|
||||
L0: fork lab1
|
||||
join L0 # patched.
|
||||
goto Louter
|
||||
lab1:
|
||||
def x
|
||||
join L0
|
||||
Louter:
|
||||
use x
|
||||
|
||||
|
||||
block outer:
|
||||
while a:
|
||||
while b:
|
||||
if foo:
|
||||
if bar:
|
||||
break outer # --> we need to 'join' every pushed 'fork' here
|
||||
|
||||
|
||||
This works and then our abstract interpretation needs to deal with 'fork'
|
||||
differently. It really causes a split in execution. Two threads are
|
||||
"spawned" and both need to reach the 'join L' instruction. Afterwards
|
||||
the abstract interpretations are joined and execution resumes single
|
||||
threaded.
|
||||
|
||||
|
||||
Abstract Interpretation
|
||||
-----------------------
|
||||
|
||||
proc interpret(pc, state, comesFrom): state =
|
||||
result = state
|
||||
# we need an explicit 'create' instruction (an explicit heap), in order
|
||||
# to deal with 'var x = create(); var y = x; var z = y; destroy(z)'
|
||||
while true:
|
||||
case pc
|
||||
of fork:
|
||||
let a = interpret(pc+1, result, pc)
|
||||
let b = interpret(forkTarget, result, pc)
|
||||
result = a ++ b # ++ is a union operation
|
||||
inc pc
|
||||
of join:
|
||||
if joinTarget == comesFrom: return result
|
||||
else: inc pc
|
||||
of use X:
|
||||
if not result.contains(x):
|
||||
error "variable not initialized " & x
|
||||
inc pc
|
||||
of def X:
|
||||
if not result.contains(x):
|
||||
result.incl X
|
||||
else:
|
||||
error "overwrite of variable causes memory leak " & x
|
||||
inc pc
|
||||
of destroy X:
|
||||
result.excl X
|
||||
|
||||
This is correct but still can lead to false positives:
|
||||
|
||||
proc p(cond: bool) =
|
||||
if cond:
|
||||
new(x)
|
||||
otherThings()
|
||||
if cond:
|
||||
destroy x
|
||||
|
||||
Is not a leak. We should find a way to model *data* flow, not just
|
||||
control flow. One solution is to rewrite the 'if' without a fork
|
||||
instruction. The unstructured aspect can now be easily dealt with
|
||||
the 'goto' and 'join' instructions.
|
||||
|
||||
proc p(cond: bool) =
|
||||
L0: fork Lend
|
||||
new(x)
|
||||
# do not 'join' here!
|
||||
|
||||
Lend:
|
||||
otherThings()
|
||||
join L0 # SKIP THIS FOR new(x) SOMEHOW
|
||||
destroy x
|
||||
join L0 # but here.
|
||||
|
||||
|
||||
|
||||
But if we follow 'goto Louter' we will never come to the join point.
|
||||
We restore the bindings after popping pc from the stack then there
|
||||
"no" problem?!
|
||||
|
||||
|
||||
while cond:
|
||||
prelude()
|
||||
if not condB: break
|
||||
postlude()
|
||||
|
||||
--->
|
||||
var setFlag = true
|
||||
while cond and not setFlag:
|
||||
prelude()
|
||||
if not condB:
|
||||
setFlag = true # BUT: Dependency
|
||||
if not setFlag: # HERE
|
||||
postlude()
|
||||
|
||||
--->
|
||||
var setFlag = true
|
||||
while cond and not setFlag:
|
||||
prelude()
|
||||
if not condB:
|
||||
postlude()
|
||||
setFlag = true
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
while cond:
|
||||
prelude()
|
||||
if more:
|
||||
if not condB: break
|
||||
stuffHere()
|
||||
postlude()
|
||||
|
||||
-->
|
||||
var setFlag = true
|
||||
while cond and not setFlag:
|
||||
prelude()
|
||||
if more:
|
||||
if not condB:
|
||||
setFlag = false
|
||||
else:
|
||||
stuffHere()
|
||||
postlude()
|
||||
else:
|
||||
postlude()
|
||||
|
||||
This is getting complicated. Instead we keep the whole 'join' idea but
|
||||
duplicate the 'join' instructions on breaks and return exits!
|
||||
|
||||
]#
|
||||
|
||||
proc genLabel(c: Con): TPosition = TPosition(c.code.len)
|
||||
|
||||
@@ -101,8 +272,8 @@ 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 jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
|
||||
c.code.add Instr(n: n, kind: goto, dest: checkedDistance(p.int - c.code.len))
|
||||
|
||||
proc patch(c: var Con, p: TPosition) =
|
||||
# patch with current index
|
||||
@@ -112,12 +283,12 @@ proc gen(c: var Con; n: PNode)
|
||||
|
||||
proc popBlock(c: var Con; oldLen: int) =
|
||||
var exits: seq[TPosition]
|
||||
exits.add c.gotoI()
|
||||
exits.add c.gotoI(newNode(nkEmpty))
|
||||
for f in c.blocks[oldLen].breakFixups:
|
||||
c.patch(f[0])
|
||||
for finale in f[1]:
|
||||
c.gen(finale)
|
||||
exits.add c.gotoI()
|
||||
exits.add c.gotoI(newNode(nkEmpty))
|
||||
for e in exits:
|
||||
c.patch e
|
||||
c.blocks.setLen(oldLen)
|
||||
@@ -132,29 +303,91 @@ 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()
|
||||
when true:
|
||||
proc genWhile(c: var Con; n: PNode) =
|
||||
# We unroll every loop 3 times. We emulate 0, 1, 2 iterations
|
||||
# through the loop. We need to prove this is correct for our
|
||||
# purposes. But Herb Sutter claims it is. (Proof by authority.)
|
||||
#
|
||||
# EDIT: Actually, we only need to unroll 2 times
|
||||
# because Nim doesn't have a way of breaking/goto-ing into
|
||||
# a loop iteration. Unrolling 2 times is much better for compile
|
||||
# times of nested loops than 3 times, so we do that here.
|
||||
#[
|
||||
while cond:
|
||||
body
|
||||
|
||||
Becomes:
|
||||
|
||||
block:
|
||||
if cond:
|
||||
body
|
||||
if cond:
|
||||
body
|
||||
if cond:
|
||||
body
|
||||
|
||||
We still need to ensure 'break' resolves properly, so an AST to AST
|
||||
translation is impossible.
|
||||
|
||||
So the code to generate is:
|
||||
|
||||
cond
|
||||
fork L4 # F1
|
||||
body
|
||||
cond
|
||||
fork L5 # F2
|
||||
body
|
||||
cond
|
||||
fork L6 # F3
|
||||
body
|
||||
L6:
|
||||
join F3
|
||||
L5:
|
||||
join F2
|
||||
L4:
|
||||
join F1
|
||||
]#
|
||||
if isTrue(n[0]):
|
||||
# 'while true' is an idiom in Nim and so we produce
|
||||
# better code for it:
|
||||
withBlock(nil):
|
||||
for i in 0..1:
|
||||
c.gen(n[1])
|
||||
else:
|
||||
withBlock(nil):
|
||||
var endings: array[2, TPosition]
|
||||
for i in 0..1:
|
||||
c.gen(n[0])
|
||||
endings[i] = c.forkI(n)
|
||||
c.gen(n[1])
|
||||
for i in countdown(endings.high, 0):
|
||||
c.patch(endings[i])
|
||||
|
||||
else:
|
||||
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(n, lab1)
|
||||
else:
|
||||
c.gen(n[0])
|
||||
forkT(n):
|
||||
c.gen(n[1])
|
||||
c.jmpBack(n, lab1)
|
||||
|
||||
template forkT(n, body) =
|
||||
let lab1 = c.forkI(n)
|
||||
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) =
|
||||
#[
|
||||
|
||||
@@ -193,22 +426,15 @@ proc genIf(c: var Con, n: PNode) =
|
||||
|
||||
]#
|
||||
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])
|
||||
forkT(it[1]):
|
||||
c.gen(it[1])
|
||||
endings.add c.gotoI(it[1])
|
||||
for i in countdown(endings.high, 0):
|
||||
c.patch(endings[i])
|
||||
|
||||
proc genAndOr(c: var Con; n: PNode) =
|
||||
# asgn dest, a
|
||||
@@ -217,7 +443,7 @@ proc genAndOr(c: var Con; n: PNode) =
|
||||
# lab1:
|
||||
# join F1
|
||||
c.gen(n[1])
|
||||
forkT:
|
||||
forkT(n):
|
||||
c.gen(n[2])
|
||||
|
||||
proc genCase(c: var Con; n: PNode) =
|
||||
@@ -232,40 +458,39 @@ proc genCase(c: var Con; n: PNode) =
|
||||
# elsePart
|
||||
# Lend:
|
||||
let isExhaustive = skipTypes(n[0].typ,
|
||||
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring}
|
||||
abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString}
|
||||
|
||||
# we generate endings as a set of chained gotos, this is a bit awkward but it
|
||||
# ensures when recursively traversing the CFG for various analysis, we don't
|
||||
# artificially extended the life of each branch (for the purposes of DFA)
|
||||
# beyond the minimum amount.
|
||||
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)
|
||||
if endings.len != 0:
|
||||
c.patch(endings[^1])
|
||||
else:
|
||||
forkT:
|
||||
forkT(it.lastSon):
|
||||
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])
|
||||
if endings.len != 0:
|
||||
c.patch(endings[^1])
|
||||
endings.add c.gotoI(it.lastSon)
|
||||
|
||||
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()
|
||||
let lab1 = c.gotoI(n)
|
||||
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:
|
||||
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
|
||||
@@ -273,7 +498,6 @@ proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
|
||||
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:
|
||||
@@ -304,9 +528,9 @@ proc genTry(c: var Con; n: PNode) =
|
||||
for i in 1..<n.len:
|
||||
let it = n[i]
|
||||
if it.kind != nkFinally:
|
||||
forkT:
|
||||
forkT(it):
|
||||
c.gen(it.lastSon)
|
||||
endings.add c.gotoI()
|
||||
endings.add c.gotoI(it)
|
||||
for i in countdown(endings.high, 0):
|
||||
c.patch(endings[i])
|
||||
|
||||
@@ -314,12 +538,11 @@ proc genTry(c: var Con; n: PNode) =
|
||||
if fin.kind == nkFinally:
|
||||
c.gen(fin[0])
|
||||
|
||||
template genNoReturn(c: var Con) =
|
||||
template genNoReturn(c: var Con; n: PNode) =
|
||||
# leave the graph
|
||||
c.code.add Instr(kind: goto, dest: high(int) - c.code.len)
|
||||
c.code.add Instr(n: n, 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):
|
||||
@@ -328,14 +551,13 @@ proc genRaise(c: var Con; n: PNode) =
|
||||
return
|
||||
assert false #Unreachable
|
||||
else:
|
||||
genNoReturn(c)
|
||||
genNoReturn(c, n)
|
||||
|
||||
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:
|
||||
@@ -344,6 +566,124 @@ proc genReturn(c: var Con; n: PNode) =
|
||||
|
||||
const
|
||||
InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
|
||||
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
|
||||
|
||||
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[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, nkCheckedFieldExpr, nkBracketExpr}:
|
||||
n = n[0]
|
||||
of PathKinds1:
|
||||
n = n[1]
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr:
|
||||
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 nkCheckedFieldExpr:
|
||||
if currFieldPath[0][1].sym != currObjPath[0][1].sym: return no
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
proc skipTrivials(c: var Con, n: PNode): PNode =
|
||||
result = n
|
||||
@@ -362,9 +702,8 @@ 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
|
||||
if n.sym.kind in InterestingSyms:
|
||||
c.code.add Instr(n: orig, kind: use)
|
||||
else:
|
||||
gen(c, n)
|
||||
|
||||
@@ -372,9 +711,7 @@ 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
|
||||
c.code.add Instr(n: orig, kind: def)
|
||||
|
||||
proc genCall(c: var Con; n: PNode) =
|
||||
gen(c, n[0])
|
||||
@@ -382,18 +719,18 @@ proc genCall(c: var Con; n: PNode) =
|
||||
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])
|
||||
when false:
|
||||
if t != nil and i < t.len and t[i].kind == tyOut:
|
||||
# Pass by 'out' is a 'must def'. Good enough for a move optimizer.
|
||||
genDef(c, n[i])
|
||||
# every call can potentially raise:
|
||||
if c.inTryStmt > 0 and canRaiseConservative(n[0]):
|
||||
inc c.interestingInstructions
|
||||
# we generate the instruction sequence:
|
||||
# fork lab1
|
||||
# goto exceptionHandler (except or finally)
|
||||
# lab1:
|
||||
# join F1
|
||||
forkT:
|
||||
forkT(n):
|
||||
for i in countdown(c.blocks.high, 0):
|
||||
if c.blocks[i].isTryBlock:
|
||||
genBreakOrRaiseAux(c, i, n)
|
||||
@@ -431,11 +768,11 @@ proc gen(c: var Con; n: PNode) =
|
||||
else:
|
||||
genCall(c, n)
|
||||
if sfNoReturn in n[0].sym.flags:
|
||||
genNoReturn(c)
|
||||
genNoReturn(c, n)
|
||||
else:
|
||||
genCall(c, n)
|
||||
of nkCharLit..nkNilLit: discard
|
||||
of nkAsgn, nkFastAsgn, nkSinkAsgn:
|
||||
of nkAsgn, nkFastAsgn:
|
||||
gen(c, n[1])
|
||||
|
||||
if n[0].kind in PathKinds0:
|
||||
@@ -472,32 +809,13 @@ proc gen(c: var Con; n: PNode) =
|
||||
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 =
|
||||
proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
|
||||
## constructs a control flow graph for ``body``.
|
||||
var c = Con(code: @[], blocks: @[], owner: s, root: root)
|
||||
var c = Con(code: @[], blocks: @[], owner: s)
|
||||
withBlock(s):
|
||||
gen(c, body)
|
||||
if root.kind == skResult:
|
||||
genImplicitReturn(c)
|
||||
when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
|
||||
genImplicitReturn(c)
|
||||
when defined(gcArc) or defined(gcOrc):
|
||||
result = c.code # will move
|
||||
else:
|
||||
shallowCopy(result, c.code)
|
||||
when false:
|
||||
optimizeJumps result
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
||||
# semantic checking.
|
||||
|
||||
import
|
||||
options, ast, msgs, docgen, lineinfos, pathutils, packages
|
||||
options, ast, msgs, passes, docgen, lineinfos, pathutils
|
||||
|
||||
from modulegraphs import ModuleGraph, PPassContext
|
||||
|
||||
@@ -23,7 +23,7 @@ type
|
||||
PGen = ref TGen
|
||||
|
||||
proc shouldProcess(g: PGen): bool =
|
||||
(optWholeProject in g.doc.conf.globalOptions and g.doc.conf.belongsToProjectPackage(g.module)) or
|
||||
(optWholeProject in g.doc.conf.globalOptions and g.module.getnimblePkgId == g.doc.conf.mainPackageId) or
|
||||
sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex
|
||||
|
||||
template closeImpl(body: untyped) {.dirty.} =
|
||||
@@ -38,25 +38,25 @@ template closeImpl(body: untyped) {.dirty.} =
|
||||
except IOError:
|
||||
discard
|
||||
|
||||
proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
closeImpl:
|
||||
writeOutput(g.doc, useWarning, groupedToc)
|
||||
|
||||
proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
|
||||
closeImpl:
|
||||
writeOutputJson(g.doc, useWarning)
|
||||
|
||||
proc processNode*(c: PPassContext, n: PNode): PNode =
|
||||
proc processNode(c: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
var g = PGen(c)
|
||||
if shouldProcess(g):
|
||||
generateDoc(g.doc, n, n, g.config)
|
||||
generateDoc(g.doc, n, n)
|
||||
|
||||
proc processNodeJson*(c: PPassContext, n: PNode): PNode =
|
||||
proc processNodeJson(c: PPassContext, n: PNode): PNode =
|
||||
result = n
|
||||
var g = PGen(c)
|
||||
if shouldProcess(g):
|
||||
generateJson(g.doc, n, g.config, false)
|
||||
generateJson(g.doc, n, false)
|
||||
|
||||
template myOpenImpl(ext: untyped) {.dirty.} =
|
||||
var g: PGen
|
||||
@@ -64,15 +64,25 @@ template myOpenImpl(ext: untyped) {.dirty.} =
|
||||
g.module = module
|
||||
g.config = graph.config
|
||||
var d = newDocumentor(AbsoluteFile toFullPath(graph.config, FileIndex module.position),
|
||||
graph.cache, graph.config, ext, module, hasToc = true)
|
||||
graph.cache, graph.config, ext, module)
|
||||
d.hasToc = true
|
||||
g.doc = d
|
||||
result = g
|
||||
|
||||
proc openHtml*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
myOpenImpl(HtmlExt)
|
||||
|
||||
proc openTex*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
proc myOpenTex(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
myOpenImpl(TexExt)
|
||||
|
||||
proc openJson*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
proc myOpenJson(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
|
||||
myOpenImpl(JsonExt)
|
||||
|
||||
const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
|
||||
const docgen2TexPass* = makePass(open = myOpenTex, process = processNode,
|
||||
close = close)
|
||||
const docgen2JsonPass* = makePass(open = myOpenJson, process = processNodeJson,
|
||||
close = closeJson)
|
||||
|
||||
proc finishDoc2Pass*(project: string) =
|
||||
discard
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
|
||||
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, "$"), idgen, t.owner, info)
|
||||
result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info)
|
||||
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
|
||||
dest.typ = t
|
||||
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
|
||||
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)
|
||||
@@ -30,7 +26,7 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
|
||||
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),
|
||||
caseStmt.add newTree(nkOfBranch, newSymNode(field),
|
||||
newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
|
||||
#newIntTypeNode(nkIntLit, field.position, t)
|
||||
|
||||
@@ -67,12 +63,12 @@ proc searchObjCase(t: PType; field: PSym): PNode =
|
||||
doAssert result != nil
|
||||
|
||||
proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), idgen, t.owner, info)
|
||||
result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextSymId idgen, t.owner, info)
|
||||
|
||||
let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, 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"), idgen, result, info)
|
||||
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)
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
|
||||
import ast, renderer, options, strutils, types
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
ErrorKind* = enum ## expand as you need.
|
||||
RawTypeMismatchError
|
||||
|
||||
@@ -65,19 +65,19 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
|
||||
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)
|
||||
theAddr = gExeHandle.symAddr(name)
|
||||
# then try libc:
|
||||
if theAddr.isNil:
|
||||
let dllhandle = getDll(conf, gDllCache, libcDll, sym.info)
|
||||
theAddr = dllhandle.symAddr(name.cstring)
|
||||
theAddr = dllhandle.symAddr(name)
|
||||
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)
|
||||
theAddr = dllhandle.symAddr(name)
|
||||
if theAddr.isNil: globalError(conf, sym.info,
|
||||
"cannot import symbol: " & name & " from " & libPathMsg)
|
||||
result.intVal = cast[int](theAddr)
|
||||
result.intVal = cast[ByteAddress](theAddr)
|
||||
|
||||
proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
|
||||
if t == nil: return addr libffi.type_void
|
||||
@@ -110,10 +110,10 @@ proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI
|
||||
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 rd(T, p: untyped): untyped = (cast[ptr T](p))[]
|
||||
template wr(T, p, v: untyped): untyped = (cast[ptr T](p))[] = v
|
||||
template `+!`(x, y: untyped): untyped =
|
||||
cast[pointer](cast[int](x) + y)
|
||||
cast[pointer](cast[ByteAddress](x) + y)
|
||||
|
||||
proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
|
||||
## computes the size of the blob
|
||||
@@ -177,8 +177,8 @@ 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)
|
||||
template awr(T, v: untyped): untyped =
|
||||
wr(T, res, v)
|
||||
|
||||
case typ.kind
|
||||
of tyBool: awr(bool, v.intVal != 0)
|
||||
@@ -369,13 +369,13 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
|
||||
# in their unboxed representation so nothing it to be unpacked:
|
||||
result = n
|
||||
else:
|
||||
awi(nkPtrLit, cast[int](p))
|
||||
awi(nkPtrLit, cast[ByteAddress](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))
|
||||
awi(nkPtrLit, cast[ByteAddress](p))
|
||||
elif n != nil and n.len == 1:
|
||||
internalAssert(conf, n.kind == nkRefTy)
|
||||
n[0] = unpack(conf, p, typ.lastSon, n[0])
|
||||
@@ -423,8 +423,8 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
|
||||
proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
|
||||
internalAssert conf, call[0].kind == nkPtrLit
|
||||
|
||||
var cif: TCif = default(TCif)
|
||||
var sig: ParamList = default(ParamList)
|
||||
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)
|
||||
@@ -463,8 +463,8 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
|
||||
info: TLineInfo): PNode =
|
||||
internalAssert conf, fn.kind == nkPtrLit
|
||||
|
||||
var cif: TCif = default(TCif)
|
||||
var sig: ParamList = default(ParamList)
|
||||
var cif: TCif
|
||||
var sig: ParamList
|
||||
for i in 0..len-1:
|
||||
var aTyp = args[i+start].typ
|
||||
if aTyp.isNil:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
## Template evaluation engine. Now hygienic.
|
||||
|
||||
import
|
||||
strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents, trees
|
||||
strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents
|
||||
|
||||
type
|
||||
TemplCtx = object
|
||||
@@ -26,7 +26,7 @@ type
|
||||
|
||||
proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
|
||||
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) =
|
||||
template handleParam(param) =
|
||||
@@ -49,14 +49,13 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
|
||||
internalAssert c.config, sfGenSym in s.flags or s.kind == skType
|
||||
var x = PSym(idTableGet(c.mapping, s))
|
||||
if x == nil:
|
||||
x = copySym(s, c.idgen)
|
||||
x = copySym(s, nextSymId(c.idgen))
|
||||
# sem'check needs to set the owner properly later, see bug #9476
|
||||
x.owner = nil # c.genSymOwner
|
||||
#if x.kind == skParam and x.owner.kind == skModule:
|
||||
# internalAssert c.config, false
|
||||
idTablePut(c.mapping, s, x)
|
||||
if sfGenSym in s.flags:
|
||||
# TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID)
|
||||
result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID),
|
||||
if c.instLines: actual.info else: templ.info)
|
||||
else:
|
||||
@@ -205,7 +204,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
|
||||
result = copyNode(body)
|
||||
ctx.instLines = sfCallsite in tmpl.flags
|
||||
if ctx.instLines:
|
||||
setInfoRecursive(result, n.info)
|
||||
result.info = n.info
|
||||
for i in 0..<body.safeLen:
|
||||
evalTemplateAux(body[i], args, ctx, result)
|
||||
result.flags.incl nfFromTemplate
|
||||
|
||||
@@ -12,16 +12,9 @@
|
||||
# from a lineinfos file, to provide generalized procedures to compile
|
||||
# nim files.
|
||||
|
||||
import ropes, platform, condsyms, options, msgs, lineinfos, pathutils, modulepaths
|
||||
import ropes, platform, condsyms, options, msgs, lineinfos, pathutils
|
||||
|
||||
import std/[os, osproc, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils]
|
||||
|
||||
import std / strutils except addf
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
import std/[os, strutils, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar]
|
||||
|
||||
type
|
||||
TInfoCCProp* = enum # properties of the C compiler:
|
||||
@@ -33,7 +26,6 @@ type
|
||||
hasGnuAsm, # CC's asm uses the absurd GNU assembler syntax
|
||||
hasDeclspec, # CC has __declspec(X)
|
||||
hasAttribute, # CC has __attribute__((X))
|
||||
hasBuiltinUnreachable # CC has __builtin_unreachable
|
||||
TInfoCCProps* = set[TInfoCCProp]
|
||||
TInfoCC* = tuple[
|
||||
name: string, # the short name of the compiler
|
||||
@@ -91,12 +83,12 @@ compiler gcc:
|
||||
linkLibCmd: " -l$1",
|
||||
debug: "",
|
||||
pic: "-fPIC",
|
||||
asmStmtFrmt: "__asm__($1);$n",
|
||||
asmStmtFrmt: "asm($1);$n",
|
||||
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
|
||||
produceAsm: gnuAsmListing,
|
||||
cppXsupport: "-std=gnu++17 -funsigned-char",
|
||||
cppXsupport: "-std=gnu++14 -funsigned-char",
|
||||
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
||||
hasAttribute, hasBuiltinUnreachable})
|
||||
hasAttribute})
|
||||
|
||||
# GNU C and C++ Compiler
|
||||
compiler nintendoSwitchGCC:
|
||||
@@ -121,9 +113,9 @@ compiler nintendoSwitchGCC:
|
||||
asmStmtFrmt: "asm($1);$n",
|
||||
structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name
|
||||
produceAsm: gnuAsmListing,
|
||||
cppXsupport: "-std=gnu++17 -funsigned-char",
|
||||
cppXsupport: "-std=gnu++14 -funsigned-char",
|
||||
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
|
||||
hasAttribute, hasBuiltinUnreachable})
|
||||
hasAttribute})
|
||||
|
||||
# LLVM Frontend for GCC/G++
|
||||
compiler llvmGcc:
|
||||
@@ -158,7 +150,7 @@ compiler vcc:
|
||||
compileTmpl: "/c$vccplatform $options $include /nologo /Fo$objfile $file",
|
||||
buildGui: " /SUBSYSTEM:WINDOWS user32.lib ",
|
||||
buildDll: " /LD",
|
||||
buildLib: "vccexe --command:lib$vccplatform /nologo /OUT:$libfile $objfiles",
|
||||
buildLib: "lib /OUT:$libfile $objfiles",
|
||||
linkerExe: "cl",
|
||||
linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
|
||||
includeCmd: " /I",
|
||||
@@ -331,9 +323,7 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
|
||||
platform.OS[conf.target.targetOS].name & '.' &
|
||||
CC[c].name & fullSuffix
|
||||
result = getConfigVar(conf, fullCCname)
|
||||
if existsConfigVar(conf, fullCCname):
|
||||
result = getConfigVar(conf, fullCCname)
|
||||
else:
|
||||
if result.len == 0:
|
||||
# not overridden for this cross compilation setting?
|
||||
result = getConfigVar(conf, CC[c].name & fullSuffix)
|
||||
else:
|
||||
@@ -377,7 +367,6 @@ proc initVars*(conf: ConfigRef) =
|
||||
|
||||
proc completeCfilePath*(conf: ConfigRef; cfile: AbsoluteFile,
|
||||
createSubDir: bool = true): AbsoluteFile =
|
||||
## Generate the absolute file path to the generated modules.
|
||||
result = completeGeneratedFilePath(conf, cfile, createSubDir)
|
||||
|
||||
proc toObjFile*(conf: ConfigRef; filename: AbsoluteFile): AbsoluteFile =
|
||||
@@ -388,7 +377,7 @@ proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
|
||||
conf.toCompile.add(cf)
|
||||
|
||||
proc addLocalCompileOption*(conf: ConfigRef; option: string; nimfile: AbsoluteFile) =
|
||||
let key = completeCfilePath(conf, mangleModuleName(conf, nimfile).AbsoluteFile).string
|
||||
let key = completeCfilePath(conf, withPackageName(conf, nimfile)).string
|
||||
var value = conf.cfileSpecificOptions.getOrDefault(key)
|
||||
if strutils.find(value, option, 0) < 0:
|
||||
addOpt(value, option)
|
||||
@@ -441,22 +430,11 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
|
||||
# really: Cross compilation from Linux to Linux for example is entirely
|
||||
# reasonable.
|
||||
# `optGenMapping` is included here for niminst.
|
||||
# We use absolute paths for vcc / cl, see issue #19883.
|
||||
let options =
|
||||
if conf.cCompiler == ccVcc:
|
||||
{optGenMapping}
|
||||
else:
|
||||
{optGenScript, optGenMapping}
|
||||
result = conf.globalOptions * options != {}
|
||||
result = conf.globalOptions * {optGenScript, optGenMapping} != {}
|
||||
|
||||
proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string =
|
||||
result = conf.compileOptions
|
||||
|
||||
if (conf.cCompiler == ccGcc or conf.cCompiler == ccCLang) and
|
||||
conf.selectedGC == gcRefc:
|
||||
# bug #10625
|
||||
addOpt(result, "-fno-omit-frame-pointer")
|
||||
|
||||
for option in conf.compileOptionsCmd:
|
||||
if strutils.find(result, option, 0) < 0:
|
||||
addOpt(result, option)
|
||||
@@ -505,10 +483,7 @@ proc needsExeExt(conf: ConfigRef): bool {.inline.} =
|
||||
(conf.target.hostOS == osWindows)
|
||||
|
||||
proc useCpp(conf: ConfigRef; cfile: AbsoluteFile): bool =
|
||||
# List of possible file extensions taken from gcc
|
||||
for ext in [".C", ".cc", ".cpp", ".CPP", ".c++", ".cp", ".cxx"]:
|
||||
if cfile.string.endsWith(ext): return true
|
||||
false
|
||||
conf.backend == backendCpp and not cfile.string.endsWith(".c")
|
||||
|
||||
proc envFlags(conf: ConfigRef): string =
|
||||
result = if conf.backend == backendCpp:
|
||||
@@ -516,14 +491,14 @@ proc envFlags(conf: ConfigRef): string =
|
||||
else:
|
||||
getEnv("CFLAGS")
|
||||
|
||||
proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; isCpp: bool): string =
|
||||
proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: AbsoluteFile): string =
|
||||
if compiler == ccEnv:
|
||||
result = if isCpp:
|
||||
result = if useCpp(conf, cfile):
|
||||
getEnv("CXX")
|
||||
else:
|
||||
getEnv("CC")
|
||||
else:
|
||||
result = if isCpp:
|
||||
result = if useCpp(conf, cfile):
|
||||
CC[compiler].cppCompiler
|
||||
else:
|
||||
CC[compiler].compilerExe
|
||||
@@ -537,36 +512,47 @@ proc ccHasSaneOverflow*(conf: ConfigRef): bool =
|
||||
result = false # assume an old or crappy GCC
|
||||
var exe = getConfigVar(conf, conf.cCompiler, ".exe")
|
||||
if exe.len == 0: exe = CC[conf.cCompiler].compilerExe
|
||||
# NOTE: should we need the full version, use -dumpfullversion
|
||||
let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except IOError, OSError, ValueError: ("", 1)
|
||||
let (s, exitCode) = try: execCmdEx(exe & " --version") except: ("", 1)
|
||||
if exitCode == 0:
|
||||
var major: int
|
||||
discard parseInt(s, major)
|
||||
result = major >= 5
|
||||
var i = 0
|
||||
var j = 0
|
||||
# the version is the last part of the first line:
|
||||
while i < s.len and s[i] != '\n':
|
||||
if s[i] in {' ', '\t'}: j = i+1
|
||||
inc i
|
||||
if j > 0:
|
||||
var major = 0
|
||||
while j < s.len and s[j] in {'0'..'9'}:
|
||||
major = major * 10 + (ord(s[j]) - ord('0'))
|
||||
inc j
|
||||
if i < s.len and s[j] == '.': inc j
|
||||
while j < s.len and s[j] in {'0'..'9'}:
|
||||
inc j
|
||||
if j+1 < s.len and s[j] == '.' and s[j+1] in {'0'..'9'}:
|
||||
# we found a third version number, chances are high
|
||||
# we really parsed the version:
|
||||
result = major >= 5
|
||||
else:
|
||||
result = conf.cCompiler == ccCLang
|
||||
|
||||
proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
|
||||
result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
|
||||
else: getCompilerExe(conf, compiler, optMixedMode in conf.globalOptions or conf.backend == backendCpp)
|
||||
elif optMixedMode in conf.globalOptions and conf.backend != backendCpp: CC[compiler].cppCompiler
|
||||
else: getCompilerExe(conf, compiler, AbsoluteFile"")
|
||||
|
||||
proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
|
||||
isMainFile = false; produceOutput = false): string =
|
||||
let
|
||||
c = conf.cCompiler
|
||||
isCpp = useCpp(conf, cfile.cname)
|
||||
let c = conf.cCompiler
|
||||
# We produce files like module.nim.cpp, so the absolute Nim filename is not
|
||||
# cfile.name but `cfile.cname.changeFileExt("")`:
|
||||
var options = cFileSpecificOptions(conf, cfile.nimname, cfile.cname.changeFileExt("").string)
|
||||
if isCpp:
|
||||
if useCpp(conf, cfile.cname):
|
||||
# needs to be prepended so that --passc:-std=c++17 can override default.
|
||||
# we could avoid allocation by making cFileSpecificOptions inplace
|
||||
options = CC[c].cppXsupport & ' ' & options
|
||||
# If any C++ file was compiled, we need to use C++ driver for linking as well
|
||||
incl conf.globalOptions, optMixedMode
|
||||
|
||||
var exe = getConfigVar(conf, c, ".exe")
|
||||
if exe.len == 0: exe = getCompilerExe(conf, c, isCpp)
|
||||
if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname)
|
||||
|
||||
if needsExeExt(conf): exe = addFileExt(exe, "exe")
|
||||
if (optGenDynLib in conf.globalOptions or (conf.hcrOn and not isMainFile)) and
|
||||
@@ -586,7 +572,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
|
||||
|
||||
compilePattern = joinPath(conf.cCompilerPath, exe)
|
||||
else:
|
||||
compilePattern = exe
|
||||
compilePattern = getCompilerExe(conf, c, cfile.cname)
|
||||
|
||||
includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
|
||||
|
||||
@@ -628,7 +614,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
|
||||
"for the selected C compiler: " & CC[conf.cCompiler].name)
|
||||
|
||||
result.add(' ')
|
||||
strutils.addf(result, CC[c].compileTmpl, [
|
||||
result.addf(CC[c].compileTmpl, [
|
||||
"dfile", dfile,
|
||||
"file", cfsh, "objfile", quoteShell(objfile),
|
||||
"options", options, "include", includeCmd,
|
||||
@@ -648,7 +634,7 @@ proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
|
||||
proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
|
||||
if conf.backend == backendJs: return false # pre-existing behavior, but not sure it's good
|
||||
|
||||
let hashFile = toGeneratedFile(conf, conf.mangleModuleName(cfile.cname).AbsoluteFile, "sha1")
|
||||
let hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1")
|
||||
let currentHash = footprint(conf, cfile)
|
||||
var f: File
|
||||
if open(f, hashFile.string, fmRead):
|
||||
@@ -663,10 +649,8 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
|
||||
close(f)
|
||||
|
||||
proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
|
||||
# we want to generate the hash file unconditionally
|
||||
let extFileChanged = externalFileChanged(conf, c)
|
||||
if optForceFullMake notin conf.globalOptions and fileExists(c.obj) and
|
||||
not extFileChanged:
|
||||
not externalFileChanged(conf, c):
|
||||
c.flags.incl CfileFlag.Cached
|
||||
else:
|
||||
# make sure Nim keeps recompiling the external file on reruns
|
||||
@@ -686,8 +670,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
||||
if removeStaticFile:
|
||||
removeFile output # fixes: bug #16947
|
||||
result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(output),
|
||||
"objfiles", objfiles,
|
||||
"vccplatform", vccplatform(conf)]
|
||||
"objfiles", objfiles]
|
||||
else:
|
||||
var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe")
|
||||
if linkerExe.len == 0: linkerExe = getLinkerExe(conf, conf.cCompiler)
|
||||
@@ -720,7 +703,7 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
|
||||
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
|
||||
"exefile", exefile, "nim", getPrefixDir(conf).string, "lib", conf.libpath.string])
|
||||
result.add ' '
|
||||
strutils.addf(result, linkTmpl, ["builddll", builddll,
|
||||
result.addf(linkTmpl, ["builddll", builddll,
|
||||
"mapfile", mapfile,
|
||||
"buildgui", buildgui, "options", linkOptions,
|
||||
"objfiles", objfiles, "exefile", exefile,
|
||||
@@ -840,22 +823,10 @@ proc linkViaResponseFile(conf: ConfigRef; cmd: string) =
|
||||
else:
|
||||
writeFile(linkerArgs, args)
|
||||
try:
|
||||
when defined(macosx):
|
||||
execLinkCmd(conf, "xargs " & cmd.substr(0, last) & " < " & linkerArgs)
|
||||
else:
|
||||
execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
|
||||
execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
|
||||
finally:
|
||||
removeFile(linkerArgs)
|
||||
|
||||
proc linkViaShellScript(conf: ConfigRef; cmd: string) =
|
||||
let linkerScript = conf.projectName & "_" & "linkerScript.sh"
|
||||
writeFile(linkerScript, cmd)
|
||||
let shell = getEnv("SHELL")
|
||||
try:
|
||||
execLinkCmd(conf, shell & " " & linkerScript)
|
||||
finally:
|
||||
removeFile(linkerScript)
|
||||
|
||||
proc getObjFilePath(conf: ConfigRef, f: Cfile): string =
|
||||
if noAbsolutePaths(conf): f.obj.extractFilename
|
||||
else: f.obj.string
|
||||
@@ -869,23 +840,9 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu
|
||||
proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string =
|
||||
if conf.hasHint(hintCC):
|
||||
if optListCmd in conf.globalOptions or conf.verbosity > 1:
|
||||
result = MsgKindToStr[hintCC] % (demangleModuleName(path.splitFile.name) & ": " & compileCmd)
|
||||
result = MsgKindToStr[hintCC] % (demanglePackageName(path.splitFile.name) & ": " & compileCmd)
|
||||
else:
|
||||
result = MsgKindToStr[hintCC] % demangleModuleName(path.splitFile.name)
|
||||
|
||||
proc preventLinkCmdMaxCmdLen(conf: ConfigRef, linkCmd: string) =
|
||||
# Prevent linkcmd from exceeding the maximum command line length.
|
||||
# Windows's command line limit is about 8K (8191 characters) so C compilers on
|
||||
# Windows support a feature where the command line can be passed via ``@linkcmd``
|
||||
# to them.
|
||||
const MaxCmdLen = when defined(windows): 8_000 elif defined(macosx): 260_000 else: 32_000
|
||||
if linkCmd.len > MaxCmdLen:
|
||||
when defined(macosx):
|
||||
linkViaShellScript(conf, linkCmd)
|
||||
else:
|
||||
linkViaResponseFile(conf, linkCmd)
|
||||
else:
|
||||
execLinkCmd(conf, linkCmd)
|
||||
result = MsgKindToStr[hintCC] % demanglePackageName(path.splitFile.name)
|
||||
|
||||
proc callCCompiler*(conf: ConfigRef) =
|
||||
var
|
||||
@@ -895,7 +852,7 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||
return # speed up that call if only compiling and no script shall be
|
||||
# generated
|
||||
#var c = cCompiler
|
||||
var script: Rope = ""
|
||||
var script: Rope = nil
|
||||
var cmds: TStringSeq
|
||||
var prettyCmds: TStringSeq
|
||||
let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
|
||||
@@ -958,12 +915,19 @@ proc callCCompiler*(conf: ConfigRef) =
|
||||
objfiles.add(' ')
|
||||
objfiles.add(quoteShell(objFile))
|
||||
let mainOutput = if optGenScript notin conf.globalOptions: conf.prepareToWriteOutput
|
||||
else: AbsoluteFile(conf.outFile)
|
||||
else: AbsoluteFile(conf.projectName)
|
||||
|
||||
linkCmd = getLinkCmd(conf, mainOutput, objfiles, removeStaticFile = true)
|
||||
extraCmds = getExtraCmds(conf, mainOutput)
|
||||
if optCompileOnly notin conf.globalOptions:
|
||||
preventLinkCmdMaxCmdLen(conf, linkCmd)
|
||||
const MaxCmdLen = when defined(windows): 8_000 else: 32_000
|
||||
if linkCmd.len > MaxCmdLen:
|
||||
# Windows's command line limit is about 8K (don't laugh...) so C compilers on
|
||||
# Windows support a feature where the command line can be passed via ``@linkcmd``
|
||||
# to them.
|
||||
linkViaResponseFile(conf, linkCmd)
|
||||
else:
|
||||
execLinkCmd(conf, linkCmd)
|
||||
for cmd in extraCmds:
|
||||
execExternalProgram(conf, cmd, hintExecuting)
|
||||
else:
|
||||
@@ -998,7 +962,7 @@ type BuildCache = object
|
||||
depfiles: seq[(string, string)]
|
||||
nimexe: string
|
||||
|
||||
proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
|
||||
proc writeJsonBuildInstructions*(conf: ConfigRef) =
|
||||
var linkFiles = collect(for it in conf.externalToLink:
|
||||
var it = it
|
||||
if conf.noAbsolutePaths: it = it.extractFilename
|
||||
@@ -1019,14 +983,10 @@ proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
|
||||
currentDir: getCurrentDir())
|
||||
if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"):
|
||||
bcache.cmdline = conf.commandLine
|
||||
for it in conf.m.fileInfos:
|
||||
bcache.depfiles = collect(for it in conf.m.fileInfos:
|
||||
let path = it.fullPath.string
|
||||
if isAbsolute(path): # TODO: else?
|
||||
if path in deps:
|
||||
bcache.depfiles.add (path, deps[path])
|
||||
else: # backup for configs etc.
|
||||
bcache.depfiles.add (path, $secureHashFile(path))
|
||||
|
||||
(path, $secureHashFile(path)))
|
||||
bcache.nimexe = hashNimExe()
|
||||
conf.jsonBuildFile = conf.jsonBuildInstructionsFile
|
||||
conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
|
||||
@@ -1051,7 +1011,7 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute
|
||||
proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||
var bcache: BuildCache
|
||||
try: bcache.fromJson(jsonFile.string.parseFile)
|
||||
except ValueError, KeyError, JsonKindError:
|
||||
except:
|
||||
let e = getCurrentException()
|
||||
conf.quitOrRaise "\ncaught exception:\n$#\nstacktrace:\n$#error evaluating JSON file: $#" %
|
||||
[e.msg, e.getStackTrace(), jsonFile.string]
|
||||
@@ -1068,7 +1028,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
|
||||
cmds.add cmd
|
||||
prettyCmds.add displayProgressCC(conf, name, cmd)
|
||||
execCmdsInParallel(conf, cmds, prettyCb)
|
||||
preventLinkCmdMaxCmdLen(conf, bcache.linkcmd)
|
||||
execLinkCmd(conf, bcache.linkcmd)
|
||||
for cmd in bcache.extraCmds: execExternalProgram(conf, cmd, hintExecuting)
|
||||
|
||||
proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope =
|
||||
|
||||
@@ -9,14 +9,9 @@
|
||||
|
||||
## Module that implements ``gorge`` for the compiler.
|
||||
|
||||
import msgs, os, osproc, streams, options,
|
||||
import msgs, std / sha1, os, osproc, streams, options,
|
||||
lineinfos, pathutils
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/syncio
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
|
||||
proc readOutput(p: Process): (string, int) =
|
||||
result[0] = ""
|
||||
var output = p.outputStream
|
||||
@@ -51,11 +46,7 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str
|
||||
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)
|
||||
if not readSuccessful: result = ("", -1)
|
||||
else:
|
||||
try:
|
||||
var p = startProcess(cmd, workingDir,
|
||||
@@ -66,7 +57,4 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str
|
||||
result = p.readOutput
|
||||
p.close()
|
||||
except IOError, OSError:
|
||||
when defined(nimLegacyGorgeErrors):
|
||||
result = ("", -1)
|
||||
else:
|
||||
result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
|
||||
result = ("", -1)
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
|
||||
saturate, modulegraphs, options, lineinfos, int128
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
const
|
||||
someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
|
||||
mEqStr, mEqSet, mEqCString}
|
||||
@@ -68,12 +65,9 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
|
||||
case n.kind
|
||||
of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv:
|
||||
n = n[0]
|
||||
of nkDerefExpr:
|
||||
of nkDerefExpr, nkHiddenDeref:
|
||||
n = n[0]
|
||||
inc derefs
|
||||
of nkHiddenDeref:
|
||||
n = n[0]
|
||||
if not isApprox: inc derefs
|
||||
of nkBracketExpr:
|
||||
if isConstExpr(n[1]) or isLet(n[1]) or isConstExpr(n[1].skipConv):
|
||||
n = n[0]
|
||||
@@ -203,7 +197,7 @@ proc highBound*(conf: ConfigRef; x: PNode; o: Operators): PNode =
|
||||
nkIntLit.newIntNode(lastOrd(conf, typ))
|
||||
elif typ.kind == tySequence and x.kind == nkSym and
|
||||
x.sym.kind == skConst:
|
||||
nkIntLit.newIntNode(x.sym.astdef.len-1)
|
||||
nkIntLit.newIntNode(x.sym.ast.len-1)
|
||||
else:
|
||||
o.opAdd.buildCall(o.opLen.buildCall(x), minusOne())
|
||||
result.info = x.info
|
||||
@@ -1047,12 +1041,8 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
|
||||
if check[1].kind == nkCurly:
|
||||
result = copyTree(check)
|
||||
if access.kind == nkDotExpr:
|
||||
# change the access to the discriminator field access
|
||||
var a = copyTree(access)
|
||||
# set field name to discriminator field name
|
||||
a[1] = check[2]
|
||||
# set discriminator field type: important for `neg`
|
||||
a.typ = check[2].typ
|
||||
result[2] = a
|
||||
# 'access.kind != nkDotExpr' can happen for object constructors
|
||||
# which we don't check yet
|
||||
@@ -1061,12 +1051,8 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
|
||||
assert check.getMagic == mNot
|
||||
result = buildProperFieldCheck(access, check[1], o).neg(o)
|
||||
|
||||
proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef; produceError: bool) =
|
||||
proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) =
|
||||
for i in 1..<n.len:
|
||||
let check = buildProperFieldCheck(n[0], n[i], m.g.operators)
|
||||
if check != nil and m.doesImply(check) != impYes:
|
||||
if produceError:
|
||||
localError(conf, n.info, "field access outside of valid case branch: " & renderTree(n[0]))
|
||||
else:
|
||||
message(conf, n.info, warnProveField, renderTree(n[0]))
|
||||
break
|
||||
message(conf, n.info, warnProveField, renderTree(n[0])); break
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#
|
||||
|
||||
# This include implements the high level optimization pass.
|
||||
# included from sem.nim
|
||||
|
||||
proc hlo(c: PContext, n: PNode): PNode
|
||||
|
||||
@@ -68,7 +67,7 @@ proc hlo(c: PContext, n: PNode): PNode =
|
||||
# already processed (special cases in semstmts.nim)
|
||||
result = n
|
||||
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
|
||||
{sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
|
||||
# do not optimize 'var g {.global} = re(...)' again!
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
## A BiTable is a table that can be seen as an optimized pair
|
||||
## of `(Table[LitId, Val], Table[Val, LitId])`.
|
||||
## of (Table[LitId, Val], Table[Val, LitId]).
|
||||
|
||||
import hashes, rodfiles
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
LitId* = distinct uint32
|
||||
|
||||
@@ -93,13 +90,13 @@ proc getOrIncl*[T](t: var BiTable[T]; v: T): LitId =
|
||||
t.vals.add v
|
||||
|
||||
|
||||
proc `[]`*[T](t: var BiTable[T]; litId: LitId): var T {.inline.} =
|
||||
let idx = idToIdx litId
|
||||
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
|
||||
proc `[]`*[T](t: BiTable[T]; LitId: LitId): lent T {.inline.} =
|
||||
let idx = idToIdx LitId
|
||||
assert idx < t.vals.len
|
||||
result = t.vals[idx]
|
||||
|
||||
|
||||
@@ -20,11 +20,8 @@
|
||||
|
||||
import std/packedsets, algorithm, tables
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen,
|
||||
pathutils, extccomp, msgs, modulepaths]
|
||||
pathutils, extccomp, msgs]
|
||||
|
||||
import packed_ast, ic, dce, rodfiles
|
||||
|
||||
@@ -50,10 +47,7 @@ proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var Alive
|
||||
let n = unpackTree(g, m.module.position, m.fromDisk.topLevel, p)
|
||||
cgen.genTopLevelStmt(bmod, n)
|
||||
|
||||
let disps = finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
|
||||
if disps != nil:
|
||||
for disp in disps:
|
||||
genProcAux(bmod, disp.sym)
|
||||
finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
|
||||
m.fromDisk.backendFlags = cgen.whichInitProcs(bmod)
|
||||
|
||||
proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
|
||||
@@ -67,8 +61,7 @@ proc addFileToLink(config: ConfigRef; m: PSym) =
|
||||
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 cfile = changeFileExt(completeCfilePath(config, withPackageName(config, filename)), ext)
|
||||
let objFile = completeCfilePath(config, toObjFile(config, cfile))
|
||||
if fileExists(objFile):
|
||||
var cf = Cfile(nimname: m.name.s, cname: cfile,
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
## 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
|
||||
|
||||
@@ -7,18 +7,13 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
import hashes, tables, intsets
|
||||
import hashes, tables, intsets, std/sha1
|
||||
import packed_ast, bitabs, rodfiles
|
||||
import ".." / [ast, idents, lineinfos, msgs, ropes, options,
|
||||
pathutils, condsyms, packages, modulepaths]
|
||||
pathutils, condsyms]
|
||||
#import ".." / [renderer, astalgo]
|
||||
from os import removeFile, isAbsolute
|
||||
|
||||
import ../../dist/checksums/src/checksums/sha1
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions, formatfloat]
|
||||
|
||||
type
|
||||
PackedConfig* = object
|
||||
backend: TBackend
|
||||
@@ -45,10 +40,11 @@ type
|
||||
reexports*: seq[(LitId, PackedItemId)]
|
||||
compilerProcs*: seq[(LitId, int32)]
|
||||
converters*, methods*, trmacros*, pureEnums*: seq[int32]
|
||||
macroUsages*: seq[(PackedItemId, PackedLineInfo)]
|
||||
|
||||
typeInstCache*: seq[(PackedItemId, PackedItemId)]
|
||||
procInstCache*: seq[PackedInstantiation]
|
||||
attachedOps*: seq[(PackedItemId, TTypeAttachedOp, PackedItemId)]
|
||||
attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)]
|
||||
methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
|
||||
enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
|
||||
|
||||
@@ -356,7 +352,7 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
|
||||
|
||||
var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv,
|
||||
size: t.size, align: t.align, nonUniqueId: t.itemId.item,
|
||||
paddingAtEnd: t.paddingAtEnd)
|
||||
paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel)
|
||||
storeNode(p, t, n)
|
||||
p.typeInst = t.typeInst.storeType(c, m)
|
||||
for kid in items t.sons:
|
||||
@@ -374,7 +370,7 @@ proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib
|
||||
if l.isNil: return
|
||||
result.kind = l.kind
|
||||
result.generated = l.generated
|
||||
result.isOverridden = l.isOverridden
|
||||
result.isOverriden = l.isOverriden
|
||||
result.name = toLitId($l.name, m)
|
||||
storeNode(result, l, path)
|
||||
|
||||
@@ -392,7 +388,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
|
||||
assert sfForward notin s.flags
|
||||
|
||||
var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic,
|
||||
position: s.position, offset: s.offset, disamb: s.disamb, options: s.options,
|
||||
position: s.position, offset: s.offset, options: s.options,
|
||||
name: s.name.s.toLitId(m))
|
||||
|
||||
storeNode(p, s, ast)
|
||||
@@ -404,7 +400,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
|
||||
p.bitsize = s.bitsize
|
||||
p.alignment = s.alignment
|
||||
|
||||
p.externalName = toLitId(s.loc.r, m)
|
||||
p.externalName = toLitId(if s.loc.r.isNil: "" else: $s.loc.r, m)
|
||||
p.locFlags = s.loc.flags
|
||||
c.addMissing s.typ
|
||||
p.typ = s.typ.storeType(c, m)
|
||||
@@ -421,7 +417,7 @@ proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var Pac
|
||||
## add a remote symbol reference to the tree
|
||||
let info = n.info.toPackedInfo(c, m)
|
||||
ir.nodes.add PackedNode(kind: nkModuleRef, operand: 3.int32, # spans 3 nodes in total
|
||||
typeId: storeTypeLater(n.typ, c, m), info: info, flags: n.flags)
|
||||
typeId: storeTypeLater(n.typ, c, m), info: info)
|
||||
ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
|
||||
operand: toLitId(n.sym.itemId.module.FileIndex, c, m).int32)
|
||||
ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
|
||||
@@ -530,15 +526,6 @@ proc toPackedGeneratedProcDef*(s: PSym, encoder: var PackedEncoder; m: var Packe
|
||||
toPackedProcDef(s.ast, m.topLevel, encoder, m)
|
||||
#flush encoder, m
|
||||
|
||||
proc storeAttachedProcDef*(t: PType; op: TTypeAttachedOp; s: PSym,
|
||||
encoder: var PackedEncoder; m: var PackedModule) =
|
||||
assert s.kind in routineKinds
|
||||
assert isActive(encoder)
|
||||
let tid = storeTypeLater(t, encoder, m)
|
||||
let sid = storeSymLater(s, encoder, m)
|
||||
m.attachedOps.add (tid, op, sid)
|
||||
toPackedGeneratedProcDef(s, encoder, m)
|
||||
|
||||
proc storeInstantiation*(c: var PackedEncoder; m: var PackedModule; s: PSym; i: PInstantiation) =
|
||||
var t = newSeq[PackedItemId](i.concreteTypes.len)
|
||||
for j in 0..high(i.concreteTypes):
|
||||
@@ -561,10 +548,6 @@ proc loadError(err: RodFileError; filename: AbsoluteFile; config: ConfigRef;) =
|
||||
rawMessage(config, warnCannotOpenFile, filename.string & " reason: " & $err)
|
||||
#echo "Error: ", $err, " loading file: ", filename.string
|
||||
|
||||
proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile =
|
||||
result = changeFileExt(completeGeneratedFilePath(conf,
|
||||
mangleModuleName(conf, f).AbsoluteFile), ext)
|
||||
|
||||
proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef;
|
||||
ignoreConfig = false): RodFileError =
|
||||
var f = rodfiles.open(filename.string)
|
||||
@@ -607,6 +590,7 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
|
||||
loadSeqSection convertersSection, m.converters
|
||||
loadSeqSection methodsSection, m.methods
|
||||
loadSeqSection pureEnumsSection, m.pureEnums
|
||||
loadSeqSection macroUsagesSection, m.macroUsages
|
||||
|
||||
loadSeqSection toReplaySection, m.toReplay.nodes
|
||||
loadSeqSection topLevelSection, m.topLevel.nodes
|
||||
@@ -670,6 +654,7 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
|
||||
storeSeqSection convertersSection, m.converters
|
||||
storeSeqSection methodsSection, m.methods
|
||||
storeSeqSection pureEnumsSection, m.pureEnums
|
||||
storeSeqSection macroUsagesSection, m.macroUsages
|
||||
|
||||
storeSeqSection toReplaySection, m.toReplay.nodes
|
||||
storeSeqSection topLevelSection, m.topLevel.nodes
|
||||
@@ -768,8 +753,6 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
|
||||
result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId])
|
||||
of nkSym:
|
||||
result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree.nodes[n.int].operand))
|
||||
if result.typ == nil:
|
||||
result.typ = result.sym.typ
|
||||
of directIntLit:
|
||||
result.intVal = tree.nodes[n.int].operand
|
||||
of externIntLit:
|
||||
@@ -784,8 +767,6 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
|
||||
assert n2.kind == nkInt32Lit
|
||||
transitionNoneToSym(result)
|
||||
result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
|
||||
if result.typ == nil:
|
||||
result.typ = result.sym.typ
|
||||
else:
|
||||
for n0 in sonsReadonly(tree, n):
|
||||
result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
|
||||
@@ -836,7 +817,6 @@ proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
options: s.options,
|
||||
position: if s.kind in {skForVar, skVar, skLet, skTemp}: 0 else: s.position,
|
||||
offset: if s.kind in routineKinds: defaultOffset else: s.offset,
|
||||
disamb: s.disamb,
|
||||
name: getIdent(c.cache, g[si].fromDisk.strings[s.name])
|
||||
)
|
||||
|
||||
@@ -854,7 +834,7 @@ proc loadLib(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
if l.name.int == 0:
|
||||
result = nil
|
||||
else:
|
||||
result = PLib(generated: l.generated, isOverridden: l.isOverridden,
|
||||
result = PLib(generated: l.generated, isOverriden: l.isOverriden,
|
||||
kind: l.kind, name: rope g[si].fromDisk.strings[l.name])
|
||||
loadAstBody(l, path)
|
||||
|
||||
@@ -907,7 +887,7 @@ proc typeHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
|
||||
t: PackedType; si, item: int32): PType =
|
||||
result = PType(itemId: ItemId(module: si, item: t.nonUniqueId), kind: t.kind,
|
||||
flags: t.flags, size: t.size, align: t.align,
|
||||
paddingAtEnd: t.paddingAtEnd,
|
||||
paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel,
|
||||
uniqueId: ItemId(module: si, item: item),
|
||||
callConv: t.callConv)
|
||||
|
||||
@@ -947,6 +927,17 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t
|
||||
result = g[si].types[t.item]
|
||||
assert result.itemId.item > 0
|
||||
|
||||
proc newPackage(config: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym =
|
||||
let filename = AbsoluteFile toFullPath(config, fileIdx)
|
||||
let name = getIdent(cache, splitFile(filename).name)
|
||||
let info = newLineInfo(fileIdx, 1, 1)
|
||||
let
|
||||
pck = getPackageName(config, filename.string)
|
||||
pck2 = if pck.len > 0: pck else: "unknown"
|
||||
pack = getIdent(cache, pck2)
|
||||
result = newSym(skPackage, getIdent(cache, pck2),
|
||||
ItemId(module: PackageModuleId, item: int32(fileIdx)), nil, info)
|
||||
|
||||
proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
||||
fileIdx: FileIndex; m: var LoadedModule) =
|
||||
m.iface = initTable[PIdent, seq[PackedItemId]]()
|
||||
@@ -974,7 +965,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa
|
||||
name: getIdent(cache, splitFile(filename).name),
|
||||
info: newLineInfo(fileIdx, 1, 1),
|
||||
position: int(fileIdx))
|
||||
m.module.owner = getPackage(conf, cache, fileIdx)
|
||||
m.module.owner = newPackage(conf, cache, fileIdx)
|
||||
m.module.flags = m.fromDisk.moduleFlags
|
||||
|
||||
proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
|
||||
|
||||
@@ -11,10 +11,6 @@
|
||||
## The set must cover a complete Nim project.
|
||||
|
||||
import sets
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import ".." / [ast, modulegraphs]
|
||||
import packed_ast, bitabs, ic
|
||||
|
||||
|
||||
@@ -16,9 +16,6 @@ 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
|
||||
|
||||
|
||||
@@ -16,9 +16,6 @@ import hashes, tables, strtabs
|
||||
import bitabs
|
||||
import ".." / [ast, options]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
SymId* = distinct int32
|
||||
ModuleId* = distinct int32
|
||||
@@ -45,7 +42,7 @@ type
|
||||
PackedLib* = object
|
||||
kind*: TLibKind
|
||||
generated*: bool
|
||||
isOverridden*: bool
|
||||
isOverriden*: bool
|
||||
name*: LitId
|
||||
path*: NodeId
|
||||
|
||||
@@ -63,8 +60,7 @@ type
|
||||
alignment*: int # for alignment
|
||||
options*: TOptions
|
||||
position*: int
|
||||
offset*: int32
|
||||
disamb*: int32
|
||||
offset*: int
|
||||
externalName*: LitId # instead of TLoc
|
||||
locFlags*: TLocFlags
|
||||
annex*: PackedLib
|
||||
@@ -85,6 +81,7 @@ type
|
||||
size*: BiggestInt
|
||||
align*: int16
|
||||
paddingAtEnd*: int16
|
||||
lockLevel*: TLockLevel # lock level as required for deadlock checking
|
||||
# not serialized: loc*: TLoc because it is backend-specific
|
||||
typeInst*: PackedItemId
|
||||
nonUniqueId*: int32
|
||||
|
||||
@@ -16,9 +16,6 @@ import ".." / [ast, modulegraphs, trees, extccomp, btrees,
|
||||
|
||||
import tables
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import packed_ast, ic, bitabs
|
||||
|
||||
proc replayStateChanges*(module: PSym; g: ModuleGraph) =
|
||||
@@ -89,20 +86,6 @@ proc replayStateChanges*(module: PSym; g: ModuleGraph) =
|
||||
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
|
||||
@@ -134,7 +117,11 @@ proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
|
||||
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.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)
|
||||
|
||||
for it in mitems(g.packed[module].fromDisk.methods):
|
||||
let sym = loadSymFromId(g.config, g.cache, g.packed, module,
|
||||
|
||||
@@ -16,15 +16,12 @@
|
||||
|
||||
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
|
||||
## and preceeded by a header (see: `cookie`). The precise layout, section
|
||||
## ordering and data following the section are determined by the user. See
|
||||
## `ic.loadRodFile`.
|
||||
##
|
||||
@@ -37,7 +34,8 @@ when defined(nimPreviewSlimSystem):
|
||||
##
|
||||
## Now read the bits below to understand what's missing.
|
||||
##
|
||||
## ### Issues with the Example
|
||||
## 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` &
|
||||
@@ -84,6 +82,7 @@ type
|
||||
convertersSection
|
||||
methodsSection
|
||||
pureEnumsSection
|
||||
macroUsagesSection
|
||||
toReplaySection
|
||||
topLevelSection
|
||||
bodiesSection
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
import
|
||||
hashes, wordrecg
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
PIdent* = ref TIdent
|
||||
TIdent*{.acyclic.} = object
|
||||
|
||||
@@ -15,9 +15,6 @@ import
|
||||
modulegraphs, wordrecg, tables
|
||||
from strutils import `%`
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
proc readExceptSet*(c: PContext, n: PNode): IntSet =
|
||||
assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
|
||||
result = initIntSet()
|
||||
@@ -141,7 +138,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
|
||||
# for an enumeration we have to add all identifiers
|
||||
if multiImport:
|
||||
# for a overloadable syms add all overloaded routines
|
||||
var it: ModuleIter = default(ModuleIter)
|
||||
var it: ModuleIter
|
||||
var e = initModuleIter(it, c.graph, fromMod, s.name)
|
||||
while e != nil:
|
||||
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
|
||||
@@ -188,13 +185,11 @@ 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)
|
||||
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)
|
||||
addPattern(c, it)
|
||||
for it in mitems c.graph.ifaces[fromMod.position].pureEnums:
|
||||
if filter:
|
||||
loadPackedSym(c.graph, it)
|
||||
@@ -202,7 +197,7 @@ template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} =
|
||||
|
||||
proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
|
||||
c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet)
|
||||
addUnnamedIt(c, fromMod, it.sym.name.id notin exceptSet)
|
||||
addUnnamedIt(c, fromMod, it.sym.id notin exceptSet)
|
||||
|
||||
proc importAllSymbols*(c: PContext, fromMod: PSym) =
|
||||
c.addImport ImportedModule(m: fromMod, mode: importAll)
|
||||
@@ -228,15 +223,10 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
|
||||
for i in 0..n.safeLen-1:
|
||||
importForwarded(c, n[i], exceptSet, fromMod, importSet)
|
||||
|
||||
proc addUnique[T](x: var seq[T], y: sink T) {.noSideEffect.} =
|
||||
for i in 0..high(x):
|
||||
if x[i] == y: return
|
||||
x.add y
|
||||
|
||||
proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
|
||||
result = realModule
|
||||
template createModuleAliasImpl(ident): untyped =
|
||||
createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
|
||||
createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options)
|
||||
if n.kind != nkImportAs: discard
|
||||
elif n.len != 2 or n[1].kind != nkIdent:
|
||||
localError(c.config, n.info, "module alias must be an identifier")
|
||||
@@ -250,7 +240,6 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool)
|
||||
result.options.incl optImportHidden
|
||||
c.unusedImports.add((result, n.info))
|
||||
c.importModuleMap[result.id] = realModule.id
|
||||
c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
|
||||
|
||||
proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
|
||||
var ret: typeof(result)
|
||||
@@ -304,15 +293,7 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
|
||||
var prefix = ""
|
||||
if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
|
||||
message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
|
||||
|
||||
proc suggestMod(n: PNode; s: PSym) =
|
||||
if n.kind == nkImportAs:
|
||||
suggestMod(n[0], realModule)
|
||||
elif n.kind == nkInfix:
|
||||
suggestMod(n[2], s)
|
||||
else:
|
||||
suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
|
||||
suggestMod(n, result)
|
||||
suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
|
||||
importStmtResult.add newSymNode(result, n.info)
|
||||
#newStrNode(toFullPath(c.config, f), n.info)
|
||||
|
||||
@@ -337,26 +318,22 @@ proc evalImport*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkImportStmt, n.info)
|
||||
for i in 0..<n.len:
|
||||
let it = n[i]
|
||||
if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket:
|
||||
let lastPos = it.len - 1
|
||||
var imp = copyNode(it)
|
||||
newSons(imp, it.len)
|
||||
for i in 0 ..< lastPos: imp[i] = it[i]
|
||||
imp[lastPos] = imp[0] # dummy entry, replaced in the loop
|
||||
for x in it[lastPos]:
|
||||
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":
|
||||
var impAs = copyNode(x)
|
||||
newSons(impAs, 3)
|
||||
impAs[0] = x[0]
|
||||
imp[lastPos] = x[1]
|
||||
let impAs = copyTree(x)
|
||||
imp[2] = x[1]
|
||||
impAs[1] = imp
|
||||
impAs[2] = x[2]
|
||||
impAs.info = x[2].info
|
||||
impMod(c, impAs, result)
|
||||
impMod(c, imp, result)
|
||||
else:
|
||||
imp[lastPos] = x
|
||||
imp.info = x.info
|
||||
imp[2] = x
|
||||
impMod(c, imp, result)
|
||||
else:
|
||||
impMod(c, it, result)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ Platforms: """
|
||||
macosx: i386;amd64;powerpc64;arm64
|
||||
solaris: i386;amd64;sparc;sparc64
|
||||
freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
|
||||
netbsd: i386;amd64;arm64
|
||||
netbsd: i386;amd64
|
||||
openbsd: i386;amd64;arm;arm64
|
||||
dragonfly: i386;amd64
|
||||
crossos: amd64
|
||||
@@ -65,12 +65,11 @@ Files: "compiler"
|
||||
Files: "doc"
|
||||
Files: "doc/html"
|
||||
Files: "tools"
|
||||
Files: "tools/debug/nim-gdb.py"
|
||||
Files: "tools/nim-gdb.py"
|
||||
Files: "nimpretty"
|
||||
Files: "testament"
|
||||
Files: "nimsuggest"
|
||||
Files: "nimsuggest/tests/*.nim"
|
||||
Files: "changelogs/*.md"
|
||||
|
||||
[Lib]
|
||||
Files: "lib"
|
||||
@@ -78,7 +77,6 @@ Files: "lib"
|
||||
[Other]
|
||||
Files: "examples"
|
||||
Files: "dist/nimble"
|
||||
Files: "dist/checksums"
|
||||
|
||||
Files: "tests"
|
||||
|
||||
@@ -92,7 +90,6 @@ Files: "bin/nimgrab.exe"
|
||||
Files: "bin/nimpretty.exe"
|
||||
Files: "bin/testament.exe"
|
||||
Files: "bin/nim-gdb.bat"
|
||||
Files: "bin/atlas.exe"
|
||||
|
||||
Files: "koch.exe"
|
||||
Files: "finish.exe"
|
||||
@@ -123,7 +120,6 @@ Files: "bin/nim"
|
||||
InstallScript: "yes"
|
||||
UninstallScript: "yes"
|
||||
Files: "bin/nim-gdb"
|
||||
Files: "build_all.sh"
|
||||
|
||||
|
||||
[InnoSetup]
|
||||
@@ -145,5 +141,5 @@ shortDesc: "The Nim Compiler"
|
||||
licenses: "bin/nim,MIT;lib/*,MIT;"
|
||||
|
||||
[nimble]
|
||||
pkgName: "nim"
|
||||
pkgName: "compiler"
|
||||
pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css"
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
|
||||
from math import trunc
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
Int128* = object
|
||||
udata: array[4, uint32]
|
||||
@@ -33,34 +30,39 @@ 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
|
||||
when T is bool: result.sdata(0) = int32(arg)
|
||||
elif T is SomeUnsignedInt:
|
||||
when sizeof(arg) <= 4:
|
||||
result.udata[0] = uint32(arg)
|
||||
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
|
||||
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
|
||||
|
||||
template isNegative(arg: int32): bool =
|
||||
arg < 0
|
||||
|
||||
proc bitconcat(a, b: uint32): uint64 =
|
||||
(uint64(a) shl 32) or uint64(b)
|
||||
|
||||
proc bitsplit(a: uint64): (uint32, uint32) =
|
||||
(cast[uint32](a shr 32), cast[uint32](a))
|
||||
|
||||
proc toInt64*(arg: Int128): int64 =
|
||||
if isNegative(arg):
|
||||
assert(arg.sdata(3) == -1, "out of range")
|
||||
@@ -206,6 +208,12 @@ proc `==`*(a, b: Int128): bool =
|
||||
if a.udata[3] != b.udata[3]: return false
|
||||
return true
|
||||
|
||||
proc inplaceBitnot(a: var Int128) =
|
||||
a.udata[0] = not a.udata[0]
|
||||
a.udata[1] = not a.udata[1]
|
||||
a.udata[2] = not a.udata[2]
|
||||
a.udata[3] = not a.udata[3]
|
||||
|
||||
proc bitnot*(a: Int128): Int128 =
|
||||
result.udata[0] = not a.udata[0]
|
||||
result.udata[1] = not a.udata[1]
|
||||
@@ -346,10 +354,23 @@ 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])
|
||||
let
|
||||
a = cast[uint64](lhs.udata[0])
|
||||
b = cast[uint64](lhs.udata[1])
|
||||
c = cast[uint64](lhs.udata[2])
|
||||
d = cast[uint64](lhs.udata[3])
|
||||
|
||||
e = cast[uint64](rhs.udata[0])
|
||||
f = cast[uint64](rhs.udata[1])
|
||||
g = cast[uint64](rhs.udata[2])
|
||||
h = cast[uint64](rhs.udata[3])
|
||||
|
||||
|
||||
let a32 = cast[uint64](lhs.udata[1])
|
||||
let a00 = cast[uint64](lhs.udata[0])
|
||||
let b32 = cast[uint64](rhs.udata[1])
|
||||
let b00 = cast[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
|
||||
@@ -420,17 +441,17 @@ proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
|
||||
result.remainder = dividend
|
||||
|
||||
proc `div`*(a, b: Int128): Int128 =
|
||||
let (a, _) = divMod(a, b)
|
||||
let (a, b) = divMod(a, b)
|
||||
return a
|
||||
|
||||
proc `mod`*(a, b: Int128): Int128 =
|
||||
let (_, b) = divMod(a, b)
|
||||
let (a, b) = divMod(a, b)
|
||||
return b
|
||||
|
||||
proc addInt128*(result: var string; value: Int128) =
|
||||
let initialSize = result.len
|
||||
if value == Zero:
|
||||
result.add '0'
|
||||
result.add "0"
|
||||
elif value == low(Int128):
|
||||
result.add "-170141183460469231731687303715884105728"
|
||||
else:
|
||||
@@ -451,8 +472,6 @@ proc addInt128*(result: var string; value: Int128) =
|
||||
j -= 1
|
||||
|
||||
proc `$`*(a: Int128): string =
|
||||
# "-170141183460469231731687303715884105728".len == 41
|
||||
result = newStringOfCap(41)
|
||||
result.addInt128(a)
|
||||
|
||||
proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
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 =
|
||||
@@ -72,91 +69,14 @@ proc isValueOnlyType(t: PType): bool =
|
||||
proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent}
|
||||
result = not types.searchTypeFor(t, wrap)
|
||||
|
||||
type
|
||||
SearchResult = enum
|
||||
NotFound, Abort, Found
|
||||
|
||||
proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult
|
||||
|
||||
proc containsDangerousRefAux(n: PNode; marker: var IntSet): SearchResult =
|
||||
result = NotFound
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
for i in 0..<n.len:
|
||||
result = containsDangerousRefAux(n[i], marker)
|
||||
if result == Found: return result
|
||||
of nkRecCase:
|
||||
assert(n[0].kind == nkSym)
|
||||
result = containsDangerousRefAux(n[0], marker)
|
||||
if result == Found: return result
|
||||
for i in 1..<n.len:
|
||||
case n[i].kind
|
||||
of nkOfBranch, nkElse:
|
||||
result = containsDangerousRefAux(lastSon(n[i]), marker)
|
||||
if result == Found: return result
|
||||
else: discard
|
||||
of nkSym:
|
||||
result = containsDangerousRefAux(n.sym.typ, marker)
|
||||
else: discard
|
||||
|
||||
proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult =
|
||||
result = NotFound
|
||||
if t == nil: return result
|
||||
if containsOrIncl(marker, t.id): return result
|
||||
|
||||
if t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure):
|
||||
result = Found
|
||||
elif tfSendable in t.flags:
|
||||
result = Abort
|
||||
else:
|
||||
# continue the type traversal:
|
||||
result = NotFound
|
||||
|
||||
if result != NotFound: return result
|
||||
case t.kind
|
||||
of tyObject:
|
||||
if t[0] != nil:
|
||||
result = containsDangerousRefAux(t[0].skipTypes(skipPtrs), marker)
|
||||
if result == NotFound: result = containsDangerousRefAux(t.n, marker)
|
||||
of tyGenericInst, tyDistinct, tyAlias, tySink:
|
||||
result = containsDangerousRefAux(lastSon(t), marker)
|
||||
of tyArray, tySet, tyTuple, tySequence:
|
||||
for i in 0..<t.len:
|
||||
result = containsDangerousRefAux(t[i], marker)
|
||||
if result == Found: return result
|
||||
else:
|
||||
discard
|
||||
|
||||
proc containsDangerousRef(t: PType): bool =
|
||||
# a `ref` type is "dangerous" if it occurs not within a type that is like `Isolated[T]`.
|
||||
# For example:
|
||||
# `ref int` # dangerous
|
||||
# `Isolated[ref int]` # not dangerous
|
||||
var marker = initIntSet()
|
||||
result = containsDangerousRefAux(t, marker) == Found
|
||||
|
||||
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
|
||||
# can alias only with unsafeAddr(arg.x) and we don't care if it is not safe
|
||||
result = false
|
||||
else:
|
||||
var marker = initIntSet()
|
||||
result = canAlias(arg, ret, marker)
|
||||
|
||||
const
|
||||
SomeVar = {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
|
||||
|
||||
proc containsVariable(n: PNode): bool =
|
||||
case n.kind
|
||||
of nodesToIgnoreSet:
|
||||
result = false
|
||||
of nkSym:
|
||||
result = n.sym.kind in SomeVar
|
||||
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
|
||||
@@ -175,12 +95,8 @@ proc checkIsolate*(n: PNode): bool =
|
||||
discard "fine, it is isolated already"
|
||||
else:
|
||||
let argType = n[i].typ
|
||||
if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(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.
|
||||
if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType):
|
||||
if argType.canAlias(n.typ):
|
||||
return false
|
||||
result = true
|
||||
of nkIfStmt, nkIfExpr:
|
||||
@@ -209,12 +125,6 @@ proc checkIsolate*(n: PNode): bool =
|
||||
result = checkIsolate(n[^1])
|
||||
else:
|
||||
result = false
|
||||
of nkSym:
|
||||
result = true
|
||||
if n.sym.kind in SomeVar:
|
||||
let argType = n.typ
|
||||
if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType):
|
||||
result = false
|
||||
else:
|
||||
# unanalysable expression:
|
||||
result = false
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,13 +19,13 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
|
||||
s, u: Rope
|
||||
field: PSym
|
||||
b: PNode
|
||||
result = ""
|
||||
result = nil
|
||||
case n.kind
|
||||
of nkRecList:
|
||||
if n.len == 1:
|
||||
result = genObjectFields(p, typ, n[0])
|
||||
else:
|
||||
s = ""
|
||||
s = nil
|
||||
for i in 0..<n.len:
|
||||
if i > 0: s.add(", \L")
|
||||
s.add(genObjectFields(p, typ, n[i]))
|
||||
@@ -44,13 +44,13 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
|
||||
s = genTypeInfo(p, field.typ)
|
||||
for i in 1..<n.len:
|
||||
b = n[i] # branch
|
||||
u = ""
|
||||
u = nil
|
||||
case b.kind
|
||||
of nkOfBranch:
|
||||
if b.len < 2:
|
||||
internalError(p.config, b.info, "genObjectFields; nkOfBranch broken")
|
||||
for j in 0..<b.len - 1:
|
||||
if u != "": u.add(", ")
|
||||
if u != nil: u.add(", ")
|
||||
if b[j].kind == nkRange:
|
||||
u.addf("[$1, $2]", [rope(getOrdValue(b[j][0])),
|
||||
rope(getOrdValue(b[j][1]))])
|
||||
@@ -59,7 +59,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
|
||||
of nkElse:
|
||||
u = rope(lengthOrd(p.config, field.typ))
|
||||
else: internalError(p.config, n.info, "genObjectFields(nkRecCase)")
|
||||
if result != "": result.add(", \L")
|
||||
if result != nil: result.add(", \L")
|
||||
result.addf("[setConstr($1), $2]",
|
||||
[u, genObjectFields(p, typ, lastSon(b))])
|
||||
result = ("{kind: 3, offset: \"$1\", len: $3, " &
|
||||
@@ -84,7 +84,7 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
|
||||
[name, genTypeInfo(p, typ[0].skipTypes(skipPtrs))])
|
||||
|
||||
proc genTupleFields(p: PProc, typ: PType): Rope =
|
||||
var s: Rope = ""
|
||||
var s: Rope = nil
|
||||
for i in 0..<typ.len:
|
||||
if i > 0: s.add(", \L")
|
||||
s.addf("{kind: 1, offset: \"Field$1\", len: 0, " &
|
||||
@@ -102,7 +102,7 @@ proc genTupleInfo(p: PProc, typ: PType, name: Rope) =
|
||||
p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
|
||||
|
||||
proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
|
||||
var s: Rope = ""
|
||||
var s: Rope = nil
|
||||
for i in 0..<typ.n.len:
|
||||
if (typ.n[i].kind != nkSym): internalError(p.config, typ.n.info, "genEnumInfo")
|
||||
let field = typ.n[i].sym
|
||||
@@ -133,7 +133,7 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
|
||||
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
|
||||
[result, rope(ord(t.kind))]
|
||||
prepend(p.g.typeInfo, s)
|
||||
of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet, tyOpenArray:
|
||||
of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet:
|
||||
var s =
|
||||
"var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
|
||||
[result, rope(ord(t.kind))]
|
||||
|
||||
@@ -14,9 +14,6 @@ import
|
||||
idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos,
|
||||
transf, liftdestructors, typeallowed
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
discard """
|
||||
The basic approach is that captured vars need to be put on the heap and
|
||||
that the calling chain needs to be explicitly modelled. Things to consider:
|
||||
@@ -140,7 +137,7 @@ proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator)
|
||||
rawAddSon(result, intType)
|
||||
|
||||
proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
|
||||
result = newSym(skField, getIdent(g.cache, ":state"), idgen, iter, iter.info)
|
||||
result = newSym(skField, getIdent(g.cache, ":state"), nextSymId(idgen), iter, iter.info)
|
||||
result.typ = createClosureIterStateType(g, iter, idgen)
|
||||
|
||||
proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType =
|
||||
@@ -154,7 +151,7 @@ proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym
|
||||
result = iter.ast[resultPos].sym
|
||||
else:
|
||||
# XXX a bit hacky:
|
||||
result = newSym(skResult, getIdent(g.cache, ":result"), idgen, iter, iter.info, {})
|
||||
result = newSym(skResult, getIdent(g.cache, ":result"), nextSymId(idgen), iter, iter.info, {})
|
||||
result.typ = iter.typ[0]
|
||||
incl(result.flags, sfUsed)
|
||||
iter.ast.add newSymNode(result)
|
||||
@@ -245,20 +242,14 @@ proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen
|
||||
if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
|
||||
owner.flags.incl sfInjectDestructors
|
||||
|
||||
proc genCreateEnv(env: PNode): PNode =
|
||||
var c = newNodeIT(nkObjConstr, env.info, env.typ)
|
||||
c.add newNodeIT(nkType, env.info, env.typ)
|
||||
let e = copyTree(env)
|
||||
e.flags.incl nfFirstWrite
|
||||
result = newAsgnStmt(e, c)
|
||||
|
||||
proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
||||
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
|
||||
if liftingHarmful(g.config, owner): return n
|
||||
let iter = n.sym
|
||||
assert iter.isIterator
|
||||
|
||||
result = newNodeIT(nkStmtListExpr, n.info, iter.typ)
|
||||
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
|
||||
|
||||
let hp = getHiddenParam(g, iter)
|
||||
var env: PNode
|
||||
if owner.isIterator:
|
||||
@@ -266,7 +257,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
|
||||
addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen)
|
||||
env = indirectAccess(newSymNode(it), hp, hp.info)
|
||||
else:
|
||||
let e = newSym(skLet, iter.name, idgen, owner, n.info)
|
||||
let e = newSym(skLet, iter.name, nextSymId(idgen), owner, n.info)
|
||||
e.typ = hp.typ
|
||||
e.flags = hp.flags
|
||||
env = newSymNode(e)
|
||||
@@ -274,44 +265,44 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
|
||||
addVar(v, env)
|
||||
result.add(v)
|
||||
# add 'new' statement:
|
||||
#result.add newCall(getSysSym(g, n.info, "internalNew"), env)
|
||||
result.add genCreateEnv(env)
|
||||
result.add newCall(getSysSym(g, n.info, "internalNew"), env)
|
||||
createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
|
||||
result.add makeClosure(g, idgen, iter, env, n.info)
|
||||
|
||||
proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
|
||||
let envParam = getHiddenParam(g, owner)
|
||||
let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
let field = addField(obj, s, g.cache, idgen)
|
||||
addField(obj, s, g.cache, idgen)
|
||||
|
||||
var access = newSymNode(envParam)
|
||||
assert obj.kind == tyObject
|
||||
result = rawIndirectAccess(access, field, s.info)
|
||||
let field = getFieldFromObj(obj, s)
|
||||
if field != nil:
|
||||
result = rawIndirectAccess(access, field, s.info)
|
||||
else:
|
||||
localError(g.config, s.info, "internal error: cannot generate fresh variable")
|
||||
result = access
|
||||
|
||||
# ------------------ new stuff -------------------------------------------
|
||||
|
||||
proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
|
||||
let s = n.sym
|
||||
let isEnv = s.name.id == getIdent(g.cache, ":env").id
|
||||
if illegalCapture(s):
|
||||
localError(g.config, n.info,
|
||||
("'$1' is of type <$2> which cannot be captured as it would violate memory" &
|
||||
" safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
|
||||
" Consider using a <ref $2> which can be captured.") %
|
||||
" safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases") %
|
||||
[s.name.s, typeToString(s.typ), g.config$s.info])
|
||||
elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv):
|
||||
elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags):
|
||||
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
|
||||
[s.name.s, owner.name.s, $owner.typ.callConv])
|
||||
incl(owner.typ.flags, tfCapturesEnv)
|
||||
if not isEnv:
|
||||
owner.typ.callConv = ccClosure
|
||||
owner.typ.callConv = ccClosure
|
||||
|
||||
type
|
||||
DetectionPass = object
|
||||
processed, capturedVars: IntSet
|
||||
ownerToType: Table[int, PType]
|
||||
somethingToDo: bool
|
||||
inTypeOf: bool
|
||||
graph: ModuleGraph
|
||||
idgen: IdGenerator
|
||||
|
||||
@@ -382,7 +373,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
|
||||
if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
|
||||
localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
|
||||
else:
|
||||
let result = newSym(skField, upIdent, c.idgen, obj.owner, obj.owner.info)
|
||||
let result = newSym(skField, upIdent, nextSymId(c.idgen), obj.owner, obj.owner.info)
|
||||
result.typ = fieldType
|
||||
when false:
|
||||
if c.graph.config.selectedGC == gcDestructors:
|
||||
@@ -415,15 +406,12 @@ Consider:
|
||||
|
||||
"""
|
||||
|
||||
proc isTypeOf(n: PNode): bool =
|
||||
n.kind == nkSym and n.sym.magic in {mTypeOf, mType}
|
||||
|
||||
proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
|
||||
var cp = getEnvParam(fn)
|
||||
let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
|
||||
let t = c.getEnvTypeForOwner(owner, info)
|
||||
if cp == nil:
|
||||
cp = newSym(skParam, getIdent(c.graph.cache, paramName), c.idgen, fn, fn.info)
|
||||
cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextSymId(c.idgen), fn, fn.info)
|
||||
incl(cp.flags, sfFromGeneric)
|
||||
cp.typ = t
|
||||
addHiddenParam(fn, cp)
|
||||
@@ -446,27 +434,24 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
if innerProc:
|
||||
if s.isIterator: c.somethingToDo = true
|
||||
if not c.processed.containsOrIncl(s.id):
|
||||
let body = transformBody(c.graph, c.idgen, s, useCache)
|
||||
let body = transformBody(c.graph, c.idgen, s, cache = true)
|
||||
detectCapturedVars(body, s, c)
|
||||
let ow = s.skipGenericOwner
|
||||
let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator
|
||||
let interested = interestingVar(s)
|
||||
if ow == owner:
|
||||
if owner.isIterator:
|
||||
c.somethingToDo = true
|
||||
addClosureParam(c, owner, n.info)
|
||||
if interestingIterVar(s):
|
||||
if not c.capturedVars.contains(s.id):
|
||||
if not c.inTypeOf: c.capturedVars.incl(s.id)
|
||||
if not c.capturedVars.containsOrIncl(s.id):
|
||||
let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
#let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr})
|
||||
|
||||
if s.name.id == getIdent(c.graph.cache, ":state").id:
|
||||
obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
||||
else:
|
||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
||||
addField(obj, s, c.graph.cache, c.idgen)
|
||||
# direct or indirect dependency:
|
||||
elif innerClosure or interested:
|
||||
elif (innerProc and s.typ.callConv == ccClosure) or interestingVar(s):
|
||||
discard """
|
||||
proc outer() =
|
||||
var x: int
|
||||
@@ -483,12 +468,10 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
addClosureParam(c, owner, n.info)
|
||||
#echo "capturing ", n.info
|
||||
# variable 's' is actually captured:
|
||||
if interestingVar(s):
|
||||
if not c.capturedVars.contains(s.id):
|
||||
if not c.inTypeOf: c.capturedVars.incl(s.id)
|
||||
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
|
||||
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
discard addField(obj, s, c.graph.cache, c.idgen)
|
||||
if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
|
||||
let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
|
||||
#getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
|
||||
addField(obj, s, c.graph.cache, c.idgen)
|
||||
# create required upFields:
|
||||
var w = owner.skipGenericOwner
|
||||
if isInnerProc(w) or owner.isIterator:
|
||||
@@ -520,14 +503,9 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
|
||||
detectCapturedVars(n[namePos], owner, c)
|
||||
of nkReturnStmt:
|
||||
detectCapturedVars(n[0], owner, c)
|
||||
of nkIdentDefs:
|
||||
detectCapturedVars(n[^1], owner, c)
|
||||
else:
|
||||
if n.isCallExpr and n[0].isTypeOf:
|
||||
c.inTypeOf = true
|
||||
for i in 0..<n.len:
|
||||
detectCapturedVars(n[i], owner, c)
|
||||
c.inTypeOf = false
|
||||
|
||||
type
|
||||
LiftingPass = object
|
||||
@@ -560,7 +538,7 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
|
||||
result = n
|
||||
|
||||
proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idgen: IdGenerator): PNode =
|
||||
var v = newSym(skVar, getIdent(cache, envName), idgen, owner, info)
|
||||
var v = newSym(skVar, getIdent(cache, envName), nextSymId(idgen), owner, info)
|
||||
v.flags = {sfShadowed, sfGeneratedOp}
|
||||
v.typ = typ
|
||||
result = newSymNode(v)
|
||||
@@ -584,7 +562,7 @@ proc setupEnvVar(owner: PSym; d: var DetectionPass;
|
||||
result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info, d.idgen)
|
||||
c.envVars[owner.id] = result
|
||||
if optOwnedRefs in d.graph.config.globalOptions:
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), d.idgen, owner, info)
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), nextSymId d.idgen, owner, info)
|
||||
v.flags = {sfShadowed, sfGeneratedOp}
|
||||
v.typ = envVarType
|
||||
c.unownedEnvVars[owner.id] = newSymNode(v)
|
||||
@@ -619,7 +597,7 @@ proc rawClosureCreation(owner: PSym;
|
||||
addVar(v, unowned)
|
||||
|
||||
# add 'new' statement:
|
||||
result.add genCreateEnv(env)
|
||||
result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env))
|
||||
if optOwnedRefs in d.graph.config.globalOptions:
|
||||
let unowned = c.unownedEnvVars[owner.id]
|
||||
assert unowned != nil
|
||||
@@ -668,7 +646,7 @@ proc closureCreationForIter(iter: PNode;
|
||||
d: var DetectionPass; c: var LiftingPass): PNode =
|
||||
result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
|
||||
let owner = iter.sym.skipGenericOwner
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, owner, iter.info)
|
||||
var v = newSym(skVar, getIdent(d.graph.cache, envName), nextSymId(d.idgen), owner, iter.info)
|
||||
incl(v.flags, sfShadowed)
|
||||
v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
|
||||
var vnode: PNode
|
||||
@@ -681,7 +659,7 @@ proc closureCreationForIter(iter: PNode;
|
||||
var vs = newNodeI(nkVarSection, iter.info)
|
||||
addVar(vs, vnode)
|
||||
result.add(vs)
|
||||
result.add genCreateEnv(vnode)
|
||||
result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
|
||||
createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
|
||||
|
||||
let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
|
||||
@@ -752,7 +730,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
# echo renderTree(s.getBody, {renderIds})
|
||||
let oldInContainer = c.inContainer
|
||||
c.inContainer = 0
|
||||
var body = transformBody(d.graph, d.idgen, s, dontUseCache)
|
||||
var body = transformBody(d.graph, d.idgen, s, cache = false)
|
||||
body = liftCapturedVars(body, s, d, c)
|
||||
if c.envVars.getOrDefault(s.id).isNil:
|
||||
s.transformedBody = body
|
||||
@@ -797,7 +775,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
n[1] = liftCapturedVars(n[1], owner, d, c)
|
||||
if n[1].kind == nkClosure: result = n[1]
|
||||
of nkReturnStmt:
|
||||
if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
|
||||
if n[0].kind in {nkAsgn, nkFastAsgn}:
|
||||
# we have a `result = result` expression produced by the closure
|
||||
# transform, let's not touch the LHS in order to make the lifting pass
|
||||
# correct when `result` is lifted
|
||||
@@ -807,8 +785,6 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
|
||||
of nkTypeOfExpr:
|
||||
result = n
|
||||
else:
|
||||
if n.isCallExpr and n[0].isTypeOf:
|
||||
return
|
||||
if owner.isIterator:
|
||||
if nfLL in n.flags:
|
||||
# special case 'when nimVm' due to bug #3636:
|
||||
@@ -876,7 +852,7 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType;
|
||||
fn.typ.callConv = oldCC
|
||||
|
||||
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
|
||||
idgen: IdGenerator, force: bool): PNode =
|
||||
idgen: IdGenerator): PNode =
|
||||
# XXX backend == backendJs does not suffice! The compiletime stuff needs
|
||||
# the transformation even when compiling to JS ...
|
||||
|
||||
@@ -885,7 +861,7 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
|
||||
|
||||
if body.kind == nkEmpty or (
|
||||
g.config.backend == backendJs and not isCompileTime) or
|
||||
(fn.skipGenericOwner.kind != skModule and not force):
|
||||
fn.skipGenericOwner.kind != skModule:
|
||||
|
||||
# ignore forward declaration:
|
||||
result = body
|
||||
@@ -960,7 +936,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
|
||||
let iter = op.sym
|
||||
|
||||
let hp = getHiddenParam(g, iter)
|
||||
env = newSym(skLet, iter.name, idgen, owner, body.info)
|
||||
env = newSym(skLet, iter.name, nextSymId(idgen), owner, body.info)
|
||||
env.typ = hp.typ
|
||||
env.flags = hp.flags
|
||||
|
||||
@@ -968,7 +944,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
|
||||
addVar(v, newSymNode(env))
|
||||
result.add(v)
|
||||
# add 'new' statement:
|
||||
result.add genCreateEnv(env.newSymNode)
|
||||
result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
|
||||
createTypeBoundOpsLL(g, env.typ, body.info, idgen, owner)
|
||||
|
||||
elif op.kind == nkStmtListExpr:
|
||||
@@ -988,15 +964,12 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
|
||||
# gather vars in a tuple:
|
||||
var v2 = newNodeI(nkLetSection, body.info)
|
||||
var vpart = newNodeI(if body.len == 3: nkIdentDefs else: nkVarTuple, body.info)
|
||||
if body.len == 3 and body[0].kind == nkVarTuple:
|
||||
vpart = body[0] # fixes for (i,j) in walk() # bug #15924
|
||||
else:
|
||||
for i in 0..<body.len-2:
|
||||
if body[i].kind == nkSym:
|
||||
body[i].sym.transitionToLet()
|
||||
vpart.add body[i]
|
||||
for i in 0..<body.len-2:
|
||||
if body[i].kind == nkSym:
|
||||
body[i].sym.transitionToLet()
|
||||
vpart.add body[i]
|
||||
|
||||
vpart.add newNodeI(nkEmpty, body.info) # no explicit type
|
||||
vpart.add newNodeI(nkEmpty, body.info) # no explicit type
|
||||
if not env.isNil:
|
||||
call[0] = makeClosure(g, idgen, call[0].sym, env.newSymNode, body.info)
|
||||
vpart.add call
|
||||
|
||||
@@ -510,7 +510,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||
rememberSplit(splitComma)
|
||||
wrSpace em
|
||||
of openPars:
|
||||
if tsLeading in tok.spacing and not em.endsInWhite and
|
||||
if tok.strongSpaceA > 0 and not em.endsInWhite and
|
||||
(not em.wasExportMarker or tok.tokType == tkCurlyDotLe):
|
||||
wrSpace em
|
||||
wr(em, $tok.tokType, ltSomeParLe)
|
||||
@@ -528,7 +528,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||
wr(em, $tok.tokType, ltOther)
|
||||
if not em.inquote: wrSpace(em)
|
||||
of tkOpr, tkDotDot:
|
||||
if em.inquote or (tok.spacing == {} and
|
||||
if em.inquote or ((tok.strongSpaceA == 0 and tok.strongSpaceB == 0) and
|
||||
tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]):
|
||||
# bug #9504: remember to not spacify a keyword:
|
||||
lastTokWasTerse = true
|
||||
@@ -538,7 +538,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||
if not em.endsInWhite: wrSpace(em)
|
||||
wr(em, tok.ident.s, ltOpr)
|
||||
template isUnary(tok): bool =
|
||||
tok.spacing == {tsLeading}
|
||||
tok.strongSpaceB == 0 and tok.strongSpaceA > 0
|
||||
|
||||
if not isUnary(tok):
|
||||
rememberSplit(splitBinary)
|
||||
@@ -551,15 +551,11 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
|
||||
if not preventComment:
|
||||
emitComment(em, tok, dontIndent = false)
|
||||
of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
|
||||
if not em.inquote:
|
||||
let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
|
||||
if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em)
|
||||
em.lineSpan = countNewlines(lit)
|
||||
if em.lineSpan > 0: calcCol(em, lit)
|
||||
wr em, lit, ltLit
|
||||
else:
|
||||
if endsInAlpha(em): wrSpace(em)
|
||||
wr em, tok.literal, ltLit
|
||||
let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
|
||||
if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em)
|
||||
em.lineSpan = countNewlines(lit)
|
||||
if em.lineSpan > 0: calcCol(em, lit)
|
||||
wr em, lit, ltLit
|
||||
of tkEof: discard
|
||||
else:
|
||||
let lit = if tok.ident != nil: tok.ident.s else: tok.literal
|
||||
|
||||
@@ -19,10 +19,8 @@ import
|
||||
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
|
||||
wordrecg, lineinfos, pathutils, parseutils
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[assertions, formatfloat]
|
||||
|
||||
const
|
||||
MaxLineLength* = 80 # lines longer than this lead to a warning
|
||||
numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'}
|
||||
SymChars*: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'}
|
||||
SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
|
||||
@@ -92,21 +90,19 @@ type
|
||||
# so that it is the correct default value
|
||||
base2, base8, base16
|
||||
|
||||
TokenSpacing* = enum
|
||||
tsLeading, tsTrailing, tsEof
|
||||
|
||||
Token* = object # a Nim token
|
||||
tokType*: TokType # the type of the token
|
||||
base*: NumericalBase # the numerical base; only valid for int
|
||||
# or float literals
|
||||
spacing*: set[TokenSpacing] # spaces around token
|
||||
indent*: int # the indentation; != -1 if the token has been
|
||||
# preceded with indentation
|
||||
ident*: PIdent # the parsed identifier
|
||||
iNumber*: BiggestInt # the parsed integer literal
|
||||
fNumber*: BiggestFloat # the parsed floating point literal
|
||||
literal*: string # the parsed (string) literal; and
|
||||
# documentation comments are here too
|
||||
Token* = object # a Nim token
|
||||
tokType*: TokType # the type of the token
|
||||
indent*: int # the indentation; != -1 if the token has been
|
||||
# preceded with indentation
|
||||
ident*: PIdent # the parsed identifier
|
||||
iNumber*: BiggestInt # the parsed integer literal
|
||||
fNumber*: BiggestFloat # the parsed floating point literal
|
||||
base*: NumericalBase # the numerical base; only valid for int
|
||||
# or float literals
|
||||
strongSpaceA*: int8 # leading spaces of an operator
|
||||
strongSpaceB*: int8 # trailing spaces of an operator
|
||||
literal*: string # the parsed (string) literal; and
|
||||
# documentation comments are here too
|
||||
line*, col*: int
|
||||
when defined(nimpretty):
|
||||
offsetA*, offsetB*: int # used for pretty printing so that literals
|
||||
@@ -125,8 +121,6 @@ type
|
||||
cache*: IdentCache
|
||||
when defined(nimsuggest):
|
||||
previousToken: TLineInfo
|
||||
tokenEnd*: TLineInfo
|
||||
previousTokenEnd*: TLineInfo
|
||||
config*: ConfigRef
|
||||
|
||||
proc getLineInfo*(L: Lexer, tok: Token): TLineInfo {.inline.} =
|
||||
@@ -176,7 +170,7 @@ proc initToken*(L: var Token) =
|
||||
L.tokType = tkInvalid
|
||||
L.iNumber = 0
|
||||
L.indent = 0
|
||||
L.spacing = {}
|
||||
L.strongSpaceA = 0
|
||||
L.literal = ""
|
||||
L.fNumber = 0.0
|
||||
L.base = base10
|
||||
@@ -189,7 +183,7 @@ proc fillToken(L: var Token) =
|
||||
L.tokType = tkInvalid
|
||||
L.iNumber = 0
|
||||
L.indent = 0
|
||||
L.spacing = {}
|
||||
L.strongSpaceA = 0
|
||||
setLen(L.literal, 0)
|
||||
L.fNumber = 0.0
|
||||
L.base = base10
|
||||
@@ -508,11 +502,11 @@ proc getNumber(L: var Lexer, result: var Token) =
|
||||
of tkUInt16Lit: setNumber result.iNumber, xi and 0xffff
|
||||
of tkUInt32Lit: setNumber result.iNumber, xi and 0xffffffff
|
||||
of tkFloat32Lit:
|
||||
setNumber result.fNumber, (cast[ptr float32](addr(xi)))[]
|
||||
setNumber result.fNumber, (cast[PFloat32](addr(xi)))[]
|
||||
# note: this code is endian neutral!
|
||||
# XXX: Test this on big endian machine!
|
||||
of tkFloat64Lit, tkFloatLit:
|
||||
setNumber result.fNumber, (cast[ptr float64](addr(xi)))[]
|
||||
setNumber result.fNumber, (cast[PFloat64](addr(xi)))[]
|
||||
else: internalError(L.config, getLineInfo(L), "getNumber")
|
||||
|
||||
# Bounds checks. Non decimal literals are allowed to overflow the range of
|
||||
@@ -735,6 +729,10 @@ proc handleCRLF(L: var Lexer, pos: int): int =
|
||||
template registerLine =
|
||||
let col = L.getColNumber(pos)
|
||||
|
||||
when not defined(nimpretty):
|
||||
if col > MaxLineLength:
|
||||
lexMessagePos(L, hintLineTooLong, pos)
|
||||
|
||||
case L.buf[pos]
|
||||
of CR:
|
||||
registerLine()
|
||||
@@ -906,7 +904,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
|
||||
inc(pos)
|
||||
suspicious = true
|
||||
of '\x80'..'\xFF':
|
||||
if c in UnicodeOperatorStartChars and unicodeOprLen(L.buf, pos)[0] != 0:
|
||||
if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and unicodeOprLen(L.buf, pos)[0] != 0:
|
||||
break
|
||||
else:
|
||||
h = h !& ord(c)
|
||||
@@ -914,7 +912,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
|
||||
else: break
|
||||
tokenEnd(tok, pos-1)
|
||||
h = !$h
|
||||
tok.ident = L.cache.getIdent(cast[cstring](addr(L.buf[L.bufpos])), pos - L.bufpos, h)
|
||||
tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
|
||||
if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
|
||||
(tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
|
||||
tok.tokType = tkSymbol
|
||||
@@ -928,7 +926,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
|
||||
proc endOperator(L: var Lexer, tok: var Token, pos: int,
|
||||
hash: Hash) {.inline.} =
|
||||
var h = !$hash
|
||||
tok.ident = L.cache.getIdent(cast[cstring](addr(L.buf[L.bufpos])), pos - L.bufpos, h)
|
||||
tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
|
||||
if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr
|
||||
else: tok.tokType = TokType(tok.ident.id - oprLow + ord(tkColon))
|
||||
L.bufpos = pos
|
||||
@@ -942,7 +940,7 @@ proc getOperator(L: var Lexer, tok: var Token) =
|
||||
if c in OpChars:
|
||||
h = h !& ord(c)
|
||||
inc(pos)
|
||||
elif c in UnicodeOperatorStartChars:
|
||||
elif c in UnicodeOperatorStartChars and unicodeOperators in L.config.features:
|
||||
let oprLen = unicodeOprLen(L.buf, pos)[0]
|
||||
if oprLen == 0: break
|
||||
for i in 0..<oprLen:
|
||||
@@ -954,15 +952,12 @@ proc getOperator(L: var Lexer, tok: var Token) =
|
||||
tokenEnd(tok, pos-1)
|
||||
# advance pos but don't store it in L.bufpos so the next token (which might
|
||||
# be an operator too) gets the preceding spaces:
|
||||
tok.spacing = tok.spacing - {tsTrailing, tsEof}
|
||||
var trailing = false
|
||||
tok.strongSpaceB = 0
|
||||
while L.buf[pos] == ' ':
|
||||
inc pos
|
||||
trailing = true
|
||||
inc tok.strongSpaceB
|
||||
if L.buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
|
||||
tok.spacing.incl(tsEof)
|
||||
elif trailing:
|
||||
tok.spacing.incl(tsTrailing)
|
||||
tok.strongSpaceB = -1
|
||||
|
||||
proc getPrecedence*(tok: Token): int =
|
||||
## Calculates the precedence of the given token.
|
||||
@@ -975,7 +970,7 @@ proc getPrecedence*(tok: Token): int =
|
||||
|
||||
# arrow like?
|
||||
if tok.ident.s.len > 1 and tok.ident.s[^1] == '>' and
|
||||
tok.ident.s[^2] in {'-', '~', '='}: return 0
|
||||
tok.ident.s[^2] in {'-', '~', '='}: return 1
|
||||
|
||||
template considerAsgn(value: untyped) =
|
||||
result = if tok.ident.s[^1] == '=': 1 else: value
|
||||
@@ -1073,6 +1068,7 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
|
||||
when defined(nimpretty): tok.literal.add "\L"
|
||||
if isDoc:
|
||||
when not defined(nimpretty): tok.literal.add "\n"
|
||||
inc tok.iNumber
|
||||
var c = toStrip
|
||||
while L.buf[pos] == ' ' and c > 0:
|
||||
inc pos
|
||||
@@ -1091,6 +1087,8 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
|
||||
proc scanComment(L: var Lexer, tok: var Token) =
|
||||
var pos = L.bufpos
|
||||
tok.tokType = tkComment
|
||||
# iNumber contains the number of '\n' in the token
|
||||
tok.iNumber = 0
|
||||
assert L.buf[pos+1] == '#'
|
||||
when defined(nimpretty):
|
||||
tok.commentOffsetA = L.offsetBase + pos
|
||||
@@ -1133,6 +1131,7 @@ proc scanComment(L: var Lexer, tok: var Token) =
|
||||
while L.buf[pos] == ' ' and c > 0:
|
||||
inc pos
|
||||
dec c
|
||||
inc tok.iNumber
|
||||
else:
|
||||
if L.buf[pos] > ' ':
|
||||
L.indentAhead = indent
|
||||
@@ -1145,7 +1144,7 @@ proc scanComment(L: var Lexer, tok: var Token) =
|
||||
proc skip(L: var Lexer, tok: var Token) =
|
||||
var pos = L.bufpos
|
||||
tokenBegin(tok, pos)
|
||||
tok.spacing.excl(tsLeading)
|
||||
tok.strongSpaceA = 0
|
||||
when defined(nimpretty):
|
||||
var hasComment = false
|
||||
var commentIndent = L.currLineIndent
|
||||
@@ -1156,7 +1155,7 @@ proc skip(L: var Lexer, tok: var Token) =
|
||||
case L.buf[pos]
|
||||
of ' ':
|
||||
inc(pos)
|
||||
tok.spacing.incl(tsLeading)
|
||||
inc(tok.strongSpaceA)
|
||||
of '\t':
|
||||
if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead")
|
||||
inc(pos)
|
||||
@@ -1178,7 +1177,7 @@ proc skip(L: var Lexer, tok: var Token) =
|
||||
pos = L.bufpos
|
||||
else:
|
||||
break
|
||||
tok.spacing.excl(tsLeading)
|
||||
tok.strongSpaceA = 0
|
||||
when defined(nimpretty):
|
||||
if L.buf[pos] == '#' and tok.line < 0: commentIndent = indent
|
||||
if L.buf[pos] > ' ' and (L.buf[pos] != '#' or L.buf[pos+1] == '#'):
|
||||
@@ -1217,10 +1216,6 @@ proc skip(L: var Lexer, tok: var Token) =
|
||||
proc rawGetTok*(L: var Lexer, tok: var Token) =
|
||||
template atTokenEnd() {.dirty.} =
|
||||
when defined(nimsuggest):
|
||||
L.previousTokenEnd.line = L.tokenEnd.line
|
||||
L.previousTokenEnd.col = L.tokenEnd.col
|
||||
L.tokenEnd.line = tok.line.uint16
|
||||
L.tokenEnd.col = getColNumber(L, L.bufpos).int16
|
||||
# we attach the cursor to the last *strong* token
|
||||
if tok.tokType notin weakTokens:
|
||||
L.previousToken.line = tok.line.uint16
|
||||
@@ -1246,7 +1241,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
|
||||
else:
|
||||
case c
|
||||
of UnicodeOperatorStartChars:
|
||||
if unicodeOprLen(L.buf, L.bufpos)[0] != 0:
|
||||
if unicodeOperators in L.config.features and unicodeOprLen(L.buf, L.bufpos)[0] != 0:
|
||||
getOperator(L, tok)
|
||||
else:
|
||||
getSymbol(L, tok)
|
||||
@@ -1357,7 +1352,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
|
||||
getNumber(L, tok)
|
||||
let c = L.buf[L.bufpos]
|
||||
if c in SymChars+{'_'}:
|
||||
if c in UnicodeOperatorStartChars and
|
||||
if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and
|
||||
unicodeOprLen(L.buf, L.bufpos)[0] != 0:
|
||||
discard
|
||||
else:
|
||||
@@ -1372,7 +1367,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
|
||||
getNumber(L, tok)
|
||||
let c = L.buf[L.bufpos]
|
||||
if c in SymChars+{'_'}:
|
||||
if c in UnicodeOperatorStartChars and
|
||||
if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and
|
||||
unicodeOprLen(L.buf, L.bufpos)[0] != 0:
|
||||
discard
|
||||
else:
|
||||
|
||||
@@ -8,16 +8,13 @@
|
||||
#
|
||||
|
||||
## This module implements lifting for type-bound operations
|
||||
## (`=sink`, `=copy`, `=destroy`, `=deepCopy`, `=wasMoved`, `=dup`).
|
||||
## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``).
|
||||
|
||||
import modulegraphs, lineinfos, idents, ast, renderer, semdata,
|
||||
sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils
|
||||
|
||||
from trees import isCaseObj
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
TLiftCtx = object
|
||||
g: ModuleGraph
|
||||
@@ -34,7 +31,6 @@ type
|
||||
|
||||
template destructor*(t: PType): PSym = getAttachedOp(c.g, t, attachedDestructor)
|
||||
template assignment*(t: PType): PSym = getAttachedOp(c.g, t, attachedAsgn)
|
||||
template dup*(t: PType): PSym = getAttachedOp(c.g, t, attachedDup)
|
||||
template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
|
||||
@@ -50,9 +46,9 @@ proc at(a, i: PNode, elemType: PType): PNode =
|
||||
result[1] = i
|
||||
result.typ = elemType
|
||||
|
||||
proc destructorOverridden(g: ModuleGraph; t: PType): bool =
|
||||
proc destructorOverriden(g: ModuleGraph; t: PType): bool =
|
||||
let op = getAttachedOp(g, t, attachedDestructor)
|
||||
op != nil and sfOverridden in op.flags
|
||||
op != nil and sfOverriden in op.flags
|
||||
|
||||
proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
for i in 0..<t.len:
|
||||
@@ -83,14 +79,12 @@ proc genBuiltin(c: var TLiftCtx; magic: TMagic; name: string; i: PNode): PNode =
|
||||
result = genBuiltin(c.g, c.idgen, magic, name, i)
|
||||
|
||||
proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink, attachedDup}:
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
|
||||
body.add newAsgnStmt(x, y)
|
||||
elif c.kind == attachedDestructor and c.addMemReset:
|
||||
let call = genBuiltin(c, mDefault, "default", x)
|
||||
call.typ = t
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif c.kind == attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc genAddr(c: var TLiftCtx; x: PNode): PNode =
|
||||
if x.kind == nkHiddenDeref:
|
||||
@@ -140,34 +134,25 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
|
||||
proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||
var destroy = newNodeIT(nkCall, x.info, op.typ[0])
|
||||
destroy.add(newSymNode(op))
|
||||
if op.typ[1].kind != tyVar:
|
||||
destroy.add x
|
||||
else:
|
||||
destroy.add genAddr(c, x)
|
||||
destroy.add genAddr(c, x)
|
||||
if sfNeverRaises notin op.flags:
|
||||
c.canRaise = true
|
||||
if c.addMemReset:
|
||||
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "`=wasMoved`", x))
|
||||
result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "wasMoved", x))
|
||||
else:
|
||||
result = destroy
|
||||
|
||||
proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||
result = newNodeIT(nkCall, x.info, op.typ[0])
|
||||
result.add(newSymNode(op))
|
||||
result.add genAddr(c, x)
|
||||
|
||||
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool, enforceWasMoved = false) =
|
||||
proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) =
|
||||
case n.kind
|
||||
of nkSym:
|
||||
if c.filterDiscriminator != nil: return
|
||||
let f = n.sym
|
||||
let b = if c.kind == attachedTrace: y else: y.dotField(f)
|
||||
if (sfCursor in f.flags and c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcHooks}) or
|
||||
if (sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and
|
||||
c.g.config.selectedGC in {gcArc, gcOrc, gcHooks}) or
|
||||
enforceDefaultOp:
|
||||
defaultOp(c, f.typ, body, x.dotField(f), b)
|
||||
else:
|
||||
if enforceWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f))
|
||||
fillBody(c, f.typ, body, x.dotField(f), b)
|
||||
of nkNilLit: discard
|
||||
of nkRecCase:
|
||||
@@ -177,12 +162,9 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
|
||||
# the value needs to be destroyed before we assign the selector
|
||||
# or the value is lost
|
||||
let prevKind = c.kind
|
||||
let prevAddMemReset = c.addMemReset
|
||||
c.kind = attachedDestructor
|
||||
c.addMemReset = true
|
||||
fillBodyObj(c, n, body, x, y, enforceDefaultOp = false)
|
||||
c.kind = prevKind
|
||||
c.addMemReset = prevAddMemReset
|
||||
localEnforceDefaultOp = true
|
||||
|
||||
if c.kind != attachedDestructor:
|
||||
@@ -206,7 +188,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
|
||||
branch[^1] = newNodeI(nkStmtList, c.info)
|
||||
|
||||
fillBodyObj(c, n[i].lastSon, branch[^1], x, y,
|
||||
enforceDefaultOp = localEnforceDefaultOp, enforceWasMoved = c.kind == attachedAsgn)
|
||||
enforceDefaultOp = localEnforceDefaultOp)
|
||||
if branch[^1].len == 0: inc emptyBranches
|
||||
caseStmt.add(branch)
|
||||
if emptyBranches != n.len-1:
|
||||
@@ -217,38 +199,14 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool,
|
||||
fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
|
||||
c.filterDiscriminator = oldfilterDiscriminator
|
||||
of nkRecList:
|
||||
# destroys in reverse order #24719
|
||||
if c.kind == attachedDestructor:
|
||||
for i in countdown(n.len-1, 0):
|
||||
fillBodyObj(c, n[i], body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
else:
|
||||
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
|
||||
for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp)
|
||||
else:
|
||||
illFormedAstLocal(n, c.g.config)
|
||||
|
||||
proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
template fillBase =
|
||||
if t.len > 0 and t[0] != nil:
|
||||
let dest = newNodeIT(nkHiddenSubConv, c.info, t[0])
|
||||
dest.add newNodeI(nkEmpty, c.info)
|
||||
dest.add x
|
||||
var src = y
|
||||
if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
|
||||
src = newNodeIT(nkHiddenSubConv, c.info, t[0])
|
||||
src.add newNodeI(nkEmpty, c.info)
|
||||
src.add y
|
||||
|
||||
fillBody(c, skipTypes(t[0], abstractPtrs), body, dest, src)
|
||||
template fillFields =
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
|
||||
if c.kind == attachedDestructor:
|
||||
# destroys in reverse order #24719
|
||||
fillFields()
|
||||
fillBase()
|
||||
else:
|
||||
fillBase()
|
||||
fillFields()
|
||||
if t.len > 0 and t[0] != nil:
|
||||
fillBody(c, skipTypes(t[0], abstractPtrs), body, x, y)
|
||||
fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
|
||||
|
||||
proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
var hasCase = isCaseObj(t.n)
|
||||
@@ -272,7 +230,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
# for every field (dependent on dest.kind):
|
||||
# `=` dest.field, src.field
|
||||
# =destroy(blob)
|
||||
var dummy = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
var dummy = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info)
|
||||
dummy.typ = y.typ
|
||||
if ccgIntroducedPtr(c.g.config, dummy, y.typ):
|
||||
# Because of potential aliasing when the src param is passed by ref, we need to check for equality here,
|
||||
@@ -281,7 +239,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y))
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info)
|
||||
temp.typ = x.typ
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
var v = newNodeI(nkVarSection, c.info)
|
||||
@@ -291,7 +249,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
#body.add newAsgnStmt(blob, x)
|
||||
|
||||
var wasMovedCall = newNodeI(nkCall, c.info)
|
||||
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "`=wasMoved`", mWasMoved)))
|
||||
wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved)))
|
||||
wasMovedCall.add x # mWasMoved does not take the address
|
||||
body.add wasMovedCall
|
||||
|
||||
@@ -304,7 +262,6 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
|
||||
c.kind = attachedDestructor
|
||||
fillBodyObjTImpl(c, t, body, blob, y)
|
||||
c.kind = prevKind
|
||||
|
||||
else:
|
||||
fillBodyObjTImpl(c, t, body, x, y)
|
||||
|
||||
@@ -313,7 +270,7 @@ proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
|
||||
result.typ = getSysType(g, info, tyBool)
|
||||
|
||||
proc getCycleParam(c: TLiftCtx): PNode =
|
||||
assert c.kind in {attachedAsgn, attachedDup}
|
||||
assert c.kind == attachedAsgn
|
||||
if c.fn.typ.len == 4:
|
||||
result = c.fn.typ.n.lastSon
|
||||
assert result.kind == nkSym
|
||||
@@ -352,9 +309,6 @@ proc newOpCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
|
||||
proc newDeepCopyCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
|
||||
result = newAsgnStmt(x, newOpCall(c, op, y))
|
||||
|
||||
proc newDupCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
|
||||
result = newAsgnStmt(x, newOpCall(c, op, y))
|
||||
|
||||
proc usesBuiltinArc(t: PType): bool =
|
||||
proc wrap(t: PType): bool {.nimcall.} = ast.isGCedMem(t)
|
||||
result = types.searchTypeFor(t, wrap)
|
||||
@@ -379,9 +333,9 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
field: var PSym): bool =
|
||||
if optSeqDestructors in c.g.config.globalOptions:
|
||||
var op = field
|
||||
let destructorOverridden = destructorOverridden(c.g, t)
|
||||
let destructorOverriden = destructorOverriden(c.g, t)
|
||||
if op != nil and op != c.fn and
|
||||
(sfOverridden in op.flags or destructorOverridden):
|
||||
(sfOverriden in op.flags or destructorOverriden):
|
||||
if sfError in op.flags:
|
||||
incl c.fn.flags, sfError
|
||||
#else:
|
||||
@@ -389,7 +343,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
|
||||
onUse(c.info, op)
|
||||
body.add newHookCall(c, op, x, y)
|
||||
result = true
|
||||
elif op == nil and destructorOverridden:
|
||||
elif op == nil and destructorOverriden:
|
||||
op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
|
||||
body.add newHookCall(c, op, x, y)
|
||||
result = true
|
||||
@@ -427,7 +381,7 @@ proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
|
||||
let t = orig.skipTypes(abstractInst - {tyDistinct})
|
||||
var op = t.destructor
|
||||
|
||||
if op != nil and sfOverridden in op.flags:
|
||||
if op != nil and sfOverriden in op.flags:
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic destructor:
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
@@ -450,7 +404,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
case c.kind
|
||||
of attachedDestructor:
|
||||
var op = t.destructor
|
||||
if op != nil and sfOverridden in op.flags:
|
||||
if op != nil and sfOverriden in op.flags:
|
||||
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic destructor:
|
||||
@@ -464,7 +418,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
#result = addDestructorCall(c, t, body, x)
|
||||
of attachedAsgn, attachedSink, attachedTrace:
|
||||
var op = getAttachedOp(c.g, t, c.kind)
|
||||
if op != nil and sfOverridden in op.flags:
|
||||
if op != nil and sfOverriden in op.flags:
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic =trace:
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
@@ -482,36 +436,8 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
|
||||
body.add newDeepCopyCall(c, op, x, y)
|
||||
result = true
|
||||
|
||||
of attachedWasMoved:
|
||||
var op = getAttachedOp(c.g, t, attachedWasMoved)
|
||||
if op != nil and sfOverridden in op.flags:
|
||||
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic destructor:
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
setAttachedOp(c.g, c.idgen.module, t, attachedWasMoved, op)
|
||||
|
||||
#markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
body.add genWasMovedCall(c, op, x)
|
||||
result = true
|
||||
|
||||
of attachedDup:
|
||||
var op = getAttachedOp(c.g, t, attachedDup)
|
||||
if op != nil and sfOverridden in op.flags:
|
||||
|
||||
if op.ast.isGenericRoutine:
|
||||
# patch generic destructor:
|
||||
op = instantiateGeneric(c, op, t, t.typeInst)
|
||||
setAttachedOp(c.g, c.idgen.module, t, attachedDup, op)
|
||||
|
||||
#markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op)
|
||||
body.add newDupCall(c, op, x, y)
|
||||
result = true
|
||||
|
||||
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
|
||||
temp.typ = getSysType(c.g, body.info, tyInt)
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
|
||||
@@ -521,7 +447,7 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
|
||||
body.add v
|
||||
|
||||
proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode =
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
|
||||
var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
|
||||
temp.typ = value.typ
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
|
||||
@@ -568,28 +494,14 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
else:
|
||||
body.sons.setLen counterIdx
|
||||
|
||||
proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
var cond = callCodegenProc(c.g, "sameSeqPayload", c.info,
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x),
|
||||
newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
|
||||
)
|
||||
cond.typ = getSysType(c.g, c.info, tyBool)
|
||||
body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
|
||||
|
||||
proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
of attachedDup:
|
||||
body.add setLenSeqCall(c, t, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedAsgn, attachedDeepCopy:
|
||||
# we generate:
|
||||
# if x.p == y.p:
|
||||
# return
|
||||
# setLen(dest, y.len)
|
||||
# var i = 0
|
||||
# while i < y.len: dest[i] = y[i]; inc(i)
|
||||
# This is usually more efficient than a destroy/create pair.
|
||||
checkSelfAssignment(c, t, body, x, y)
|
||||
body.add setLenSeqCall(c, t, x, y)
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedSink:
|
||||
@@ -603,18 +515,17 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
forallElements(c, t, body, x, y)
|
||||
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||
of attachedTrace:
|
||||
if canFormAcycle(c.g, t.elemType):
|
||||
if canFormAcycle(t.elemType):
|
||||
# follow all elements:
|
||||
forallElements(c, t, body, x, y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
|
||||
# recursions are tricky, so we might need to forward the generated
|
||||
# operation here:
|
||||
var t = t
|
||||
if t.assignment == nil or t.destructor == nil or t.dup == nil:
|
||||
let h = sighashes.hashType(t,c.g.config, {CoType, CoConsiderOwned, CoDistinct})
|
||||
if t.assignment == nil or t.destructor == nil:
|
||||
let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
|
||||
let canon = c.g.canonTypes.getOrDefault(h)
|
||||
if canon != nil: t = canon
|
||||
|
||||
@@ -639,22 +550,15 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
doAssert t.destructor != nil
|
||||
body.add destructorCall(c, t.destructor, x)
|
||||
of attachedTrace:
|
||||
if t.kind != tyString and canFormAcycle(c.g, t.elemType):
|
||||
if t.kind != tyString and canFormAcycle(t.elemType):
|
||||
let op = getAttachedOp(c.g, t, c.kind)
|
||||
if op == nil:
|
||||
return # protect from recursion
|
||||
body.add newHookCall(c, op, x, y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedDup:
|
||||
# XXX: replace these with assertions.
|
||||
let op = getAttachedOp(c.g, t, c.kind)
|
||||
if op == nil:
|
||||
return # protect from recursion
|
||||
body.add newDupCall(c, op, x, y)
|
||||
|
||||
proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
of attachedAsgn, attachedDeepCopy, attachedDup:
|
||||
of attachedAsgn, attachedDeepCopy:
|
||||
body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
|
||||
of attachedSink:
|
||||
let moveCall = genBuiltin(c, mMove, "move", x)
|
||||
@@ -666,11 +570,10 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genBuiltin(c, mDestroy, "destroy", x)
|
||||
of attachedTrace:
|
||||
discard "strings are atomic and have no inner elements that are to trace"
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc cyclicType*(g: ModuleGraph, t: PType): bool =
|
||||
proc cyclicType*(t: PType): bool =
|
||||
case t.kind
|
||||
of tyRef: result = types.canFormAcycle(g, t.lastSon)
|
||||
of tyRef: result = types.canFormAcycle(t.lastSon)
|
||||
of tyProc: result = t.callConv == ccClosure
|
||||
else: result = false
|
||||
|
||||
@@ -698,15 +601,10 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let elemType = t.lastSon
|
||||
|
||||
createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
|
||||
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(c.g, elemType)
|
||||
|
||||
let isInheritableAcyclicRef = c.g.config.selectedGC == gcOrc and
|
||||
(not isPureObject(elemType)) and
|
||||
tfAcyclic in skipTypes(elemType, abstractInst+{tyOwned}-{tyTypeDesc}).flags
|
||||
# dynamic Acyclic refs need to use dyn decRef
|
||||
let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType)
|
||||
|
||||
let tmp =
|
||||
if isCyclic and c.kind in {attachedAsgn, attachedSink, attachedDup}:
|
||||
if isCyclic and c.kind in {attachedAsgn, attachedSink}:
|
||||
declareTempOf(c, body, x)
|
||||
else:
|
||||
x
|
||||
@@ -728,8 +626,6 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
|
||||
else:
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
|
||||
elif isInheritableAcyclicRef:
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLastDyn", c.info, x)
|
||||
else:
|
||||
cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
|
||||
cond.typ = getSysType(c.g, x.info, tyBool)
|
||||
@@ -765,16 +661,6 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
# If the ref is polymorphic we have to account for this
|
||||
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
|
||||
#echo "can follow ", elemType, " static ", isFinal(elemType)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
of attachedDup:
|
||||
if isCyclic:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, y, callCodegenProc(c.g,
|
||||
"nimIncRefCyclic", c.info, y, getCycleParam(c)))
|
||||
else:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, y, callCodegenProc(c.g,
|
||||
"nimIncRef", c.info, y))
|
||||
|
||||
proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
## Closures are really like refs except they always use a virtual destructor
|
||||
@@ -784,7 +670,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
|
||||
let isCyclic = c.g.config.selectedGC == gcOrc
|
||||
let tmp =
|
||||
if isCyclic and c.kind in {attachedAsgn, attachedSink, attachedDup}:
|
||||
if isCyclic and c.kind in {attachedAsgn, attachedSink}:
|
||||
declareTempOf(c, body, xenv)
|
||||
else:
|
||||
xenv
|
||||
@@ -818,21 +704,11 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
|
||||
body.add genIf(c, cond, actions)
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yenv.typ = getSysType(c.g, c.info, tyPointer)
|
||||
if isCyclic:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
|
||||
else:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv))
|
||||
of attachedDestructor:
|
||||
body.add genIf(c, cond, actions)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace:
|
||||
body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case c.kind
|
||||
@@ -845,9 +721,6 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
|
||||
body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
|
||||
of attachedDestructor:
|
||||
# it's better to prepend the destruction of weak refs in order to
|
||||
# prevent wrong "dangling refs exist" problems:
|
||||
@@ -860,7 +733,6 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.sons.insert(des, 0)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
var actions = newNodeI(nkStmtList, c.info)
|
||||
@@ -882,13 +754,10 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedSink, attachedAsgn:
|
||||
body.add genIf(c, x, actions)
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDestructor:
|
||||
body.add genIf(c, x, actions)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
if c.kind == attachedDeepCopy:
|
||||
@@ -900,7 +769,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
call[1] = y
|
||||
body.add newAsgnStmt(x, call)
|
||||
elif (optOwnedRefs in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
|
||||
optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcOrc}:
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
case c.kind
|
||||
@@ -915,11 +784,6 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
|
||||
body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
|
||||
yy.typ = getSysType(c.g, c.info, tyPointer)
|
||||
body.add newAsgnStmt(x, y)
|
||||
body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
|
||||
of attachedDestructor:
|
||||
let des = genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
|
||||
if body.len == 0:
|
||||
@@ -928,7 +792,6 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
body.sons.insert(des, 0)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
|
||||
@@ -940,13 +803,10 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
of attachedSink, attachedAsgn:
|
||||
body.add genIf(c, xx, actions)
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDup:
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedDestructor:
|
||||
body.add genIf(c, xx, actions)
|
||||
of attachedDeepCopy: assert(false, "cannot happen")
|
||||
of attachedTrace: discard
|
||||
of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
|
||||
proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
case t.kind
|
||||
@@ -955,7 +815,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
tyPtr, tyUncheckedArray, tyVar, tyLent:
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyRef:
|
||||
if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
if c.g.config.selectedGC in {gcArc, gcOrc}:
|
||||
atomicRefOp(c, t, body, x, y)
|
||||
elif (optOwnedRefs in c.g.config.globalOptions and
|
||||
optRefCheck in c.g.config.options):
|
||||
@@ -964,7 +824,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyProc:
|
||||
if t.callConv == ccClosure:
|
||||
if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
|
||||
if c.g.config.selectedGC in {gcArc, gcOrc}:
|
||||
atomicClosureOp(c, t, body, x, y)
|
||||
else:
|
||||
closureOp(c, t, body, x, y)
|
||||
@@ -1010,25 +870,10 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
defaultOp(c, t, body, x, y)
|
||||
of tyObject:
|
||||
if not considerUserDefinedOp(c, t, body, x, y):
|
||||
if t.sym != nil and sfImportc in t.sym.flags:
|
||||
case c.kind
|
||||
of {attachedAsgn, attachedSink, attachedDup}:
|
||||
body.add newAsgnStmt(x, y)
|
||||
of attachedWasMoved:
|
||||
body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
|
||||
else:
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
if c.kind in {attachedAsgn, attachedSink} and t.sym != nil and sfImportc in t.sym.flags:
|
||||
body.add newAsgnStmt(x, y)
|
||||
else:
|
||||
if c.kind == attachedDup:
|
||||
var op2 = getAttachedOp(c.g, t, attachedAsgn)
|
||||
if op2 != nil and sfOverridden in op2.flags:
|
||||
#markUsed(c.g.config, c.info, op, c.g.usageSym)
|
||||
onUse(c.info, op2)
|
||||
body.add newHookCall(c, t.assignment, x, y)
|
||||
else:
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
else:
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
fillBodyObjT(c, t, body, x, y)
|
||||
of tyDistinct:
|
||||
if not considerUserDefinedOp(c, t, body, x, y):
|
||||
fillBody(c, t[0], body, x, y)
|
||||
@@ -1061,56 +906,15 @@ proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
|
||||
result = getAttachedOp(g, baseType, kind)
|
||||
setAttachedOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
let procname = getIdent(g.cache, AttachedOpToStr[kind])
|
||||
result = newSym(skProc, procname, idgen, owner, info)
|
||||
let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
|
||||
let src = newSym(skParam, getIdent(g.cache, "src"),
|
||||
idgen, result, info)
|
||||
res.typ = typ
|
||||
src.typ = typ
|
||||
|
||||
result.typ = newType(tyProc, nextTypeId idgen, owner)
|
||||
result.typ.n = newNodeI(nkFormalParams, info)
|
||||
rawAddSon(result.typ, res.typ)
|
||||
result.typ.n.add newNodeI(nkEffectList, info)
|
||||
|
||||
result.typ.addParam src
|
||||
|
||||
if g.config.selectedGC == gcOrc and
|
||||
cyclicType(g, typ.skipTypes(abstractInst)):
|
||||
let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
|
||||
idgen, result, info)
|
||||
cycleParam.typ = getSysType(g, info, tyBool)
|
||||
result.typ.addParam cycleParam
|
||||
|
||||
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] = newNodeI(nkStmtList, info)
|
||||
n[resultPos] = newSymNode(res)
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfGeneratedOp
|
||||
|
||||
proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator; isDiscriminant = false): PSym =
|
||||
if kind == attachedDup:
|
||||
return symDupPrototype(g, typ, owner, kind, info, idgen)
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
|
||||
let procname = getIdent(g.cache, AttachedOpToStr[kind])
|
||||
result = newSym(skProc, procname, idgen, owner, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info)
|
||||
result = newSym(skProc, procname, nextSymId(idgen), owner, info)
|
||||
let dest = newSym(skParam, getIdent(g.cache, "dest"), nextSymId(idgen), result, info)
|
||||
let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
|
||||
idgen, result, info)
|
||||
if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
|
||||
((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence})):
|
||||
dest.typ = typ
|
||||
else:
|
||||
dest.typ = makeVarType(typ.owner, typ, idgen)
|
||||
|
||||
nextSymId(idgen), result, info)
|
||||
dest.typ = makeVarType(typ.owner, typ, idgen)
|
||||
if kind == attachedTrace:
|
||||
src.typ = getSysType(g, info, tyPointer)
|
||||
else:
|
||||
@@ -1118,13 +922,13 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
|
||||
result.typ = newProcType(info, nextTypeId(idgen), owner)
|
||||
result.typ.addParam dest
|
||||
if kind notin {attachedDestructor, attachedWasMoved}:
|
||||
if kind != attachedDestructor:
|
||||
result.typ.addParam src
|
||||
|
||||
if kind == attachedAsgn and g.config.selectedGC == gcOrc and
|
||||
cyclicType(g, typ.skipTypes(abstractInst)):
|
||||
cyclicType(typ.skipTypes(abstractInst)):
|
||||
let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
|
||||
idgen, result, info)
|
||||
nextSymId(idgen), result, info)
|
||||
cycleParam.typ = getSysType(g, info, tyBool)
|
||||
result.typ.addParam cycleParam
|
||||
|
||||
@@ -1136,16 +940,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
|
||||
result.ast = n
|
||||
incl result.flags, sfFromGeneric
|
||||
incl result.flags, sfGeneratedOp
|
||||
if kind == attachedWasMoved:
|
||||
incl result.flags, sfNoSideEffect
|
||||
incl result.typ.flags, tfNoSideEffect
|
||||
|
||||
proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
|
||||
let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x)
|
||||
let yy = genBuiltin(c, mAccessTypeField, "accessTypeField", y)
|
||||
xx.typ = getSysType(c.g, c.info, tyPointer)
|
||||
yy.typ = xx.typ
|
||||
body.add newAsgnStmt(xx, yy)
|
||||
|
||||
proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
@@ -1159,25 +954,22 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
|
||||
fn: result)
|
||||
|
||||
let dest = if kind == attachedDup: result.ast[resultPos].sym else: result.typ.n[1].sym
|
||||
let d = if result.typ[1].kind == tyVar: newDeref(newSymNode(dest)) else: newSymNode(dest)
|
||||
let src = case kind
|
||||
of {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
|
||||
of attachedDup: newSymNode(result.typ.n[1].sym)
|
||||
let dest = result.typ.n[1].sym
|
||||
let d = newDeref(newSymNode(dest))
|
||||
let src = if kind == attachedDestructor: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
|
||||
else: newSymNode(result.typ.n[2].sym)
|
||||
|
||||
# register this operation already:
|
||||
setAttachedOpPartial(g, idgen.module, typ, kind, result)
|
||||
|
||||
if kind == attachedSink and destructorOverridden(g, typ):
|
||||
if kind == attachedSink and destructorOverriden(g, typ):
|
||||
## compiler can use a combination of `=destroy` and memCopy for sink op
|
||||
dest.flags.incl sfCursor
|
||||
let op = getAttachedOp(g, typ, attachedDestructor)
|
||||
result.ast[bodyPos].add newOpCall(a, op, if op.typ[1].kind == tyVar: d[0] else: d)
|
||||
result.ast[bodyPos].add newOpCall(a, getAttachedOp(g, typ, attachedDestructor), d[0])
|
||||
result.ast[bodyPos].add newAsgnStmt(d, src)
|
||||
else:
|
||||
var tk: TTypeKind
|
||||
if g.config.selectedGC in {gcArc, gcOrc, gcHooks, gcAtomicArc}:
|
||||
if g.config.selectedGC in {gcArc, gcOrc, gcHooks}:
|
||||
tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
|
||||
else:
|
||||
tk = tyNone # no special casing for strings and seqs
|
||||
@@ -1188,26 +980,14 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
|
||||
fillStrOp(a, typ, result.ast[bodyPos], d, src)
|
||||
else:
|
||||
fillBody(a, typ, result.ast[bodyPos], d, src)
|
||||
if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not lacksMTypeField(typ):
|
||||
# bug #19205: Do not forget to also copy the hidden type field:
|
||||
genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
|
||||
|
||||
if not a.canRaise:
|
||||
incl result.flags, sfNeverRaises
|
||||
result.ast[pragmasPos] = newNodeI(nkPragma, info)
|
||||
result.ast[pragmasPos].add newTree(nkExprColonExpr,
|
||||
newIdentNode(g.cache.getIdent("raises"), info), newNodeI(nkBracket, info))
|
||||
|
||||
if kind == attachedDestructor:
|
||||
incl result.options, optQuirky
|
||||
if not a.canRaise: incl result.flags, sfNeverRaises
|
||||
completePartialOp(g, idgen.module, typ, kind, result)
|
||||
|
||||
|
||||
proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
|
||||
info: TLineInfo; idgen: IdGenerator): PSym =
|
||||
assert(typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject)
|
||||
# discrimantor assignments needs pointers to destroy fields; alas, we cannot use non-var destructor here
|
||||
result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen, isDiscriminant = true)
|
||||
result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen)
|
||||
var a = TLiftCtx(info: info, g: g, kind: attachedDestructor, asgnForType: typ, idgen: idgen,
|
||||
fn: result)
|
||||
a.asgnForType = typ
|
||||
@@ -1215,7 +995,7 @@ proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
|
||||
a.addMemReset = true
|
||||
let discrimantDest = result.typ.n[1].sym
|
||||
|
||||
let dst = newSym(skVar, getIdent(g.cache, "dest"), idgen, result, info)
|
||||
let dst = newSym(skVar, getIdent(g.cache, "dest"), nextSymId(idgen), result, info)
|
||||
dst.typ = makePtrType(typ.owner, typ, idgen)
|
||||
let dstSym = newSymNode(dst)
|
||||
let d = newDeref(dstSym)
|
||||
@@ -1241,7 +1021,7 @@ proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: Id
|
||||
if op != nil:
|
||||
if op.ast.isGenericRoutine:
|
||||
internalError(g.config, info, "resolved destructor is generic")
|
||||
if op.magic == mDestroy and t.kind != tyString:
|
||||
if op.magic == mDestroy:
|
||||
internalError(g.config, info, "patching mDestroy with mDestroy?")
|
||||
n[0] = newSymNode(op)
|
||||
for x in n: patchBody(g, c, x, info, idgen)
|
||||
@@ -1251,7 +1031,13 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
|
||||
let op = getAttachedOp(g, t, kind)
|
||||
if op != nil and op.ast != nil and op.ast.isGenericRoutine:
|
||||
if t.typeInst != nil:
|
||||
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, idgen: idgen)
|
||||
var a: TLiftCtx
|
||||
a.info = info
|
||||
a.g = g
|
||||
a.kind = kind
|
||||
a.c = c
|
||||
a.idgen = idgen
|
||||
|
||||
let opInst = instantiateGeneric(a, op, t, t.typeInst)
|
||||
if opInst.ast != nil:
|
||||
patchBody(g, c, opInst.ast, info, a.idgen)
|
||||
@@ -1259,7 +1045,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
|
||||
else:
|
||||
localError(g.config, info, "unresolved generic parameter")
|
||||
|
||||
proc isTrival*(s: PSym): bool {.inline.} =
|
||||
proc isTrival(s: PSym): bool {.inline.} =
|
||||
s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
|
||||
|
||||
proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
|
||||
@@ -1273,7 +1059,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
|
||||
if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
|
||||
|
||||
let h = sighashes.hashType(skipped, g.config, {CoType, CoConsiderOwned, CoDistinct})
|
||||
let h = sighashes.hashType(skipped, {CoType, CoConsiderOwned, CoDistinct})
|
||||
var canon = g.canonTypes.getOrDefault(h)
|
||||
if canon == nil:
|
||||
g.canonTypes[h] = skipped
|
||||
@@ -1294,15 +1080,15 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
|
||||
# bug #15122: We need to produce all prototypes before entering the
|
||||
# mind boggling recursion. Hacks like these imply we should rewrite
|
||||
# this module.
|
||||
var generics: array[attachedWasMoved..attachedTrace, bool]
|
||||
for k in attachedWasMoved..lastAttached:
|
||||
var generics: array[attachedDestructor..attachedTrace, bool]
|
||||
for k in attachedDestructor..lastAttached:
|
||||
generics[k] = getAttachedOp(g, canon, k) != nil
|
||||
if not generics[k]:
|
||||
setAttachedOp(g, idgen.module, canon, k,
|
||||
symPrototype(g, canon, canon.owner, k, info, idgen))
|
||||
|
||||
# we generate the destructor first so that other operators can depend on it:
|
||||
for k in attachedWasMoved..lastAttached:
|
||||
for k in attachedDestructor..lastAttached:
|
||||
if not generics[k]:
|
||||
discard produceSym(g, c, canon, k, info, idgen)
|
||||
else:
|
||||
|
||||
@@ -32,19 +32,15 @@ type
|
||||
# non-fatal errors
|
||||
errIllFormedAstX, errCannotOpenFile,
|
||||
errXExpected,
|
||||
errRstMissingClosing,
|
||||
errRstGridTableNotImplemented,
|
||||
errRstMarkdownIllformedTable,
|
||||
errRstIllformedTable,
|
||||
errRstNewSectionExpected,
|
||||
errRstGeneralParseError,
|
||||
errRstInvalidDirectiveX,
|
||||
errRstInvalidField,
|
||||
errRstFootnoteMismatch,
|
||||
errRstSandboxedDirective,
|
||||
errProveInit, # deadcode
|
||||
errGenerated,
|
||||
errFailedMove,
|
||||
errUser,
|
||||
# warnings
|
||||
warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape",
|
||||
@@ -54,11 +50,9 @@ type
|
||||
warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic",
|
||||
warnRstRedefinitionOfLabel = "RedefinitionOfLabel",
|
||||
warnRstUnknownSubstitutionX = "UnknownSubstitutionX",
|
||||
warnRstAmbiguousLink = "AmbiguousLink",
|
||||
warnRstBrokenLink = "BrokenLink",
|
||||
warnRstLanguageXNotSupported = "LanguageXNotSupported",
|
||||
warnRstFieldXNotSupported = "FieldXNotSupported",
|
||||
warnRstUnusedImportdoc = "UnusedImportdoc",
|
||||
warnRstStyle = "warnRstStyle",
|
||||
warnCommentXIgnored = "CommentXIgnored",
|
||||
warnTypelessParam = "TypelessParam",
|
||||
@@ -70,8 +64,7 @@ type
|
||||
warnUnreachableElse = "UnreachableElse", warnUnreachableCode = "UnreachableCode",
|
||||
warnStaticIndexCheck = "IndexCheck", warnGcUnsafe = "GcUnsafe", warnGcUnsafe2 = "GcUnsafe2",
|
||||
warnUninit = "Uninit", warnGcMem = "GcMem", warnDestructor = "Destructor",
|
||||
warnLockLevel = "LockLevel", # deadcode
|
||||
warnResultShadowed = "ResultShadowed",
|
||||
warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed",
|
||||
warnInconsistentSpacing = "Spacing", warnCaseTransition = "CaseTransition",
|
||||
warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores",
|
||||
warnStrictNotNil = "StrictNotNil",
|
||||
@@ -82,20 +75,12 @@ type
|
||||
warnAnyEnumConv = "AnyEnumConv",
|
||||
warnHoleEnumConv = "HoleEnumConv",
|
||||
warnCstringConv = "CStringConv",
|
||||
warnPtrToCstringConv = "PtrToCstringConv",
|
||||
warnEffect = "Effect",
|
||||
warnCastSizes = "CastSizes", # deadcode
|
||||
warnAboveMaxSizeSet = "AboveMaxSizeSet",
|
||||
warnImplicitTemplateRedefinition = "ImplicitTemplateRedefinition",
|
||||
warnUnnamedBreak = "UnnamedBreak",
|
||||
warnStmtListLambda = "StmtListLambda",
|
||||
warnBareExcept = "BareExcept",
|
||||
warnImplicitDefaultValue = "ImplicitDefaultValue",
|
||||
warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
|
||||
warnUser = "User",
|
||||
# hints
|
||||
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
||||
hintCC = "CC",
|
||||
hintLineTooLong = "LineTooLong",
|
||||
hintXDeclaredButNotUsed = "XDeclaredButNotUsed", hintDuplicateModuleImport = "DuplicateModuleImport",
|
||||
hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded",
|
||||
hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX",
|
||||
@@ -108,7 +93,6 @@ type
|
||||
hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
|
||||
hintMsgOrigin = "MsgOrigin", # since 1.3.5
|
||||
hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
|
||||
hintUnknownHint = "UnknownHint"
|
||||
|
||||
const
|
||||
MsgKindToStr*: array[TMsgKind, string] = [
|
||||
@@ -118,19 +102,15 @@ const
|
||||
errIllFormedAstX: "illformed AST: $1",
|
||||
errCannotOpenFile: "cannot open '$1'",
|
||||
errXExpected: "'$1' expected",
|
||||
errRstMissingClosing: "$1",
|
||||
errRstGridTableNotImplemented: "grid table is not implemented",
|
||||
errRstMarkdownIllformedTable: "illformed delimiter row of a markdown table",
|
||||
errRstIllformedTable: "Illformed table: $1",
|
||||
errRstNewSectionExpected: "new section expected $1",
|
||||
errRstGeneralParseError: "general parse error",
|
||||
errRstInvalidDirectiveX: "invalid directive: '$1'",
|
||||
errRstInvalidField: "invalid field: $1",
|
||||
errRstFootnoteMismatch: "number of footnotes and their references don't match: $1",
|
||||
errRstSandboxedDirective: "disabled directive: '$1'",
|
||||
errProveInit: "Cannot prove that '$1' is initialized.", # deadcode
|
||||
errGenerated: "$1",
|
||||
errFailedMove: "$1",
|
||||
errUser: "$1",
|
||||
warnCannotOpenFile: "cannot open '$1'",
|
||||
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
|
||||
@@ -143,11 +123,9 @@ const
|
||||
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
|
||||
warnRstRedefinitionOfLabel: "redefinition of label '$1'",
|
||||
warnRstUnknownSubstitutionX: "unknown substitution '$1'",
|
||||
warnRstAmbiguousLink: "ambiguous doc link $1",
|
||||
warnRstBrokenLink: "broken link '$1'",
|
||||
warnRstLanguageXNotSupported: "language '$1' not supported",
|
||||
warnRstFieldXNotSupported: "field '$1' not supported",
|
||||
warnRstUnusedImportdoc: "importdoc for '$1' is not used",
|
||||
warnRstStyle: "RST style: $1",
|
||||
warnCommentXIgnored: "comment '$1' ignored",
|
||||
warnTypelessParam: "", # deadcode
|
||||
@@ -172,7 +150,7 @@ const
|
||||
warnUninit: "use explicit initialization of '$1' for clarity",
|
||||
warnGcMem: "'$1' uses GC'ed memory",
|
||||
warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
|
||||
warnLockLevel: "$1", # deadcode
|
||||
warnLockLevel: "$1",
|
||||
warnResultShadowed: "Special variable 'result' is shadowed.",
|
||||
warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
|
||||
warnCaseTransition: "Potential object case transition, instantiate new object instead",
|
||||
@@ -186,21 +164,13 @@ const
|
||||
warnAnyEnumConv: "$1",
|
||||
warnHoleEnumConv: "$1",
|
||||
warnCstringConv: "$1",
|
||||
warnPtrToCstringConv: "unsafe conversion to 'cstring' from '$1'; Use a `cast` operation like `cast[cstring](x)`; this will become a compile time error in the future",
|
||||
warnEffect: "$1",
|
||||
warnCastSizes: "$1", # deadcode
|
||||
warnAboveMaxSizeSet: "$1",
|
||||
warnImplicitTemplateRedefinition: "template '$1' is implicitly redefined; this is deprecated, add an explicit .redefine pragma",
|
||||
warnUnnamedBreak: "Using an unnamed break in a block is deprecated; Use a named block with a named break instead",
|
||||
warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
|
||||
warnBareExcept: "$1",
|
||||
warnImplicitDefaultValue: "$1",
|
||||
warnIgnoredSymbolInjection: "$1",
|
||||
warnUser: "$1",
|
||||
hintSuccess: "operation successful: $#",
|
||||
# keep in sync with `testament.isSuccess`
|
||||
hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
|
||||
hintCC: "CC: $1",
|
||||
hintLineTooLong: "line too long",
|
||||
hintXDeclaredButNotUsed: "'$1' is declared but not used",
|
||||
hintDuplicateModuleImport: "$1",
|
||||
hintXCannotRaiseY: "$1",
|
||||
@@ -232,7 +202,6 @@ const
|
||||
hintExtendedContext: "$1",
|
||||
hintMsgOrigin: "$1",
|
||||
hintDeclaredLoc: "$1",
|
||||
hintUnknownHint: "unknown hint: $1"
|
||||
]
|
||||
|
||||
const
|
||||
@@ -250,8 +219,8 @@ type
|
||||
TNoteKinds* = set[TNoteKind]
|
||||
|
||||
proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
|
||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept}
|
||||
result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
|
||||
result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv}
|
||||
result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
|
||||
result[1] = result[2] - {warnProveField, warnProveIndex,
|
||||
warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
|
||||
hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
|
||||
@@ -281,7 +250,7 @@ type
|
||||
# and parsed; usually "" but is used
|
||||
# for 'nimsuggest'
|
||||
hash*: string # the checksum of the file
|
||||
dirty*: bool # for 'nimpretty' like tooling
|
||||
dirty*: bool # for 'nimfix' / 'nimpretty' like tooling
|
||||
when defined(nimpretty):
|
||||
fullContent*: string
|
||||
FileIndex* = distinct int32
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
import std/strutils
|
||||
from std/sugar import dup
|
||||
|
||||
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages, modulegraphs
|
||||
export packages
|
||||
import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs
|
||||
|
||||
const
|
||||
Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
|
||||
@@ -86,33 +85,24 @@ proc differ*(line: string, a, b: int, x: string): string =
|
||||
result = y
|
||||
|
||||
proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||
# operators stay as they are:
|
||||
if k in {skResult, skTemp} or s.name.s[0] notin Letters: return
|
||||
if k in {skType, skGenericParam} and sfAnon in s.flags: return
|
||||
if s.typ != nil and s.typ.kind == tyTypeDesc: return
|
||||
if {sfImportc, sfExportc} * s.flags != {}: return
|
||||
if optStyleCheck notin s.options: return
|
||||
let beau = beautifyName(s.name.s, k)
|
||||
if s.name.s != beau:
|
||||
lintReport(conf, info, beau, s.name.s)
|
||||
|
||||
template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind) =
|
||||
## Check symbol definitions adhere to NEP1 style rules.
|
||||
if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
|
||||
{optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
|
||||
sym.kind != skResult and # ignore `result`
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
|
||||
k notin {skType, skGenericParam} and # ignore types and generic params
|
||||
(sym.typ == nil or sym.typ.kind != tyTypeDesc) and # ignore `typedesc`
|
||||
{sfImportc, sfExportc} * sym.flags == {} and # ignore FFI
|
||||
sfAnon notin sym.flags: # ignore if created by compiler
|
||||
nep1CheckDefImpl(ctx.config, info, sym, k)
|
||||
template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
|
||||
if {optStyleHint, optStyleError} * conf.globalOptions != {} and optStyleUsages notin conf.globalOptions:
|
||||
nep1CheckDefImpl(conf, info, s, k)
|
||||
|
||||
template styleCheckDef*(ctx: PContext; info: TLineInfo; s: PSym) =
|
||||
## Check symbol definitions adhere to NEP1 style rules.
|
||||
styleCheckDef(ctx, info, s, s.kind)
|
||||
|
||||
template styleCheckDef*(ctx: PContext; s: PSym) =
|
||||
## Check symbol definitions adhere to NEP1 style rules.
|
||||
styleCheckDef(ctx, s.info, s, s.kind)
|
||||
template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
styleCheckDef(conf, info, s, s.kind)
|
||||
template styleCheckDef*(conf: ConfigRef; s: PSym) =
|
||||
styleCheckDef(conf, s.info, s, s.kind)
|
||||
|
||||
proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string =
|
||||
let line = sourceLine(conf, info)
|
||||
@@ -126,31 +116,23 @@ proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string =
|
||||
let last = first+identLen(line, first)-1
|
||||
result = differ(line, first, last, newName)
|
||||
|
||||
proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
proc styleCheckUse*(conf: ConfigRef; info: TLineInfo; s: PSym) =
|
||||
if info.fileIndex.int < 0: return
|
||||
# we simply convert it to what it looks like in the definition
|
||||
# for consistency
|
||||
|
||||
# operators stay as they are:
|
||||
if s.kind == skTemp or s.name.s[0] notin Letters or sfAnon in s.flags:
|
||||
return
|
||||
|
||||
let newName = s.name.s
|
||||
let badName = differs(conf, info, newName)
|
||||
if badName.len > 0:
|
||||
lintReport(conf, info, newName, badName, "".dup(addDeclaredLoc(conf, s)))
|
||||
# special rules for historical reasons
|
||||
let forceHint = badName == "nnkArgList" and newName == "nnkArglist" or badName == "nnkArglist" and newName == "nnkArgList"
|
||||
lintReport(conf, info, newName, badName, forceHint = forceHint, extraMsg = "".dup(addDeclaredLoc(conf, s)))
|
||||
|
||||
template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
|
||||
## Check symbol uses match their definition's style.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)) and # ignore foreign packages
|
||||
sym.kind != skTemp and # ignore temporary variables created by the compiler
|
||||
sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
|
||||
sfAnon notin sym.flags: # ignore temporary variables created by the compiler
|
||||
styleCheckUseImpl(ctx.config, info, sym)
|
||||
|
||||
proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
|
||||
proc checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
|
||||
let wanted = $w
|
||||
if pragmaName != wanted:
|
||||
lintReport(conf, info, wanted, pragmaName)
|
||||
|
||||
template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragmaName: string, sym: PSym) =
|
||||
## Check builtin pragma uses match their definition's style.
|
||||
## Note: This only applies to builtin pragmas, not user pragmas.
|
||||
if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
|
||||
hintName in ctx.config.notes and # ignore if name checks are not requested
|
||||
ctx.config.belongsToProjectPackageMaybeNil(getModule(ctx.graph, info.fileIndex)): # ignore foreign packages
|
||||
checkPragmaUseImpl(ctx.config, info, w, pragmaName)
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
import
|
||||
pathutils
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/syncio
|
||||
|
||||
# support `useGnuReadline`, `useLinenoise` for backwards compatibility
|
||||
const hasRstdin = (defined(nimUseLinenoise) or defined(useLinenoise) or defined(useGnuReadline)) and
|
||||
not defined(windows)
|
||||
|
||||
@@ -8,14 +8,10 @@
|
||||
#
|
||||
|
||||
# This module implements lookup helpers.
|
||||
import std/[algorithm, strutils, tables]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
import std/[algorithm, strutils]
|
||||
import
|
||||
intsets, ast, astalgo, idents, semdata, types, msgs, options,
|
||||
renderer, lineinfos, modulegraphs, astmsgs, sets, wordrecg
|
||||
renderer, nimfix/prettybase, lineinfos, modulegraphs, astmsgs
|
||||
|
||||
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
|
||||
|
||||
@@ -48,11 +44,6 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||
case x.kind
|
||||
of nkIdent: id.add(x.ident.s)
|
||||
of nkSym: id.add(x.sym.name.s)
|
||||
of nkSymChoices, nkOpenSym:
|
||||
if x[0].kind == nkSym:
|
||||
id.add(x[0].sym.name.s)
|
||||
else:
|
||||
handleError(n, origin)
|
||||
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
|
||||
else: handleError(n, origin)
|
||||
result = getIdent(c.cache, id)
|
||||
@@ -61,8 +52,6 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
|
||||
result = n[0].sym.name
|
||||
else:
|
||||
handleError(n, origin)
|
||||
of nkOpenSym:
|
||||
result = considerQuotedIdent(c, n[0], origin)
|
||||
else:
|
||||
handleError(n, origin)
|
||||
|
||||
@@ -96,6 +85,17 @@ iterator localScopesFrom*(c: PContext; scope: PScope): PScope =
|
||||
if s == c.topLevelScope: break
|
||||
yield s
|
||||
|
||||
proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
|
||||
if s == nil or s.kind != skAlias:
|
||||
result = s
|
||||
else:
|
||||
result = s.owner
|
||||
if conf.cmd == cmdNimfix:
|
||||
prettybase.replaceDeprecated(conf, n.info, s, result)
|
||||
else:
|
||||
message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
|
||||
s.name.s & " is deprecated")
|
||||
|
||||
proc isShadowScope*(s: PScope): bool {.inline.} =
|
||||
s.parent != nil and s.parent.depthLevel == s.depthLevel
|
||||
|
||||
@@ -137,7 +137,7 @@ proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule;
|
||||
return result
|
||||
|
||||
iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent; g: ModuleGraph): PSym =
|
||||
var ti: ModuleIter = default(ModuleIter)
|
||||
var ti: ModuleIter
|
||||
var candidate = initIdentIter(ti, marked, im, name, g)
|
||||
while candidate != nil:
|
||||
yield candidate
|
||||
@@ -150,7 +150,7 @@ iterator importedItems*(c: PContext; name: PIdent): PSym =
|
||||
yield s
|
||||
|
||||
proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] =
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
var ti: TIdentIter
|
||||
result = @[]
|
||||
var res = initIdentIter(ti, c.pureEnumFields, name)
|
||||
while res != nil:
|
||||
@@ -161,7 +161,6 @@ iterator allSyms*(c: PContext): (PSym, int, bool) =
|
||||
# really iterate over all symbols in all the scopes. This is expensive
|
||||
# and only used by suggest.nim.
|
||||
var isLocal = true
|
||||
|
||||
var scopeN = 0
|
||||
for scope in allScopes(c.currentScope):
|
||||
if scope == c.topLevelScope: isLocal = false
|
||||
@@ -176,20 +175,11 @@ iterator allSyms*(c: PContext): (PSym, int, bool) =
|
||||
assert s != nil
|
||||
yield (s, scopeN, isLocal)
|
||||
|
||||
iterator uniqueSyms*(c: PContext): (PSym, int, bool) =
|
||||
## Like [allSyms] except only returns unique symbols (Uniqueness determined by line + name)
|
||||
# Track seen symbols so we don't duplicate them.
|
||||
# The int is for the symbols name, and line info is
|
||||
# to be able to tell apart symbols with same name but on different lines
|
||||
var seen = initHashSet[(TLineInfo, int)]()
|
||||
for res in allSyms(c):
|
||||
if not seen.containsOrIncl((res[0].info, res[0].name.id)):
|
||||
yield res
|
||||
|
||||
|
||||
proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym =
|
||||
var marked = initIntSet()
|
||||
var symSet = OverloadableSyms
|
||||
if overloadableEnums notin c.features:
|
||||
symSet.excl skEnumField
|
||||
result = nil
|
||||
block outer:
|
||||
for im in c.imports.mitems:
|
||||
@@ -219,28 +209,11 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
|
||||
if i == limit: return
|
||||
inc i
|
||||
|
||||
proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||
while candidate != nil:
|
||||
if candidate.kind in filter:
|
||||
result.add candidate
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
|
||||
if result.len == 0:
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
|
||||
proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
|
||||
result = @[]
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
var ti: TIdentIter
|
||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||
while candidate != nil:
|
||||
if candidate.kind in filter:
|
||||
@@ -256,63 +229,8 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
|
||||
if s.kind in filter:
|
||||
result.add s
|
||||
|
||||
proc cmpScopes*(ctx: PContext, s: PSym): int =
|
||||
# Do not return a negative number
|
||||
if s.originatingModule == ctx.module:
|
||||
result = 2
|
||||
var owner = s
|
||||
while true:
|
||||
owner = owner.skipGenericOwner
|
||||
if owner.kind == skModule: break
|
||||
inc result
|
||||
else:
|
||||
result = 1
|
||||
|
||||
proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): bool =
|
||||
result = false
|
||||
block outer:
|
||||
for scope in allScopes(c.currentScope):
|
||||
var ti: TIdentIter = default(TIdentIter)
|
||||
var candidate = initIdentIter(ti, scope.symbols, s)
|
||||
var scopeHasCandidate = false
|
||||
while candidate != nil:
|
||||
if candidate.kind in filter:
|
||||
if scopeHasCandidate:
|
||||
# 2 candidates in same scope, ambiguous
|
||||
return true
|
||||
else:
|
||||
scopeHasCandidate = true
|
||||
sym = candidate
|
||||
candidate = nextIdentIter(ti, scope.symbols)
|
||||
if scopeHasCandidate:
|
||||
# scope had a candidate but wasn't ambiguous
|
||||
return false
|
||||
|
||||
var importsHaveCandidate = false
|
||||
var marked = initIntSet()
|
||||
for im in c.imports.mitems:
|
||||
for s in symbols(im, marked, s, c.graph):
|
||||
if s.kind in filter:
|
||||
if importsHaveCandidate:
|
||||
# 2 candidates among imports, ambiguous
|
||||
return true
|
||||
else:
|
||||
importsHaveCandidate = true
|
||||
sym = s
|
||||
if importsHaveCandidate:
|
||||
# imports had a candidate but wasn't ambiguous
|
||||
return false
|
||||
|
||||
proc errorSym*(c: PContext, ident: PIdent, info: TLineInfo): PSym =
|
||||
## creates an error symbol to avoid cascading errors (for IDE support)
|
||||
result = newSym(skError, ident, c.idgen, getCurrOwner(c), info, {})
|
||||
result.typ = errorType(c)
|
||||
incl(result.flags, sfDiscardable)
|
||||
# pretend it's from the top level scope to prevent cascading errors:
|
||||
if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
|
||||
c.moduleScope.addSym(result)
|
||||
|
||||
proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
## creates an error symbol to avoid cascading errors (for IDE support)
|
||||
var m = n
|
||||
# ensure that 'considerQuotedIdent' can't fail:
|
||||
if m.kind == nkDotExpr: m = m[1]
|
||||
@@ -320,7 +238,12 @@ proc errorSym*(c: PContext, n: PNode): PSym =
|
||||
considerQuotedIdent(c, m)
|
||||
else:
|
||||
getIdent(c.cache, "err:" & renderTree(m))
|
||||
result = errorSym(c, ident, n.info)
|
||||
result = newSym(skError, ident, nextSymId(c.idgen), getCurrOwner(c), n.info, {})
|
||||
result.typ = errorType(c)
|
||||
incl(result.flags, sfDiscardable)
|
||||
# pretend it's from the top level scope to prevent cascading errors:
|
||||
if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
|
||||
c.moduleScope.addSym(result)
|
||||
|
||||
type
|
||||
TOverloadIterMode* = enum
|
||||
@@ -347,7 +270,7 @@ proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string =
|
||||
|
||||
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
|
||||
# check if all symbols have been used and defined:
|
||||
var it: TTabIter = default(TTabIter)
|
||||
var it: TTabIter
|
||||
var s = initTabIter(it, scope.symbols)
|
||||
var missingImpls = 0
|
||||
var unusedSyms: seq[tuple[sym: PSym, key: string]]
|
||||
@@ -381,18 +304,15 @@ proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
|
||||
# xxx pending bootstrap >= 1.4, replace all those overloads with a single one:
|
||||
# proc addDecl*(c: PContext, sym: PSym, info = sym.info, scope = c.currentScope) {.inline.} =
|
||||
proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
|
||||
if sym.name.id == ord(wUnderscore): return
|
||||
let conflict = scope.addUniqueSym(sym)
|
||||
if conflict != nil:
|
||||
if sym.kind == skModule and conflict.kind == skModule:
|
||||
if sym.kind == skModule and conflict.kind == skModule and sym.owner == conflict.owner:
|
||||
# e.g.: import foo; import foo
|
||||
# xxx we could refine this by issuing a different hint for the case
|
||||
# where a duplicate import happens inside an include.
|
||||
if c.importModuleMap[sym.id] == c.importModuleMap[conflict.id]:
|
||||
#only hints if the conflict is the actual module not just a shared name
|
||||
localError(c.config, info, hintDuplicateModuleImport,
|
||||
"duplicate import of '$1'; previous import here: $2" %
|
||||
[sym.name.s, c.config $ conflict.info])
|
||||
localError(c.config, info, hintDuplicateModuleImport,
|
||||
"duplicate import of '$1'; previous import here: $2" %
|
||||
[sym.name.s, c.config $ conflict.info])
|
||||
else:
|
||||
wrongRedefinition(c, info, sym.name.s, conflict.info, errGenerated)
|
||||
|
||||
@@ -438,12 +358,11 @@ proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) =
|
||||
if fn.kind notin OverloadableSyms:
|
||||
internalError(c.config, fn.info, "addOverloadableSymAt")
|
||||
return
|
||||
if fn.name.id != ord(wUnderscore):
|
||||
let check = strTableGet(scope.symbols, fn.name)
|
||||
if check != nil and check.kind notin OverloadableSyms:
|
||||
wrongRedefinition(c, fn.info, fn.name.s, check.info)
|
||||
else:
|
||||
scope.addSym(fn)
|
||||
let check = strTableGet(scope.symbols, fn.name)
|
||||
if check != nil and check.kind notin OverloadableSyms:
|
||||
wrongRedefinition(c, fn.info, fn.name.s, check.info)
|
||||
else:
|
||||
scope.addSym(fn)
|
||||
|
||||
proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) =
|
||||
## adds an overloadable symbol on the scope and the interface if appropriate
|
||||
@@ -480,6 +399,13 @@ proc mergeShadowScope*(c: PContext) =
|
||||
else:
|
||||
c.addInterfaceDecl(sym)
|
||||
|
||||
when false:
|
||||
# `nimfix` used to call `altSpelling` and prettybase.replaceDeprecated(n.info, ident, alt)
|
||||
proc altSpelling(c: PContext, x: PIdent): PIdent =
|
||||
case x.s[0]
|
||||
of 'A'..'Z': result = getIdent(c.cache, toLowerAscii(x.s[0]) & x.s.substr(1))
|
||||
of 'a'..'z': result = getIdent(c.cache, toLowerAscii(x.s[0]) & x.s.substr(1))
|
||||
else: result = x
|
||||
|
||||
import std/editdistance, heapqueue
|
||||
|
||||
@@ -489,7 +415,7 @@ type SpellCandidate = object
|
||||
msg: string
|
||||
sym: PSym
|
||||
|
||||
template toOrderTup(a: SpellCandidate): (int, int, string) =
|
||||
template toOrderTup(a: SpellCandidate): auto =
|
||||
# `dist` is first, to favor nearby matches
|
||||
# `depth` is next, to favor nearby enclosing scopes among ties
|
||||
# `sym.name.s` is last, to make the list ordered and deterministic among ties
|
||||
@@ -502,7 +428,7 @@ proc mustFixSpelling(c: PContext): bool {.inline.} =
|
||||
result = c.config.spellSuggestMax != 0 and c.compilesContextId == 0
|
||||
# don't slowdown inside compiles()
|
||||
|
||||
proc fixSpelling(c: PContext, ident: PIdent, result: var string) =
|
||||
proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) =
|
||||
## when we cannot find the identifier, suggest nearby spellings
|
||||
var list = initHeapQueue[SpellCandidate]()
|
||||
let name0 = ident.s.nimIdentNormalize
|
||||
@@ -512,29 +438,29 @@ proc fixSpelling(c: PContext, ident: PIdent, result: var string) =
|
||||
let dist = editDistance(name0, sym.name.s.nimIdentNormalize)
|
||||
var msg: string
|
||||
msg.add "\n ($1, $2): '$3'" % [$dist, $depth, sym.name.s]
|
||||
addDeclaredLoc(msg, c.config, sym) # `msg` needed for deterministic ordering.
|
||||
list.push SpellCandidate(dist: dist, depth: depth, msg: msg, sym: sym)
|
||||
|
||||
if list.len == 0: return
|
||||
let e0 = list[0]
|
||||
var
|
||||
count = 0
|
||||
last: PIdent = nil
|
||||
var count = 0
|
||||
while true:
|
||||
# pending https://github.com/timotheecour/Nim/issues/373 use more efficient `itemsSorted`.
|
||||
if list.len == 0: break
|
||||
let e = list.pop()
|
||||
if c.config.spellSuggestMax == spellSuggestSecretSauce:
|
||||
const
|
||||
minLengthForSuggestion = 4
|
||||
maxCount = 3 # avoids ton of matches; three counts for equal distances
|
||||
if e.dist > e0.dist or count >= maxCount or name0.len < minLengthForSuggestion: break
|
||||
smallThres = 2
|
||||
maxCountForSmall = 4
|
||||
# avoids ton of operator matches when mis-matching short symbols such as `i`
|
||||
# other heuristics could be devised, such as only suggesting operators if `name0`
|
||||
# is an operator (likewise with non-operators).
|
||||
if e.dist > e0.dist or (name0.len <= smallThres and count >= maxCountForSmall): break
|
||||
elif count >= c.config.spellSuggestMax: break
|
||||
if count == 0:
|
||||
result.add "\ncandidates (edit distance, scope distance); see '--spellSuggest': "
|
||||
if e.sym.name != last:
|
||||
result.add e.msg
|
||||
count.inc
|
||||
last = e.sym.name
|
||||
result.add e.msg
|
||||
count.inc
|
||||
|
||||
proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PSym =
|
||||
var err = "ambiguous identifier: '" & s.name.s & "'"
|
||||
@@ -557,66 +483,51 @@ proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PS
|
||||
amb = false
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
|
||||
var amb: bool = false
|
||||
var amb: bool
|
||||
discard errorUseQualifier(c, info, s, amb)
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]; prefix = "use one of") =
|
||||
proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
|
||||
var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
|
||||
var i = 0
|
||||
for candidate in candidates:
|
||||
if i == 0: err.add " -- $1 the following:\n" % prefix
|
||||
if i == 0: err.add " -- use one of the following:\n"
|
||||
else: err.add "\n"
|
||||
err.add " " & candidate.owner.name.s & "." & candidate.name.s
|
||||
err.add ": " & typeToString(candidate.typ)
|
||||
inc i
|
||||
localError(c.config, info, errGenerated, err)
|
||||
|
||||
proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
|
||||
var candidates = newSeq[PSym](choices.len)
|
||||
let prefix = if choices[0].typ.kind != tyProc: "use one of" else: "you need a helper proc to disambiguate"
|
||||
for i, n in choices:
|
||||
candidates[i] = n.sym
|
||||
errorUseQualifier(c, info, candidates, prefix)
|
||||
|
||||
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
|
||||
var err: string
|
||||
if name == "_":
|
||||
err = "the special identifier '_' is ignored in declarations and cannot be used"
|
||||
else:
|
||||
err = "undeclared identifier: '" & name & "'"
|
||||
if "`gensym" in name:
|
||||
err.add "; if declared in a template, this identifier may be inconsistently marked inject or gensym"
|
||||
if extra.len != 0:
|
||||
err.add extra
|
||||
if c.recursiveDep.len > 0:
|
||||
err.add "\nThis might be caused by a recursive module dependency:\n"
|
||||
err.add c.recursiveDep
|
||||
# prevent excessive errors for 'nim check'
|
||||
c.recursiveDep = ""
|
||||
var err = "undeclared identifier: '" & name & "'" & extra
|
||||
if c.recursiveDep.len > 0:
|
||||
err.add "\nThis might be caused by a recursive module dependency:\n"
|
||||
err.add c.recursiveDep
|
||||
# prevent excessive errors for 'nim check'
|
||||
c.recursiveDep = ""
|
||||
localError(c.config, info, errGenerated, err)
|
||||
|
||||
proc errorUndeclaredIdentifierHint*(c: PContext; ident: PIdent; info: TLineInfo): PSym =
|
||||
proc errorUndeclaredIdentifierHint*(c: PContext; n: PNode, ident: PIdent): PSym =
|
||||
var extra = ""
|
||||
if c.mustFixSpelling: fixSpelling(c, ident, extra)
|
||||
errorUndeclaredIdentifier(c, info, ident.s, extra)
|
||||
result = errorSym(c, ident, info)
|
||||
if c.mustFixSpelling: fixSpelling(c, n, ident, extra)
|
||||
errorUndeclaredIdentifier(c, n.info, ident.s, extra)
|
||||
result = errorSym(c, n)
|
||||
|
||||
proc lookUp*(c: PContext, n: PNode): PSym =
|
||||
# Looks up a symbol. Generates an error in case of nil.
|
||||
var amb = false
|
||||
case n.kind
|
||||
of nkIdent:
|
||||
result = searchInScopes(c, n.ident, amb)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n.ident, n.info)
|
||||
result = searchInScopes(c, n.ident, amb).skipAlias(n, c.config)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n, n.ident)
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
of nkAccQuoted:
|
||||
var ident = considerQuotedIdent(c, n)
|
||||
result = searchInScopes(c, ident, amb)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, ident, n.info)
|
||||
result = searchInScopes(c, ident, amb).skipAlias(n, c.config)
|
||||
if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident)
|
||||
else:
|
||||
internalError(c.config, n.info, "lookUp")
|
||||
return nil
|
||||
return
|
||||
if amb:
|
||||
#contains(c.ambiguousSymbols, result.id):
|
||||
result = errorUseQualifier(c, n.info, result, amb)
|
||||
@@ -627,45 +538,36 @@ type
|
||||
TLookupFlag* = enum
|
||||
checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
|
||||
|
||||
const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
|
||||
|
||||
proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind]): seq[PSym] =
|
||||
result = searchInScopesFilterBy(c, ident, filter)
|
||||
if result.len == 0:
|
||||
result.add allPureEnumFields(c, ident)
|
||||
|
||||
proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
|
||||
case n.kind
|
||||
of nkIdent, nkAccQuoted:
|
||||
var amb = false
|
||||
var ident = considerQuotedIdent(c, n)
|
||||
if checkModule in flags:
|
||||
result = searchInScopes(c, ident, amb)
|
||||
if result == nil:
|
||||
let candidates = allPureEnumFields(c, ident)
|
||||
if candidates.len > 0:
|
||||
result = candidates[0]
|
||||
amb = candidates.len > 1
|
||||
if amb and checkAmbiguity in flags:
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
result = searchInScopes(c, ident, amb).skipAlias(n, c.config)
|
||||
else:
|
||||
let candidates = lookUpCandidates(c, ident, allExceptModule)
|
||||
let candidates = searchInScopesFilterBy(c, ident, allExceptModule) #.skipAlias(n, c.config)
|
||||
if candidates.len > 0:
|
||||
result = candidates[0]
|
||||
amb = candidates.len > 1
|
||||
if amb and checkAmbiguity in flags:
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
else:
|
||||
result = nil
|
||||
if result == nil:
|
||||
let candidates = allPureEnumFields(c, ident)
|
||||
if candidates.len > 0:
|
||||
result = candidates[0]
|
||||
amb = candidates.len > 1
|
||||
if amb and checkAmbiguity in flags:
|
||||
errorUseQualifier(c, n.info, candidates)
|
||||
|
||||
if result == nil and checkUndeclared in flags:
|
||||
result = errorUndeclaredIdentifierHint(c, ident, n.info)
|
||||
result = errorUndeclaredIdentifierHint(c, n, ident)
|
||||
elif checkAmbiguity in flags and result != nil and amb:
|
||||
result = errorUseQualifier(c, n.info, result, amb)
|
||||
c.isAmbiguous = amb
|
||||
of nkSym:
|
||||
result = n.sym
|
||||
of nkOpenSym:
|
||||
result = qualifiedLookUp(c, n[0], flags)
|
||||
of nkDotExpr:
|
||||
result = nil
|
||||
var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
|
||||
@@ -677,20 +579,13 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
ident = considerQuotedIdent(c, n[1])
|
||||
if ident != nil:
|
||||
if m == c.module:
|
||||
result = strTableGet(c.topLevelScope.symbols, ident)
|
||||
result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
|
||||
else:
|
||||
if c.importModuleLookup.getOrDefault(m.name.id).len > 1:
|
||||
var amb: bool = false
|
||||
result = errorUseQualifier(c, n.info, m, amb)
|
||||
else:
|
||||
result = someSym(c.graph, m, ident)
|
||||
result = someSym(c.graph, m, ident).skipAlias(n, c.config)
|
||||
if result == nil and checkUndeclared in flags:
|
||||
result = errorUndeclaredIdentifierHint(c, ident, n[1].info)
|
||||
result = errorUndeclaredIdentifierHint(c, n[1], ident)
|
||||
elif n[1].kind == nkSym:
|
||||
result = n[1].sym
|
||||
if result.owner != nil and result.owner != m and checkUndeclared in flags:
|
||||
# dotExpr in templates can end up here
|
||||
result = errorUndeclaredIdentifierHint(c, result.name, n[1].info)
|
||||
elif checkUndeclared in flags and
|
||||
n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
|
||||
localError(c.config, n[1].info, "identifier expected, but got: " &
|
||||
@@ -702,10 +597,6 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
|
||||
if result != nil and result.kind == skStub: loadStub(result)
|
||||
|
||||
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
if n.kind == nkOpenSym:
|
||||
# maybe the logic in semexprs should be mirrored here instead
|
||||
# for now it only seems this is called for `pickSym` in `getTypeIdent`
|
||||
return initOverloadIter(o, c, n[0])
|
||||
o.importIdx = -1
|
||||
o.marked = initIntSet()
|
||||
case n.kind
|
||||
@@ -714,7 +605,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
var scope = c.currentScope
|
||||
o.mode = oimNoQualifier
|
||||
while true:
|
||||
result = initIdentIter(o.it, scope.symbols, ident)
|
||||
result = initIdentIter(o.it, scope.symbols, ident).skipAlias(n, c.config)
|
||||
if result != nil:
|
||||
o.currentScope = scope
|
||||
break
|
||||
@@ -722,7 +613,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
scope = scope.parent
|
||||
if scope == nil:
|
||||
for i in 0..c.imports.high:
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph).skipAlias(n, c.config)
|
||||
if result != nil:
|
||||
o.currentScope = nil
|
||||
o.importIdx = i
|
||||
@@ -745,10 +636,10 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
if o.m == c.module:
|
||||
# a module may access its private members:
|
||||
result = initIdentIter(o.it, c.topLevelScope.symbols,
|
||||
ident)
|
||||
ident).skipAlias(n, c.config)
|
||||
o.mode = oimSelfModule
|
||||
else:
|
||||
result = initModuleIter(o.mit, c.graph, o.m, ident)
|
||||
result = initModuleIter(o.mit, c.graph, o.m, ident).skipAlias(n, c.config)
|
||||
else:
|
||||
noidentError(c.config, n[1], n)
|
||||
result = errorSym(c, n[1])
|
||||
@@ -781,7 +672,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym
|
||||
var idx = o.importIdx+1
|
||||
o.importIdx = c.imports.len # assume the other imported modules lack this symbol too
|
||||
while idx < c.imports.len:
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph).skipAlias(n, c.config)
|
||||
if result != nil:
|
||||
# oh, we were wrong, some other module had the symbol, so remember that:
|
||||
o.importIdx = idx
|
||||
@@ -791,7 +682,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym
|
||||
proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym =
|
||||
assert o.currentScope == nil
|
||||
while o.importIdx < c.imports.len:
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config)
|
||||
#while result != nil and result.id in o.marked:
|
||||
# result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx])
|
||||
if result != nil:
|
||||
@@ -806,29 +697,29 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
of oimNoQualifier:
|
||||
if o.currentScope != nil:
|
||||
assert o.importIdx < 0
|
||||
result = nextIdentIter(o.it, o.currentScope.symbols)
|
||||
result = nextIdentIter(o.it, o.currentScope.symbols).skipAlias(n, c.config)
|
||||
while result == nil:
|
||||
o.currentScope = o.currentScope.parent
|
||||
if o.currentScope != nil:
|
||||
result = initIdentIter(o.it, o.currentScope.symbols, o.it.name)
|
||||
result = initIdentIter(o.it, o.currentScope.symbols, o.it.name).skipAlias(n, c.config)
|
||||
# BUGFIX: o.it.name <-> n.ident
|
||||
else:
|
||||
o.importIdx = 0
|
||||
if c.imports.len > 0:
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph)
|
||||
result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config)
|
||||
if result == nil:
|
||||
result = nextOverloadIterImports(o, c, n)
|
||||
break
|
||||
elif o.importIdx < c.imports.len:
|
||||
result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph)
|
||||
result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config)
|
||||
if result == nil:
|
||||
result = nextOverloadIterImports(o, c, n)
|
||||
else:
|
||||
result = nil
|
||||
of oimSelfModule:
|
||||
result = nextIdentIter(o.it, c.topLevelScope.symbols)
|
||||
result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config)
|
||||
of oimOtherModule:
|
||||
result = nextModuleIter(o.mit, c.graph)
|
||||
result = nextModuleIter(o.mit, c.graph).skipAlias(n, c.config)
|
||||
of oimSymChoice:
|
||||
if o.symChoiceIndex < n.len:
|
||||
result = n[o.symChoiceIndex].sym
|
||||
@@ -839,12 +730,12 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
o.mode = oimSymChoiceLocalLookup
|
||||
o.currentScope = c.currentScope
|
||||
result = firstIdentExcluding(o.it, o.currentScope.symbols,
|
||||
n[0].sym.name, o.marked)
|
||||
n[0].sym.name, o.marked).skipAlias(n, c.config)
|
||||
while result == nil:
|
||||
o.currentScope = o.currentScope.parent
|
||||
if o.currentScope != nil:
|
||||
result = firstIdentExcluding(o.it, o.currentScope.symbols,
|
||||
n[0].sym.name, o.marked)
|
||||
n[0].sym.name, o.marked).skipAlias(n, c.config)
|
||||
else:
|
||||
o.importIdx = 0
|
||||
result = symChoiceExtension(o, c, n)
|
||||
@@ -853,12 +744,12 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
incl o.marked, result.id
|
||||
of oimSymChoiceLocalLookup:
|
||||
if o.currentScope != nil:
|
||||
result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked)
|
||||
result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked).skipAlias(n, c.config)
|
||||
while result == nil:
|
||||
o.currentScope = o.currentScope.parent
|
||||
if o.currentScope != nil:
|
||||
result = firstIdentExcluding(o.it, o.currentScope.symbols,
|
||||
n[0].sym.name, o.marked)
|
||||
n[0].sym.name, o.marked).skipAlias(n, c.config)
|
||||
else:
|
||||
o.importIdx = 0
|
||||
result = symChoiceExtension(o, c, n)
|
||||
@@ -867,10 +758,10 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
|
||||
incl o.marked, result.id
|
||||
|
||||
elif o.importIdx < c.imports.len:
|
||||
result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph)
|
||||
result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config)
|
||||
#assert result.id notin o.marked
|
||||
#while result != nil and result.id in o.marked:
|
||||
# result = nextIdentIter(o.it, c.imports[o.importIdx])
|
||||
# result = nextIdentIter(o.it, c.imports[o.importIdx]).skipAlias(n, c.config)
|
||||
if result == nil:
|
||||
inc o.importIdx
|
||||
result = symChoiceExtension(o, c, n)
|
||||
|
||||
@@ -15,9 +15,6 @@ const
|
||||
import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
|
||||
lineinfos
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
proc newDeref*(n: PNode): PNode {.inline.} =
|
||||
result = newNodeIT(nkHiddenDeref, n.info, n.typ[0])
|
||||
result.add n
|
||||
@@ -74,20 +71,14 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: P
|
||||
let value = n.lastSon
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
var tempAsNode: PNode
|
||||
let avoidTemp = value.kind == nkSym
|
||||
if avoidTemp:
|
||||
tempAsNode = value
|
||||
else:
|
||||
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
|
||||
owner, value.info, g.config.options)
|
||||
temp.typ = skipTypes(value.typ, abstractInst)
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
tempAsNode = newSymNode(temp)
|
||||
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
|
||||
owner, value.info, g.config.options)
|
||||
temp.typ = skipTypes(value.typ, abstractInst)
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
|
||||
var v = newNodeI(nkVarSection, value.info)
|
||||
if not avoidTemp:
|
||||
v.addVar(tempAsNode, value)
|
||||
let tempAsNode = newSymNode(temp)
|
||||
v.addVar(tempAsNode, value)
|
||||
result.add(v)
|
||||
|
||||
for i in 0..<n.len-2:
|
||||
@@ -100,7 +91,7 @@ proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): P
|
||||
## freely, multiple times. This is frequently required and such a builtin would also be
|
||||
## handy to have in macros.nim. The value that can be reused is 'result.lastSon'!
|
||||
result = newNodeIT(nkStmtListExpr, value.info, value.typ)
|
||||
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
|
||||
var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
|
||||
owner, value.info, g.config.options)
|
||||
temp.typ = skipTypes(value.typ, abstractInst)
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
@@ -122,10 +113,29 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
|
||||
proc newTryFinally*(body, final: PNode): PNode =
|
||||
result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
|
||||
|
||||
proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
||||
let value = n.lastSon
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
|
||||
var temp = newSym(skTemp, getIdent(g.cache, "_"), nextSymId(idgen), owner, value.info, owner.options)
|
||||
var v = newNodeI(nkLetSection, value.info)
|
||||
let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
|
||||
|
||||
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
|
||||
vpart[0] = tempAsNode
|
||||
vpart[1] = newNodeI(nkEmpty, value.info)
|
||||
vpart[2] = value
|
||||
v.add vpart
|
||||
result.add(v)
|
||||
|
||||
let lhs = n[0]
|
||||
for i in 0..<lhs.len:
|
||||
result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempAsNode, i))
|
||||
|
||||
proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
|
||||
result = newNodeI(nkStmtList, n.info)
|
||||
# note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
|
||||
var temp = newSym(skVar, getIdent(g.cache, genPrefix), idgen, owner, n.info, owner.options)
|
||||
var temp = newSym(skVar, getIdent(g.cache, genPrefix), nextSymId(idgen), owner, n.info, owner.options)
|
||||
temp.typ = n[1].typ
|
||||
incl(temp.flags, sfFromGeneric)
|
||||
incl(temp.flags, sfGenSym)
|
||||
@@ -152,7 +162,8 @@ proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo
|
||||
rawAddSon(result, getCompilerProc(g, "RootObj").typ)
|
||||
result.n = newNodeI(nkRecList, info)
|
||||
let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s),
|
||||
idgen, owner, info, owner.options)
|
||||
nextSymId(idgen),
|
||||
owner, info, owner.options)
|
||||
incl s.flags, sfAnon
|
||||
s.typ = result
|
||||
result.sym = s
|
||||
@@ -210,30 +221,25 @@ proc lookupInRecord(n: PNode, id: ItemId): PSym =
|
||||
if n.sym.itemId.module == id.module and n.sym.itemId.item == -abs(id.item): result = n.sym
|
||||
else: discard
|
||||
|
||||
proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym =
|
||||
proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator) =
|
||||
# because of 'gensym' support, we have to mangle the name with its ID.
|
||||
# This is hacky but the clean solution is much more complex than it looks.
|
||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len),
|
||||
idgen, s.owner, s.info, s.options)
|
||||
nextSymId(idgen), s.owner, s.info, s.options)
|
||||
field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
||||
let t = skipIntLit(s.typ, idgen)
|
||||
field.typ = t
|
||||
if s.kind in {skLet, skVar, skField, skForVar}:
|
||||
#field.bitsize = s.bitsize
|
||||
field.alignment = s.alignment
|
||||
assert t.kind != tyTyped
|
||||
propagateToOwner(obj, t)
|
||||
field.position = obj.n.len
|
||||
# sfNoInit flag for skField is used in closureiterator codegen
|
||||
field.flags = s.flags * {sfCursor, sfNoInit}
|
||||
field.flags = s.flags * {sfCursor}
|
||||
obj.n.add newSymNode(field)
|
||||
fieldCheck()
|
||||
result = field
|
||||
|
||||
proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
|
||||
result = lookupInRecord(obj.n, s.itemId)
|
||||
if result == nil:
|
||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), idgen,
|
||||
var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), nextSymId(idgen),
|
||||
s.owner, s.info, s.options)
|
||||
field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
|
||||
let t = skipIntLit(s.typ, idgen)
|
||||
@@ -330,8 +336,7 @@ proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
|
||||
|
||||
proc callCodegenProc*(g: ModuleGraph; name: string;
|
||||
info: TLineInfo = unknownLineInfo;
|
||||
arg1: PNode = nil, arg2: PNode = nil,
|
||||
arg3: PNode = nil, optionalArgs: PNode = nil): PNode =
|
||||
arg1, arg2, arg3, optionalArgs: PNode = nil): PNode =
|
||||
result = newNodeI(nkCall, info)
|
||||
let sym = magicsys.getCompilerProc(g, name)
|
||||
if sym == nil:
|
||||
|
||||
@@ -26,8 +26,9 @@ proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
|
||||
result = systemModuleSym(g, getIdent(g.cache, name))
|
||||
if result == nil:
|
||||
localError(g.config, info, "system module needs: " & name)
|
||||
result = newSym(skError, getIdent(g.cache, name), g.idgen, g.systemModule, g.systemModule.info, {})
|
||||
result = newSym(skError, getIdent(g.cache, name), nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
|
||||
result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
|
||||
if result.kind == skAlias: result = result.owner
|
||||
|
||||
proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
|
||||
let id = getIdent(g.cache, name)
|
||||
@@ -38,7 +39,7 @@ proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSy
|
||||
result = r
|
||||
if result != nil: return result
|
||||
localError(g.config, info, "system module needs: " & name)
|
||||
result = newSym(skError, id, g.idgen, g.systemModule, g.systemModule.info, {})
|
||||
result = newSym(skError, id, nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
|
||||
result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
|
||||
|
||||
proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType =
|
||||
|
||||
@@ -13,28 +13,24 @@ when not defined(nimcore):
|
||||
{.error: "nimcore MUST be defined for Nim's core tooling".}
|
||||
|
||||
import
|
||||
std/[strutils, os, times, tables, with, json],
|
||||
std/[strutils, os, times, tables, sha1, with, json],
|
||||
llstream, ast, lexer, syntaxes, options, msgs,
|
||||
condsyms,
|
||||
idents, extccomp,
|
||||
sem, idents, passes, extccomp,
|
||||
cgen, nversion,
|
||||
platform, nimconf, depends,
|
||||
platform, nimconf, passaux, depends, vm,
|
||||
modules,
|
||||
modulegraphs, lineinfos, pathutils, vmprofiler
|
||||
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/[syncio, assertions]
|
||||
|
||||
import ic / [cbackend, integrity, navigator]
|
||||
from ic / ic import rodViewer
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
|
||||
import pipelines
|
||||
|
||||
when not defined(leanCompiler):
|
||||
import docgen
|
||||
import jsgen, docgen, docgen2
|
||||
|
||||
proc semanticPasses(g: ModuleGraph) =
|
||||
registerPass g, verbosePass
|
||||
registerPass g, semPass
|
||||
|
||||
proc writeDepsFile(g: ModuleGraph) =
|
||||
let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps")
|
||||
@@ -47,39 +43,13 @@ proc writeDepsFile(g: ModuleGraph) =
|
||||
f.writeLine(toFullPath(g.config, k))
|
||||
f.close()
|
||||
|
||||
proc writeCMakeDepsFile(conf: ConfigRef) =
|
||||
## write a list of C files for build systems like CMake.
|
||||
## only updated when the C file list changes.
|
||||
let fname = getNimcacheDir(conf) / conf.outFile.changeFileExt("cdeps")
|
||||
# generate output files list
|
||||
var cfiles: seq[string] = @[]
|
||||
for it in conf.toCompile: cfiles.add(it.cname.string)
|
||||
let fileset = cfiles.toCountTable()
|
||||
# read old cfiles list
|
||||
var fl: File
|
||||
var prevset = initCountTable[string]()
|
||||
if open(fl, fname.string, fmRead):
|
||||
for line in fl.lines: prevset.inc(line)
|
||||
fl.close()
|
||||
# write cfiles out
|
||||
if fileset != prevset:
|
||||
fl = open(fname.string, fmWrite)
|
||||
for line in cfiles: fl.writeLine(line)
|
||||
fl.close()
|
||||
|
||||
proc commandGenDepend(graph: ModuleGraph) =
|
||||
setPipeLinePass(graph, GenDependPass)
|
||||
compilePipelineProject(graph)
|
||||
semanticPasses(graph)
|
||||
registerPass(graph, gendependPass)
|
||||
compileProject(graph)
|
||||
let project = graph.config.projectFull
|
||||
writeDepsFile(graph)
|
||||
generateDot(graph, project)
|
||||
|
||||
# dot in graphivz tool kit is required
|
||||
let graphvizDotPath = findExe("dot")
|
||||
if graphvizDotPath.len == 0:
|
||||
quit("gendepend: Graphviz's tool dot is required," &
|
||||
"see https://graphviz.org/download for downloading")
|
||||
|
||||
execExternalProgram(graph.config, "dot -Tpng -o" &
|
||||
changeFileExt(project, "png").string &
|
||||
' ' & changeFileExt(project, "dot").string)
|
||||
@@ -88,13 +58,8 @@ proc commandCheck(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
conf.setErrorMaxHighMaybe
|
||||
defineSymbol(conf.symbols, "nimcheck")
|
||||
if optWasNimscript in conf.globalOptions:
|
||||
defineSymbol(conf.symbols, "nimscript")
|
||||
defineSymbol(conf.symbols, "nimconfig")
|
||||
elif conf.backend == backendJs:
|
||||
setTarget(conf.target, osJS, cpuJS)
|
||||
setPipeLinePass(graph, SemPass)
|
||||
compilePipelineProject(graph)
|
||||
semanticPasses(graph) # use an empty backend for semantic checking only
|
||||
compileProject(graph)
|
||||
|
||||
if conf.symbolFiles != disabledSf:
|
||||
case conf.ideCmd
|
||||
@@ -108,20 +73,22 @@ when not defined(leanCompiler):
|
||||
proc commandDoc2(graph: ModuleGraph; ext: string) =
|
||||
handleDocOutputOptions graph.config
|
||||
graph.config.setErrorMaxHighMaybe
|
||||
semanticPasses(graph)
|
||||
case ext:
|
||||
of TexExt:
|
||||
setPipeLinePass(graph, Docgen2TexPass)
|
||||
of JsonExt:
|
||||
setPipeLinePass(graph, Docgen2JsonPass)
|
||||
of HtmlExt:
|
||||
setPipeLinePass(graph, Docgen2Pass)
|
||||
of TexExt: registerPass(graph, docgen2TexPass)
|
||||
of JsonExt: registerPass(graph, docgen2JsonPass)
|
||||
of HtmlExt: registerPass(graph, docgen2Pass)
|
||||
else: doAssert false, $ext
|
||||
compilePipelineProject(graph)
|
||||
compileProject(graph)
|
||||
finishDoc2Pass(graph.config.projectName)
|
||||
|
||||
proc commandCompileToC(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
extccomp.initVars(conf)
|
||||
semanticPasses(graph)
|
||||
if conf.symbolFiles == disabledSf:
|
||||
registerPass(graph, cgenPass)
|
||||
|
||||
if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
|
||||
if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
|
||||
# nothing changed
|
||||
@@ -131,11 +98,7 @@ proc commandCompileToC(graph: ModuleGraph) =
|
||||
if not extccomp.ccHasSaneOverflow(conf):
|
||||
conf.symbols.defineSymbol("nimEmulateOverflowChecks")
|
||||
|
||||
if conf.symbolFiles == disabledSf:
|
||||
setPipeLinePass(graph, CgenPass)
|
||||
else:
|
||||
setPipeLinePass(graph, SemPass)
|
||||
compilePipelineProject(graph)
|
||||
compileProject(graph)
|
||||
if graph.config.errorCounter > 0:
|
||||
return # issue #9933
|
||||
if conf.symbolFiles == disabledSf:
|
||||
@@ -151,11 +114,9 @@ proc commandCompileToC(graph: ModuleGraph) =
|
||||
extccomp.callCCompiler(conf)
|
||||
# for now we do not support writing out a .json file with the build instructions when HCR is on
|
||||
if not conf.hcrOn:
|
||||
extccomp.writeJsonBuildInstructions(conf, graph.cachedFiles)
|
||||
extccomp.writeJsonBuildInstructions(conf)
|
||||
if optGenScript in graph.config.globalOptions:
|
||||
writeDepsFile(graph)
|
||||
if optGenCDeps in graph.config.globalOptions:
|
||||
writeCMakeDepsFile(conf)
|
||||
|
||||
proc commandJsonScript(graph: ModuleGraph) =
|
||||
extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile)
|
||||
@@ -168,35 +129,41 @@ proc commandCompileToJS(graph: ModuleGraph) =
|
||||
conf.exc = excCpp
|
||||
setTarget(conf.target, osJS, cpuJS)
|
||||
defineSymbol(conf.symbols, "ecmascript") # For backward compatibility
|
||||
setPipeLinePass(graph, JSgenPass)
|
||||
compilePipelineProject(graph)
|
||||
semanticPasses(graph)
|
||||
registerPass(graph, JSgenPass)
|
||||
compileProject(graph)
|
||||
if optGenScript in conf.globalOptions:
|
||||
writeDepsFile(graph)
|
||||
|
||||
proc commandInteractive(graph: ModuleGraph) =
|
||||
graph.config.setErrorMaxHighMaybe
|
||||
proc interactivePasses(graph: ModuleGraph) =
|
||||
initDefines(graph.config.symbols)
|
||||
defineSymbol(graph.config.symbols, "nimscript")
|
||||
# note: seems redundant with -d:nimHasLibFFI
|
||||
when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
|
||||
setPipeLinePass(graph, InterpreterPass)
|
||||
compilePipelineSystemModule(graph)
|
||||
registerPass(graph, verbosePass)
|
||||
registerPass(graph, semPass)
|
||||
registerPass(graph, evalPass)
|
||||
|
||||
proc commandInteractive(graph: ModuleGraph) =
|
||||
graph.config.setErrorMaxHighMaybe
|
||||
interactivePasses(graph)
|
||||
compileSystemModule(graph)
|
||||
if graph.config.commandArgs.len > 0:
|
||||
discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
|
||||
discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
|
||||
else:
|
||||
var m = graph.makeStdinModule()
|
||||
incl(m.flags, sfMainModule)
|
||||
var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
|
||||
let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config))
|
||||
discard processPipelineModule(graph, m, idgen, s)
|
||||
processModule(graph, m, idgen, s)
|
||||
|
||||
proc commandScan(cache: IdentCache, config: ConfigRef) =
|
||||
var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
|
||||
var stream = llStreamOpen(f, fmRead)
|
||||
if stream != nil:
|
||||
var
|
||||
L: Lexer = default(Lexer)
|
||||
tok: Token = default(Token)
|
||||
L: Lexer
|
||||
tok: Token
|
||||
initToken(tok)
|
||||
openLexer(L, f, stream, cache, config)
|
||||
while true:
|
||||
@@ -243,6 +210,8 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
let conf = graph.config
|
||||
let cache = graph.cache
|
||||
|
||||
# In "nim serve" scenario, each command must reset the registered passes
|
||||
clearPasses(graph)
|
||||
conf.lastCmdTime = epochTime()
|
||||
conf.searchPaths.add(conf.libpath)
|
||||
|
||||
@@ -298,8 +267,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf)
|
||||
else: conf.projectPath
|
||||
doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee
|
||||
if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex}:
|
||||
ret = ret / htmldocsDir
|
||||
if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex}: ret = ret / htmldocsDir
|
||||
conf.outDir = ret
|
||||
|
||||
## process all commands
|
||||
@@ -316,6 +284,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
of cmdDoc0: docLikeCmd commandDoc(cache, conf)
|
||||
of cmdDoc:
|
||||
docLikeCmd():
|
||||
conf.setNoteDefaults(warnLockLevel, false) # issue #13218
|
||||
conf.setNoteDefaults(warnRstRedefinitionOfLabel, false) # issue #13218
|
||||
# because currently generates lots of false positives due to conflation
|
||||
# of labels links in doc comments, e.g. for random.rand:
|
||||
@@ -324,7 +293,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
commandDoc2(graph, HtmlExt)
|
||||
if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions:
|
||||
commandBuildIndex(conf, $conf.outDir)
|
||||
of cmdRst2html, cmdMd2html:
|
||||
of cmdRst2html:
|
||||
# XXX: why are warnings disabled by default for rst2html and rst2tex?
|
||||
for warn in rstWarnings:
|
||||
conf.setNoteDefaults(warn, true)
|
||||
@@ -333,24 +302,20 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
conf.quitOrRaise "compiler wasn't built with documentation generator"
|
||||
else:
|
||||
loadConfigs(DocConfig, cache, conf, graph.idgen)
|
||||
commandRst2Html(cache, conf, preferMarkdown = (conf.cmd == cmdMd2html))
|
||||
of cmdRst2tex, cmdMd2tex, cmdDoc2tex:
|
||||
commandRst2Html(cache, conf)
|
||||
of cmdRst2tex, cmdDoc2tex:
|
||||
for warn in rstWarnings:
|
||||
conf.setNoteDefaults(warn, true)
|
||||
when defined(leanCompiler):
|
||||
conf.quitOrRaise "compiler wasn't built with documentation generator"
|
||||
else:
|
||||
if conf.cmd in {cmdRst2tex, cmdMd2tex}:
|
||||
if conf.cmd == cmdRst2tex:
|
||||
loadConfigs(DocTexConfig, cache, conf, graph.idgen)
|
||||
commandRst2TeX(cache, conf, preferMarkdown = (conf.cmd == cmdMd2tex))
|
||||
commandRst2TeX(cache, conf)
|
||||
else:
|
||||
docLikeCmd commandDoc2(graph, TexExt)
|
||||
of cmdJsondoc0: docLikeCmd commandJson(cache, conf)
|
||||
of cmdJsondoc:
|
||||
docLikeCmd():
|
||||
commandDoc2(graph, JsonExt)
|
||||
if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions:
|
||||
commandBuildIndexJson(conf, $conf.outDir)
|
||||
of cmdJsondoc: docLikeCmd commandDoc2(graph, JsonExt)
|
||||
of cmdCtags: docLikeCmd commandTags(cache, conf)
|
||||
of cmdBuildindex: docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile)
|
||||
of cmdGendepend: commandGenDepend(graph)
|
||||
@@ -398,8 +363,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook})
|
||||
|
||||
for it in conf.searchPaths: msgWriteln(conf, it.string)
|
||||
of cmdCheck:
|
||||
commandCheck(graph)
|
||||
of cmdCheck: commandCheck(graph)
|
||||
of cmdParse:
|
||||
wantMainModule(conf)
|
||||
discard parseFile(conf.projectMainIdx, cache, conf)
|
||||
@@ -417,7 +381,7 @@ proc mainCommand*(graph: ModuleGraph) =
|
||||
of cmdJsonscript:
|
||||
setOutFile(graph.config)
|
||||
commandJsonScript(graph)
|
||||
of cmdUnknown, cmdNone, cmdIdeTools:
|
||||
of cmdUnknown, cmdNone, cmdIdeTools, cmdNimfix:
|
||||
rawMessage(conf, errGenerated, "invalid command: " & conf.command)
|
||||
|
||||
if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}:
|
||||
|
||||
@@ -11,14 +11,10 @@
|
||||
## represents a complete Nim project. Single modules can either be kept in RAM
|
||||
## or stored in a rod-file.
|
||||
|
||||
import std/[intsets, tables, hashes, strtabs]
|
||||
import ../dist/checksums/src/checksums/md5
|
||||
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages
|
||||
import intsets, tables, hashes, md5
|
||||
import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils
|
||||
import ic / [packed_ast, ic]
|
||||
|
||||
when defined(nimPreviewSlimSystem):
|
||||
import std/assertions
|
||||
|
||||
type
|
||||
SigHash* = distinct MD5Digest
|
||||
|
||||
@@ -54,23 +50,6 @@ type
|
||||
concreteTypes*: seq[FullId]
|
||||
inst*: PInstantiation
|
||||
|
||||
SymInfoPair* = object
|
||||
sym*: PSym
|
||||
info*: TLineInfo
|
||||
isDecl*: bool
|
||||
|
||||
PipelinePass* = enum
|
||||
NonePass
|
||||
SemPass
|
||||
JSgenPass
|
||||
CgenPass
|
||||
EvalPass
|
||||
InterpreterPass
|
||||
GenDependPass
|
||||
Docgen2TexPass
|
||||
Docgen2JsonPass
|
||||
Docgen2Pass
|
||||
|
||||
ModuleGraph* {.acyclic.} = ref object
|
||||
ifaces*: seq[Iface] ## indexed by int32 fileIdx
|
||||
packed*: PackedModuleGraph
|
||||
@@ -78,14 +57,14 @@ type
|
||||
|
||||
typeInstCache*: Table[ItemId, seq[LazyType]] # A symbol's ItemId.
|
||||
procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
|
||||
attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc.
|
||||
attachedOps*: array[TTypeAttachedOp, Table[ItemId, PSym]] # Type ID, destructors, etc.
|
||||
methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
|
||||
memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual and ctor so far)
|
||||
enumToStringProcs*: Table[ItemId, LazySym]
|
||||
emittedTypeInfo*: Table[string, FileIndex]
|
||||
|
||||
startupPackedConfig*: PackedConfig
|
||||
packageSyms*: TStrTable
|
||||
modulesPerPackage*: Table[ItemId, TStrTable]
|
||||
deps*: IntSet # the dependency graph or potentially its transitive closure.
|
||||
importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
|
||||
suggestMode*: bool # whether we are in nimsuggest mode or not.
|
||||
@@ -102,8 +81,6 @@ type
|
||||
doStopCompile*: proc(): bool {.closure.}
|
||||
usageSym*: PSym # for nimsuggest
|
||||
owners*: seq[PSym]
|
||||
suggestSymbols*: Table[FileIndex, seq[SymInfoPair]]
|
||||
suggestErrors*: Table[FileIndex, seq[Suggest]]
|
||||
methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
|
||||
systemModule*: PSym
|
||||
sysTypes*: array[TTypeKind, PType]
|
||||
@@ -119,7 +96,6 @@ type
|
||||
cacheCounters*: Table[string, BiggestInt] # IC: implemented
|
||||
cacheTables*: Table[string, BTree[string, PNode]] # IC: implemented
|
||||
passes*: seq[TPass]
|
||||
pipelinePass*: PipelinePass
|
||||
onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
||||
onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
||||
onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
|
||||
@@ -129,8 +105,6 @@ type
|
||||
idgen*: IdGenerator
|
||||
operators*: Operators
|
||||
|
||||
cachedFiles*: StringTableRef
|
||||
|
||||
TPassContext* = object of RootObj # the pass's context
|
||||
idgen*: IdGenerator
|
||||
PPassContext* = ref TPassContext
|
||||
@@ -251,7 +225,7 @@ proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym =
|
||||
iterator allSyms*(g: ModuleGraph; m: PSym): PSym =
|
||||
let importHidden = optImportHidden in m.options
|
||||
if isCachedModule(g, m):
|
||||
var rodIt: RodIter = default(RodIter)
|
||||
var rodIt: RodIter
|
||||
var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position, importHidden)
|
||||
while r != nil:
|
||||
yield r
|
||||
@@ -272,7 +246,7 @@ proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
|
||||
result = someSym(g, g.systemModule, name)
|
||||
|
||||
iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym =
|
||||
var mi: ModuleIter = default(ModuleIter)
|
||||
var mi: ModuleIter
|
||||
var r = initModuleIter(mi, g, g.systemModule, name)
|
||||
while r != nil:
|
||||
yield r
|
||||
@@ -303,13 +277,6 @@ proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation =
|
||||
t.inst = result
|
||||
assert result != nil
|
||||
|
||||
proc resolveAttachedOp(g: ModuleGraph; t: var LazySym): PSym =
|
||||
result = t.sym
|
||||
if result == nil:
|
||||
result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
|
||||
t.sym = result
|
||||
assert result != nil
|
||||
|
||||
iterator typeInstCacheItems*(g: ModuleGraph; s: PSym): PType =
|
||||
if g.typeInstCache.contains(s.itemId):
|
||||
let x = addr(g.typeInstCache[s.itemId])
|
||||
@@ -325,25 +292,22 @@ iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation =
|
||||
proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
|
||||
## returns the requested attached operation for type `t`. Can return nil
|
||||
## if no such operation exists.
|
||||
if g.attachedOps[op].contains(t.itemId):
|
||||
result = resolveAttachedOp(g, g.attachedOps[op][t.itemId])
|
||||
else:
|
||||
result = nil
|
||||
result = g.attachedOps[op].getOrDefault(t.itemId)
|
||||
|
||||
proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
||||
## we also need to record this to the packed module.
|
||||
g.attachedOps[op][t.itemId] = LazySym(sym: value)
|
||||
g.attachedOps[op][t.itemId] = value
|
||||
|
||||
proc setAttachedOpPartial*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
||||
## we also need to record this to the packed module.
|
||||
g.attachedOps[op][t.itemId] = LazySym(sym: value)
|
||||
g.attachedOps[op][t.itemId] = value
|
||||
# XXX Also add to the packed module!
|
||||
|
||||
proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
|
||||
if g.config.symbolFiles != disabledSf:
|
||||
assert module < g.encoders.len
|
||||
assert isActive(g.encoders[module])
|
||||
toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk)
|
||||
#storeAttachedProcDef(t, op, value, g.encoders[module], g.packed[module].fromDisk)
|
||||
|
||||
proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
|
||||
result = resolveSym(g, g.enumToStringProcs[t.itemId])
|
||||
@@ -404,9 +368,20 @@ template getPContext(): untyped =
|
||||
when c is PContext: c
|
||||
else: c.c
|
||||
|
||||
when defined(nimsuggest):
|
||||
template onUse*(info: TLineInfo; s: PSym) = discard
|
||||
template onDefResolveForward*(info: TLineInfo; s: PSym) = discard
|
||||
when defined(nimfind):
|
||||
template onUse*(info: TLineInfo; s: PSym) =
|
||||
let c = getPContext()
|
||||
if c.graph.onUsage != nil: c.graph.onUsage(c.graph, s, info)
|
||||
|
||||
template onDef*(info: TLineInfo; s: PSym) =
|
||||
let c = getPContext()
|
||||
if c.graph.onDefinition != nil: c.graph.onDefinition(c.graph, s, info)
|
||||
|
||||
template onDefResolveForward*(info: TLineInfo; s: PSym) =
|
||||
let c = getPContext()
|
||||
if c.graph.onDefinitionResolveForward != nil:
|
||||
c.graph.onDefinitionResolveForward(c.graph, s, info)
|
||||
|
||||
else:
|
||||
template onUse*(info: TLineInfo; s: PSym) = discard
|
||||
template onDef*(info: TLineInfo; s: PSym) = discard
|
||||
@@ -416,7 +391,7 @@ proc stopCompile*(g: ModuleGraph): bool {.inline.} =
|
||||
result = g.doStopCompile != nil and g.doStopCompile()
|
||||
|
||||
proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic): PSym =
|
||||
result = newSym(skProc, getIdent(g.cache, name), idgen, nil, unknownLineInfo, {})
|
||||
result = newSym(skProc, getIdent(g.cache, name), nextSymId(idgen), nil, unknownLineInfo, {})
|
||||
result.magic = m
|
||||
result.flags = {sfNeverRaises}
|
||||
|
||||
@@ -457,7 +432,8 @@ proc initOperators*(g: ModuleGraph): Operators =
|
||||
result.opNot = createMagic(g, "not", mNot)
|
||||
result.opContains = createMagic(g, "contains", mInSet)
|
||||
|
||||
proc initModuleGraphFields(result: ModuleGraph) =
|
||||
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
||||
result = ModuleGraph()
|
||||
# A module ID of -1 means that the symbol is not attached to a module at all,
|
||||
# but to the module graph:
|
||||
result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32)
|
||||
@@ -467,9 +443,9 @@ proc initModuleGraphFields(result: ModuleGraph) =
|
||||
result.ifaces = @[]
|
||||
result.importStack = @[]
|
||||
result.inclToMod = initTable[FileIndex, FileIndex]()
|
||||
result.config = config
|
||||
result.cache = cache
|
||||
result.owners = @[]
|
||||
result.suggestSymbols = initTable[FileIndex, seq[SymInfoPair]]()
|
||||
result.suggestErrors = initTable[FileIndex, seq[Suggest]]()
|
||||
result.methods = @[]
|
||||
initStrTable(result.compilerprocs)
|
||||
initStrTable(result.exposed)
|
||||
@@ -482,13 +458,6 @@ proc initModuleGraphFields(result: ModuleGraph) =
|
||||
result.symBodyHashes = initTable[int, SigHash]()
|
||||
result.operators = initOperators(result)
|
||||
result.emittedTypeInfo = initTable[string, FileIndex]()
|
||||
result.cachedFiles = newStringTable()
|
||||
|
||||
proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
|
||||
result = ModuleGraph()
|
||||
result.config = config
|
||||
result.cache = cache
|
||||
initModuleGraphFields(result)
|
||||
|
||||
proc resetAllModules*(g: ModuleGraph) =
|
||||
initStrTable(g.packageSyms)
|
||||
@@ -501,7 +470,6 @@ proc resetAllModules*(g: ModuleGraph) =
|
||||
g.methods = @[]
|
||||
initStrTable(g.compilerprocs)
|
||||
initStrTable(g.exposed)
|
||||
initModuleGraphFields(g)
|
||||
|
||||
proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
if fileIdx.int32 >= 0:
|
||||
@@ -580,19 +548,7 @@ proc transitiveClosure(g: var IntSet; n: int) =
|
||||
|
||||
proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
|
||||
let m = g.getModule fileIdx
|
||||
if m != nil:
|
||||
g.suggestSymbols.del(fileIdx)
|
||||
g.suggestErrors.del(fileIdx)
|
||||
incl m.flags, sfDirty
|
||||
|
||||
proc unmarkAllDirty*(g: ModuleGraph) =
|
||||
for i in 0i32..<g.ifaces.len.int32:
|
||||
let m = g.ifaces[i].module
|
||||
if m != nil:
|
||||
m.flags.excl sfDirty
|
||||
|
||||
proc isDirty*(g: ModuleGraph; m: PSym): bool =
|
||||
result = g.suggestMode and sfDirty in m.flags
|
||||
if m != nil: incl m.flags, sfDirty
|
||||
|
||||
proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
|
||||
# we need to mark its dependent modules D as dirty right away because after
|
||||
@@ -602,28 +558,14 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
|
||||
g.invalidTransitiveClosure = false
|
||||
transitiveClosure(g.deps, g.ifaces.len)
|
||||
|
||||
# every module that *depends* on this file is also dirty:
|
||||
for i in 0i32..<g.ifaces.len.int32:
|
||||
if g.deps.contains(i.dependsOn(fileIdx.int)):
|
||||
g.markDirty(FileIndex(i))
|
||||
|
||||
proc needsCompilation*(g: ModuleGraph): bool =
|
||||
# every module that *depends* on this file is also dirty:
|
||||
for i in 0i32..<g.ifaces.len.int32:
|
||||
let m = g.ifaces[i].module
|
||||
if m != nil:
|
||||
if sfDirty in m.flags:
|
||||
return true
|
||||
if m != nil and g.deps.contains(i.dependsOn(fileIdx.int)):
|
||||
incl m.flags, sfDirty
|
||||
|
||||
proc needsCompilation*(g: ModuleGraph, fileIdx: FileIndex): bool =
|
||||
let module = g.getModule(fileIdx)
|
||||
if module != nil and g.isDirty(module):
|
||||
return true
|
||||
|
||||
for i in 0i32..<g.ifaces.len.int32:
|
||||
let m = g.ifaces[i].module
|
||||
if m != nil and g.isDirty(m) and g.deps.contains(fileIdx.int32.dependsOn(i)):
|
||||
return true
|
||||
proc isDirty*(g: ModuleGraph; m: PSym): bool =
|
||||
result = g.suggestMode and sfDirty in m.flags
|
||||
|
||||
proc getBody*(g: ModuleGraph; s: PSym): PNode {.inline.} =
|
||||
result = s.ast[bodyPos]
|
||||
@@ -652,34 +594,3 @@ proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string,
|
||||
let fromModule2 = if fromModule != nil: $fromModule.name.s else: "(toplevel)"
|
||||
let mode = if isNimscript: "(nims) " else: ""
|
||||
rawMessage(conf, hintProcessing, "$#$# $#: $#: $#" % [mode, indent, fromModule2, moduleStatus, path])
|
||||
|
||||
proc getPackage*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
## Returns a package symbol for yet to be defined module for fileIdx.
|
||||
## The package symbol is added to the graph if it doesn't exist.
|
||||
let pkgSym = getPackage(graph.config, graph.cache, fileIdx)
|
||||
# check if the package is already in the graph
|
||||
result = graph.packageSyms.strTableGet(pkgSym.name)
|
||||
if result == nil:
|
||||
# the package isn't in the graph, so create and add it
|
||||
result = pkgSym
|
||||
graph.packageSyms.strTableAdd(pkgSym)
|
||||
|
||||
func belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool =
|
||||
## Check if symbol belongs to the 'stdlib' package.
|
||||
sym.getPackageSymbol.getPackageId == graph.systemModule.getPackageId
|
||||
|
||||
proc `==`*(a, b: SymInfoPair): bool =
|
||||
result = a.sym == b.sym and a.info.exactEquals(b.info)
|
||||
|
||||
proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): seq[SymInfoPair] =
|
||||
result = graph.suggestSymbols.getOrDefault(fileIdx, @[])
|
||||
|
||||
iterator suggestSymbolsIter*(g: ModuleGraph): SymInfoPair =
|
||||
for xs in g.suggestSymbols.values:
|
||||
for x in xs:
|
||||
yield x
|
||||
|
||||
iterator suggestErrorsIter*(g: ModuleGraph): Suggest =
|
||||
for xs in g.suggestErrors.values:
|
||||
for x in xs:
|
||||
yield x
|
||||
|
||||
@@ -10,6 +10,100 @@
|
||||
import ast, renderer, strutils, msgs, options, idents, os, lineinfos,
|
||||
pathutils
|
||||
|
||||
when false:
|
||||
const
|
||||
considerParentDirs = not defined(noParentProjects)
|
||||
considerNimbleDirs = not defined(noNimbleDirs)
|
||||
|
||||
proc findInNimbleDir(pkg, subdir, dir: string): string =
|
||||
var best = ""
|
||||
var bestv = ""
|
||||
for k, p in os.walkDir(dir, relative=true):
|
||||
if k == pcDir and p.len > pkg.len+1 and
|
||||
p[pkg.len] == '-' and p.startsWith(pkg):
|
||||
let (_, a, _) = getPathVersionChecksum(p)
|
||||
if bestv.len == 0 or bestv < a:
|
||||
bestv = a
|
||||
best = dir / p
|
||||
|
||||
if best.len > 0:
|
||||
var f: File
|
||||
if open(f, best / changeFileExt(pkg, ".nimble-link")):
|
||||
# the second line contains what we're interested in, see:
|
||||
# https://github.com/nim-lang/nimble#nimble-link
|
||||
var override = ""
|
||||
discard readLine(f, override)
|
||||
discard readLine(f, override)
|
||||
close(f)
|
||||
if not override.isAbsolute():
|
||||
best = best / override
|
||||
else:
|
||||
best = override
|
||||
let f = if subdir.len == 0: pkg else: subdir
|
||||
let res = addFileExt(best / f, "nim")
|
||||
if best.len > 0 and fileExists(res):
|
||||
result = res
|
||||
|
||||
when false:
|
||||
proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string =
|
||||
template attempt(a) =
|
||||
let x = addFileExt(a, "nim")
|
||||
if fileExists(x): return x
|
||||
|
||||
case pkg
|
||||
of "stdlib":
|
||||
if subdir.len == 0:
|
||||
return options.libpath
|
||||
else:
|
||||
for candidate in stdlibDirs:
|
||||
attempt(options.libpath / candidate / subdir)
|
||||
of "root":
|
||||
let root = project.splitFile.dir
|
||||
if subdir.len == 0:
|
||||
return root
|
||||
else:
|
||||
attempt(root / subdir)
|
||||
else:
|
||||
when considerParentDirs:
|
||||
var p = parentDir(source.splitFile.dir)
|
||||
# support 'import $karax':
|
||||
let f = if subdir.len == 0: pkg else: subdir
|
||||
|
||||
while p.len > 0:
|
||||
let dir = p / pkg
|
||||
if dirExists(dir):
|
||||
attempt(dir / f)
|
||||
# 2nd attempt: try to use 'karax/karax'
|
||||
attempt(dir / pkg / f)
|
||||
# 3rd attempt: try to use 'karax/src/karax'
|
||||
attempt(dir / "src" / f)
|
||||
attempt(dir / "src" / pkg / f)
|
||||
p = parentDir(p)
|
||||
|
||||
when considerNimbleDirs:
|
||||
if not options.gNoNimblePath:
|
||||
var nimbleDir = getEnv("NIMBLE_DIR")
|
||||
if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble"
|
||||
result = findInNimbleDir(pkg, subdir, nimbleDir / "pkgs")
|
||||
if result.len > 0: return result
|
||||
when not defined(windows):
|
||||
result = findInNimbleDir(pkg, subdir, "/opt/nimble/pkgs")
|
||||
if result.len > 0: return result
|
||||
|
||||
proc scriptableImport(pkg, sub: string; info: TLineInfo): string =
|
||||
resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info)
|
||||
|
||||
proc lookupPackage(pkg, subdir: PNode): string =
|
||||
let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
|
||||
case pkg.kind
|
||||
of nkStrLit, nkRStrLit, nkTripleStrLit:
|
||||
result = scriptableImport(pkg.strVal, sub, pkg.info)
|
||||
of nkIdent:
|
||||
result = scriptableImport(pkg.ident.s, sub, pkg.info)
|
||||
else:
|
||||
localError(pkg.info, "package name must be an identifier or string literal")
|
||||
result = ""
|
||||
|
||||
proc getModuleName*(conf: ConfigRef; n: PNode): string =
|
||||
# This returns a short relative module name without the nim extension
|
||||
# e.g. like "system", "importer" or "somepath/module"
|
||||
@@ -36,18 +130,11 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
|
||||
localError(n.info, "only '/' supported with $package notation")
|
||||
result = ""
|
||||
else:
|
||||
if n0.kind in nkIdentKinds:
|
||||
let ident = n0.getPIdent
|
||||
if ident != nil and ident.s[0] == '/':
|
||||
let modname = getModuleName(conf, n[2])
|
||||
# hacky way to implement 'x / y /../ z':
|
||||
result = getModuleName(conf, n1)
|
||||
result.add renderTree(n0, {renderNoComments}).replace(" ")
|
||||
result.add modname
|
||||
else:
|
||||
result = ""
|
||||
else:
|
||||
result = ""
|
||||
let modname = getModuleName(conf, n[2])
|
||||
# hacky way to implement 'x / y /../ z':
|
||||
result = getModuleName(conf, n1)
|
||||
result.add renderTree(n0, {renderNoComments}).replace(" ")
|
||||
result.add modname
|
||||
of nkPrefix:
|
||||
when false:
|
||||
if n[0].kind == nkIdent and n[0].ident.s == "$":
|
||||
@@ -76,18 +163,3 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
|
||||
result = InvalidFileIdx
|
||||
else:
|
||||
result = fileInfoIdx(conf, fullPath)
|
||||
|
||||
proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string =
|
||||
## Mangle a relative module path to avoid path and symbol collisions.
|
||||
##
|
||||
## Used by backends that need to generate intermediary files from Nim modules.
|
||||
## This is needed because the compiler uses a flat cache file hierarchy.
|
||||
##
|
||||
## Example:
|
||||
## `foo-#head/../bar` becomes `@foo-@hhead@s..@sbar`
|
||||
"@m" & relativeTo(path, conf.projectPath).string.multiReplace(
|
||||
{$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"})
|
||||
|
||||
proc demangleModuleName*(path: string): string =
|
||||
## Demangle a relative module path.
|
||||
result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"})
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
## Implements the module handling, including the caching of modules.
|
||||
|
||||
import
|
||||
ast, magicsys, msgs, options,
|
||||
idents, lexer, syntaxes, modulegraphs,
|
||||
lineinfos, pathutils
|
||||
ast, astalgo, magicsys, msgs, options,
|
||||
idents, lexer, passes, syntaxes, llstream, modulegraphs,
|
||||
lineinfos, pathutils, tables
|
||||
|
||||
import ../dist/checksums/src/checksums/sha1
|
||||
import std/strtabs
|
||||
import ic / replayer
|
||||
|
||||
proc resetSystemArtifacts*(g: ModuleGraph) =
|
||||
magicsys.resetSysTypes(g)
|
||||
@@ -23,12 +22,57 @@ proc resetSystemArtifacts*(g: ModuleGraph) =
|
||||
template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
|
||||
getIdent(graph.cache, splitFile(filename).name)
|
||||
|
||||
proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
|
||||
template packageId(): untyped {.dirty.} = ItemId(module: PackageModuleId, item: int32(fileIdx))
|
||||
|
||||
proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
## returns package symbol (skPackage) for yet to be defined module for fileIdx
|
||||
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||
let name = getModuleIdent(graph, filename)
|
||||
let info = newLineInfo(fileIdx, 1, 1)
|
||||
let
|
||||
pck = getPackageName(graph.config, filename.string)
|
||||
pck2 = if pck.len > 0: pck else: "unknown"
|
||||
pack = getIdent(graph.cache, pck2)
|
||||
result = graph.packageSyms.strTableGet(pack)
|
||||
if result == nil:
|
||||
result = newSym(skPackage, getIdent(graph.cache, pck2), packageId(), nil, info)
|
||||
#initStrTable(packSym.tab)
|
||||
graph.packageSyms.strTableAdd(result)
|
||||
else:
|
||||
let modules = graph.modulesPerPackage.getOrDefault(result.itemId)
|
||||
let existing = if modules.data.len > 0: strTableGet(modules, name) else: nil
|
||||
if existing != nil and existing.info.fileIndex != info.fileIndex:
|
||||
when false:
|
||||
# we used to produce an error:
|
||||
localError(graph.config, info,
|
||||
"module names need to be unique per Nimble package; module clashes with " &
|
||||
toFullPath(graph.config, existing.info.fileIndex))
|
||||
else:
|
||||
# but starting with version 0.20 we now produce a fake Nimble package instead
|
||||
# to resolve the conflicts:
|
||||
let pck3 = fakePackageName(graph.config, filename)
|
||||
# this makes the new `result`'s owner be the original `result`
|
||||
result = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), result, info)
|
||||
#initStrTable(packSym.tab)
|
||||
graph.packageSyms.strTableAdd(result)
|
||||
|
||||
proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
|
||||
let packSym = getPackage(graph, fileIdx)
|
||||
result.owner = packSym
|
||||
result.position = int fileIdx
|
||||
|
||||
proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
#initStrTable(result.tab(graph))
|
||||
when false:
|
||||
strTableAdd(result.tab, result) # a module knows itself
|
||||
# This is now implemented via
|
||||
# c.moduleScope.addSym(module) # a module knows itself
|
||||
# in sem.nim, around line 527
|
||||
|
||||
if graph.modulesPerPackage.getOrDefault(packSym.itemId).data.len == 0:
|
||||
graph.modulesPerPackage[packSym.itemId] = newStrTable()
|
||||
graph.modulesPerPackage[packSym.itemId].strTableAdd(result)
|
||||
|
||||
proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||
# We cannot call ``newSym`` here, because we have to circumvent the ID
|
||||
# mechanism, which we do in order to assign each module a persistent ID.
|
||||
@@ -36,23 +80,103 @@ proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
|
||||
name: getModuleIdent(graph, filename),
|
||||
info: newLineInfo(fileIdx, 1, 1))
|
||||
if not isNimIdentifier(result.name.s):
|
||||
rawMessage(graph.config, errGenerated, "invalid module name: '" & result.name.s &
|
||||
"'; a module name must be a valid Nim identifier.")
|
||||
rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
|
||||
partialInitModule(result, graph, fileIdx, filename)
|
||||
graph.registerModule(result)
|
||||
|
||||
proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym =
|
||||
var flags = flags
|
||||
if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
|
||||
result = graph.getModule(fileIdx)
|
||||
|
||||
template processModuleAux(moduleStatus) =
|
||||
onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
|
||||
var s: PLLStream
|
||||
if sfMainModule in flags:
|
||||
if graph.config.projectIsStdin: s = stdin.llStreamOpen
|
||||
elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
|
||||
discard processModule(graph, result, idGeneratorFromModule(result), s)
|
||||
if result == nil:
|
||||
var cachedModules: seq[FileIndex]
|
||||
result = moduleFromRodFile(graph, fileIdx, cachedModules)
|
||||
let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
|
||||
if result == nil:
|
||||
result = newModule(graph, fileIdx)
|
||||
result.flags.incl flags
|
||||
registerModule(graph, result)
|
||||
processModuleAux("import")
|
||||
else:
|
||||
if sfSystemModule in flags:
|
||||
graph.systemModule = result
|
||||
partialInitModule(result, graph, fileIdx, filename)
|
||||
for m in cachedModules:
|
||||
registerModuleById(graph, m)
|
||||
replayStateChanges(graph.packed[m.int].module, graph)
|
||||
replayGenericCacheInformation(graph, m.int)
|
||||
elif graph.isDirty(result):
|
||||
result.flags.excl sfDirty
|
||||
# reset module fields:
|
||||
initStrTables(graph, result)
|
||||
result.ast = nil
|
||||
processModuleAux("import(dirty)")
|
||||
graph.markClientsDirty(fileIdx)
|
||||
|
||||
proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
|
||||
# this is called by the semantic checking phase
|
||||
assert graph.config != nil
|
||||
result = compileModule(graph, fileIdx, {}, s)
|
||||
graph.addDep(s, fileIdx)
|
||||
# keep track of import relationships
|
||||
if graph.config.hcrOn:
|
||||
graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
|
||||
#if sfSystemModule in result.flags:
|
||||
# localError(result.info, errAttemptToRedefine, result.name.s)
|
||||
# restore the notes for outer module:
|
||||
graph.config.notes =
|
||||
if s.getnimblePkgId == graph.config.mainPackageId or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
|
||||
else: graph.config.foreignPackageNotes
|
||||
|
||||
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode =
|
||||
result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
|
||||
graph.addDep(s, fileIdx)
|
||||
graph.addIncludeDep(s.position.FileIndex, fileIdx)
|
||||
let path = toFullPath(graph.config, fileIdx)
|
||||
graph.cachedFiles[path] = $secureHashFile(path)
|
||||
|
||||
proc connectCallbacks*(graph: ModuleGraph) =
|
||||
graph.includeFileCallback = includeModule
|
||||
graph.importModuleCallback = importModule
|
||||
|
||||
proc compileSystemModule*(graph: ModuleGraph) =
|
||||
if graph.systemModule == nil:
|
||||
connectCallbacks(graph)
|
||||
graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
|
||||
graph.config.libpath / RelativeFile"system.nim")
|
||||
discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
|
||||
|
||||
proc wantMainModule*(conf: ConfigRef) =
|
||||
if conf.projectFull.isEmpty:
|
||||
fatal(conf, gCmdLineInfo, "command expects a filename")
|
||||
conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
|
||||
|
||||
proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
|
||||
connectCallbacks(graph)
|
||||
let conf = graph.config
|
||||
wantMainModule(conf)
|
||||
configComplete(graph)
|
||||
|
||||
let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
|
||||
let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
|
||||
conf.projectMainIdx2 = projectFile
|
||||
|
||||
let packSym = getPackage(graph, projectFile)
|
||||
graph.config.mainPackageId = packSym.getnimblePkgId
|
||||
graph.importStack.add projectFile
|
||||
|
||||
if projectFile == systemFileIdx:
|
||||
discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
|
||||
else:
|
||||
graph.compileSystemModule()
|
||||
discard graph.compileModule(projectFile, {sfMainModule})
|
||||
|
||||
proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym =
|
||||
result = graph.newModule(fileInfoIdx(graph.config, filename))
|
||||
registerModule(graph, result)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user