diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml new file mode 100644 index 000000000000..73704598ff43 --- /dev/null +++ b/.builds/freebsd.yml @@ -0,0 +1,31 @@ +image: freebsd/latest +packages: +- databases/sqlite3 +- devel/boehm-gc-threaded +- devel/pcre +- devel/sdl20 +- devel/sfml +- www/node +- devel/gmake +- devel/git +sources: +- https://github.com/nim-lang/Nim +environment: + CC: /usr/bin/clang +tasks: +- setup: | + cd Nim + git clone --depth 1 -q https://github.com/nim-lang/csources.git + gmake -C csources -j $(sysctl -n hw.ncpu) + bin/nim c --skipUserCfg --skipParentCfg koch + echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv +- test: | + cd Nim + if ! ./koch runCI; then + nim c -r tools/ci_testresults.nim + exit 1 + fi +triggers: +- action: email + condition: failure + to: Andreas Rumpf diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000000..dc771dd31de5 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: 'araq' # Replace with a single Patreon username +open_collective: 'nim' # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: 'https://nim-lang.org/donate.html' # Replace with a single custom sponsorship URL diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 5fe7971a35e2..895b4ba613e6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,44 +1,39 @@ --- name: Bug report about: Have you found an unexpected behavior? Use this template. ---- +title: Think about the title, twice +labels: '' +assignees: '' - +--- - Function `echo` outputs the wrong string. - ### Example - ```nim echo "Hello World!" ``` - ### Current Output ``` Hola mundo! ``` - ### Expected Output - ``` Hello World! ``` - ### Possible Solution - +* In file xyz there is a call that might be the cause of it. ### Additional Information - + +* It was working in version a.b.c +* Issue #abc is related, but different because of ... +* This issue is blocking my project xyz + ``` $ nim -v Nim Compiler Version 0.1.2 diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 52aa53a67d22..b9f7caad5871 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,6 +1,9 @@ --- name: Feature request about: Do you want to suggest a new feature? Use this template. +title: '' +labels: '' +assignees: '' --- diff --git a/.gitignore b/.gitignore index 38b7976120a9..895e3637c617 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,9 @@ dnimcache/ !/icons/*.o *.obj *.ilk +*.exp *.pdb +*.lib *.dll *.exe *.so @@ -19,7 +21,6 @@ dnimcache/ *.zip *.iss *.log -*.ilk *.pdb mapping.txt @@ -42,6 +43,7 @@ bin/* *.perspectivev3 *.swp .DS_Store +.tags project.xcworkspace/ xcuserdata/ @@ -50,6 +52,9 @@ xcuserdata/ /compiler/nim.dot /reject.json /run.json +/tools/dochack/dochack.js +*.json +/pkgstemp/**/* # for `nim doc foo.nim` /*.html lib/**/*.html @@ -57,6 +62,8 @@ lib/**/*.html /testresults.json testament.db +/tests/**/*.json +/tests/**/*.js /csources dist/ @@ -76,3 +83,11 @@ megatest.nim /outputExpected.txt /outputGotten.txt +/lib/pure/*.js + +!/.builds/ + +# ignore debug dirs generated by dsymutil on OSX +*.dSYM + +nimdoc.out.css diff --git a/.travis.yml b/.travis.yml index 279da7920a20..d62fe20d24c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,13 +8,35 @@ matrix: include: - os: linux - env: NIM_COMPILE_TO_CPP=false + env: + - NIM_COMPILE_TO_CPP=false + - CPU=amd64 + + - os: linux + env: + - NIM_COMPILE_TO_CPP=false + - CPU=i386 + addons: + apt: + packages: + - gcc-multilib + - g++-multilib + - libcurl4-openssl-dev:i386 + - libgc-dev:i386 + - libglib2.0-dev:i386 + - libpulse-dev:i386 + - libsdl1.2-dev:i386 + - libsfml-dev:i386 - os: osx - env: NIM_COMPILE_TO_CPP=false + env: + - NIM_COMPILE_TO_CPP=false + - CPU=amd64 - os: osx - env: NIM_COMPILE_TO_CPP=true + env: + - NIM_COMPILE_TO_CPP=true + - CPU=amd64 # To allow failures for a failing configuration, use something like: # allow_failures: @@ -23,36 +45,37 @@ matrix: addons: apt: + # update the list above if more deps are introduced packages: - libcurl4-openssl-dev - libsdl1.2-dev - libgc-dev - libsfml-dev + - libc6-dbg + - valgrind + homebrew: + packages: + - boehmgc + - make + - sfml + update: true -before_install: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install boehmgc; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install sfml; fi +install: + - if [[ $CPU = i386 ]]; then echo -e "#!/bin/bash\n$(which gcc) -m32 \"\$@\"" > bin/gcc; fi + - if [[ $CPU = i386 ]]; then chmod 755 bin/gcc; fi + - if [[ $CPU = i386 ]]; then echo -e "#!/bin/bash\n$(which g++) -m32 \"\$@\"" > bin/g++; fi + - if [[ $CPU = i386 ]]; then chmod 755 bin/g++; fi before_script: - - set -e - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then unset -f cd; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then shell_session_update() { :; }; fi - git clone --depth 1 https://github.com/nim-lang/csources.git - - cd csources - - sh build.sh - - cd .. - - export PATH=$(pwd)/bin${PATH:+:$PATH} - - echo PATH:${PATH} - - set +e # prevents breaking after_failure + - export PATH="$PWD/bin${PATH:+:$PATH}" + - make -C csources -j 2 LD=$CC ucpu=$CPU script: - - set -e - echo "travis_fold:start:nim_c_koch" - nim c koch - echo "travis_fold:end:nim_c_koch" - ./koch runCI - - set +e before_deploy: # Make https://nim-lang.github.io/Nim work the same as https://nim-lang.github.io/Nim/overview.html diff --git a/appveyor.yml b/appveyor.yml index 31be04d1b4f8..e9407d124177 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,48 +1,31 @@ version: '{build}' environment: + DLLS_URL: https://nim-lang.org/download/dlls.zip + DLLS_ARCHIVE: dlls.zip MINGW_DIR: mingw64 - MINGW_URL: https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/4.9.2/threads-win32/seh/x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z/download - MINGW_ARCHIVE: x86_64-4.9.2-release-win32-seh-rt_v4-rev4.7z - OPENBLAS_URL: https://sourceforge.net/projects/openblas/files/v0.3.5/OpenBLAS%200.3.5%20version.zip/download - OPENBLAS_ARCHIVE: OpenBLAS-0.3.5.zip - SQLITE_URL: http://www.sqlite.org/2017/sqlite-dll-win64-x64-3160200.zip - SQLITE_ARCHIVE: sqlite-dll-win64-x64-3160200.zip - platform: x64 + MINGW_URL: https://nim-lang.org/download/mingw64.7z + MINGW_ARCHIVE: mingw64.7z matrix: - - NIM_TEST_PACKAGES: true - NIM_TEST_PACKAGES: false + - NIM_TEST_PACKAGES: true cache: - '%MINGW_ARCHIVE%' - - '%SQLITE_ARCHIVE%' - - '%OPENBLAS_ARCHIVE%' + - '%DLLS_ARCHIVE%' -matrix: - #allow_failures: - # - NIM_TEST_PACKAGES: true - fast_finish: true install: - ps: Install-Product node 8 # node 8 or later is required to test js async stuff - - MKDIR %CD%\DIST - - MKDIR %CD%\DIST\PCRE - - nuget install pcre -Verbosity quiet -Version 8.33.0.1 -OutputDirectory %CD%\DIST\PCRE - - IF not exist "%SQLITE_ARCHIVE%" appveyor DownloadFile "%SQLITE_URL%" -FileName "%SQLITE_ARCHIVE%" - - 7z x -y "%SQLITE_ARCHIVE%" -o"%CD%\DIST"> nul + - IF not exist "%DLLS_ARCHIVE%" appveyor DownloadFile "%DLLS_URL%" -FileName "%DLLS_ARCHIVE%" + - 7z x -y "%DLLS_ARCHIVE%" -o"%CD%\BIN"> nul - IF not exist "%MINGW_ARCHIVE%" appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%" - 7z x -y "%MINGW_ARCHIVE%" -o"%CD%\DIST"> nul - - IF not exist "%OPENBLAS_ARCHIVE%" appveyor DownloadFile "%OPENBLAS_URL%" -FileName "%OPENBLAS_ARCHIVE%" - - 7z x -y "%OPENBLAS_ARCHIVE%" -o"%CD%\DIST"> nul - SET PATH=%CD%\DIST\%MINGW_DIR%\BIN;%CD%\BIN;%PATH% - - IF "%PLATFORM%" == "x64" ( copy C:\OpenSSL-Win64\libeay32.dll %CD%\BIN\libeay64.dll & copy C:\OpenSSL-Win64\libeay32.dll %CD%\BIN\libeay32.dll & copy C:\OpenSSL-Win64\libssl32.dll %CD%\BIN\libssl64.dll & copy C:\OpenSSL-Win64\libssl32.dll %CD%\BIN\libssl32.dll ) - ELSE ( copy C:\OpenSSL-Win32\libeay32.dll %CD%\BIN\libeay32.dll & copy C:\OpenSSL-Win32\libssl32.dll %CD%\BIN\libssl32.dll ) - - IF "%PLATFORM%" == "x64" ( copy %CD%\DIST\sqlite3.dll %CD%\BIN\sqlite3_64.dll ) ELSE ( copy %CD%\DIST\sqlite3.dll %CD%\BIN\sqlite3_32.dll ) - - IF "%PLATFORM%" == "x64" ( copy %CD%\DIST\PCRE\pcre.redist.8.33.0.1\build\native\bin\v100\x64\Release\dynamic\utf8\pcre8.dll %CD%\bin\pcre64.dll ) ELSE ( copy %CD%\DIST\PCRE\pcre.redist.8.33.0.1\build\native\bin\v100\Win32\Release\dynamic\utf8\pcre8.dll %CD%\bin\pcre32.dll ) - git clone --depth 1 https://github.com/nim-lang/csources - cd csources - - IF "%PLATFORM%" == "x64" ( build64.bat ) else ( build.bat ) + - build64.bat - cd .. build_script: diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000000..0898755b6163 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,153 @@ +jobs: +- job: packages + + timeoutInMinutes: 90 # default `60` led to lots of cancelled jobs; use 0 for unlimited (may be undesirable) + + strategy: + matrix: + Linux_amd64: + vmImage: 'ubuntu-16.04' + CPU: amd64 + Linux_i386: + vmImage: 'ubuntu-16.04' + CPU: i386 + OSX_amd64: + vmImage: 'macOS-10.14' + CPU: amd64 + OSX_amd64_cpp: + vmImage: 'macOS-10.14' + CPU: amd64 + NIM_COMPILE_TO_CPP: true + Windows_amd64: + vmImage: 'windows-2019' + CPU: amd64 + Windows_amd64_pkg: + vmImage: 'windows-2019' + CPU: amd64 + NIM_TEST_PACKAGES: true + + pool: + vmImage: $(vmImage) + + workspace: + clean: all + + steps: + - bash: git config --global core.autocrlf false + displayName: 'Disable auto conversion to CRLF by git (Windows-only)' + condition: eq(variables['Agent.OS'], 'Windows_NT') + + - checkout: self + + - bash: git clone --depth 1 https://github.com/nim-lang/csources.git + displayName: 'Checkout csources' + + - task: NodeTool@0 + inputs: + versionSpec: '8.x' + displayName: 'Install node.js 8.x' + + - bash: | + 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 + displayName: 'Install dependencies (amd64 Linux)' + condition: and(eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64')) + + - bash: | + sudo dpkg --add-architecture i386 + + # Downgrade llvm, libgcc and libstdc++: + # - llvm has to be downgraded to have 32bit version installed for sfml. + # - libgcc and libstdc++ have to be downgraded as an optimization to + # prevent the use of the toolchain ppa, which has a terrible download + # speed. + cat << EOF | sudo tee /etc/apt/preferences.d/pin-to-rel + Package: libllvm6.0 libgcc1 libstdc++6 + Pin: origin "azure.archive.ubuntu.com" + Pin-Priority: 1001 + + Package: * + Pin: release o=LP-PPA-ubuntu-toolchain-r-test + Pin-Priority: 100 + EOF + + sudo apt-fast update -qq + DEBIAN_FRONTEND='noninteractive' \ + 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 + + cat << EOF > bin/gcc + #!/bin/bash + + exec $(which gcc) -m32 "\$@" + EOF + cat << EOF > bin/g++ + #!/bin/bash + + exec $(which g++) -m32 "\$@" + EOF + + chmod 755 bin/gcc + chmod 755 bin/g++ + + displayName: 'Install dependencies (i386 Linux)' + condition: and(eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386')) + + - bash: brew install boehmgc make sfml + displayName: 'Install dependencies (OSX)' + condition: eq(variables['Agent.OS'], 'Darwin') + + - bash: | + mkdir dist + curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z + curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip + 7z x dist/mingw64.7z -odist + 7z x dist/dlls.zip -obin + echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin' + + displayName: 'Install dependencies (Windows)' + condition: eq(variables['Agent.OS'], 'Windows_NT') + + - bash: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/bin' + displayName: 'Add build binaries to PATH' + + - bash: | + echo 'PATH:' "$PATH" + echo '##[section]gcc version' + gcc -v + echo '##[section]nodejs version' + node -v + echo '##[section]make version' + make -v + displayName: 'System information' + + - bash: | + ncpu= + case '$(Agent.OS)' in + 'Linux') + ncpu=$(nproc) + ;; + 'Darwin') + ncpu=$(sysctl -n hw.ncpu) + ;; + 'Windows_NT') + ncpu=$NUMBER_OF_PROCESSORS + ;; + esac + [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 + + make -C csources -j $ncpu CC=gcc ucpu=$(CPU) + displayName: 'Build csources' + + - bash: nim c koch + displayName: 'Build koch' + + # set result to omit the "bash exited with error code '1'" message + - bash: | + ./koch runCI || echo '##vso[task.complete result=Failed]' + displayName: 'Run CI' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/build_all.bat b/build_all.bat new file mode 100644 index 000000000000..59df15594e3b --- /dev/null +++ b/build_all.bat @@ -0,0 +1,16 @@ +@echo off +rem build development version of the compiler; can be rerun safely +if not exist csources ( + git clone --depth 1 https://github.com/nim-lang/csources.git +) +if not exist bin\nim.exe ( + cd csources + if PROCESSOR_ARCHITECTURE == AMD64 ( + SET ARCH=64 + ) + CALL build.bat + cd .. +) +bin\nim.exe c --skipUserCfg --skipParentCfg koch +koch.exe boot -d:release +koch.exe tools \ No newline at end of file diff --git a/build_all.sh b/build_all.sh index 333814d62dc4..e22facc1b22b 100644 --- a/build_all.sh +++ b/build_all.sh @@ -17,14 +17,14 @@ build_nim_csources(){ ## avoid changing dir in case of failure ( echo_run cd csources - echo_run sh build.sh + echo_run sh build.sh $@ ) # keep $nim_csources in case needed to investigate bootstrap issues # without having to rebuild from csources echo_run cp bin/nim $nim_csources } -[ -f $nim_csources ] || echo_run build_nim_csources +[ -f $nim_csources ] || echo_run build_nim_csources $@ # Note: if fails, may need to `cd csources && git pull` echo_run bin/nim c --skipUserCfg --skipParentCfg koch diff --git a/changelog.md b/changelog.md index 211855bfc289..3838da254a1e 100644 --- a/changelog.md +++ b/changelog.md @@ -1,196 +1,110 @@ -## v0.20.0 - XX/XX/2019 +# 1.2 - xxxx-xx-xx -### Changes affecting backwards compatibility +## Changes affecting backwards compatibility -- The ``isLower``, ``isUpper`` family of procs in strutils/unicode - operating on **strings** have been - deprecated since it was unclear what these do. Note that the much more - useful procs that operator on ``char`` or ``Rune`` are not affected. -- `strutils.editDistance` has been deprecated, - use `editdistance.editDistance` or `editdistance.editDistanceAscii` - instead. -- The OpenMP parallel iterator \``||`\` now supports any `#pragma omp directives` - and not just `#pragma omp parallel for`. See - [OpenMP documentation](https://www.openmp.org/wp-content/uploads/OpenMP-4.5-1115-CPP-web.pdf). +### Breaking changes in the standard library - The default annotation is `parallel for`, if you used OpenMP without annotation - the change is transparent, if you used annotations you will have to prefix - your previous annotations with `parallel for`. +- `base64.encode` no longer supports `lineLen` and `newLine`. + Use `base64.encodeMIME` instead. +- `os.splitPath()` behavior synchronized with `os.splitFile()` to return "/" + as the dir component of "/root_sub_dir" instead of the empty string. +- `sequtils.zip` now returns a sequence of anonymous tuples i.e. those tuples + now do not have fields named "a" and "b". +- `strutils.formatFloat` with `precision = 0` has the same behavior in all + backends, and it is compatible with Python's behavior, + e.g. `formatFloat(3.14159, precision = 0)` is now `3`, not `3.`. +- Global variable `lc` has been removed from sugar.nim. +- `distinctBase` has been moved from sugar.nim to typetraits and now implemented as + compiler type trait instead of macro. `distinctBase` in sugar module is now deprecated. -- The `unchecked` pragma was removed, instead use `system.UncheckedArray`. -- The undocumented ``#? strongSpaces`` parsing mode has been removed. -- The `not` operator is now always a unary operator, this means that code like - ``assert not isFalse(3)`` compiles. -- `getImpl` on a `var` or `let` symbol will now return the full `IdentDefs` - tree from the symbol declaration instead of just the initializer portion. +### Breaking changes in the compiler +- Implicit conversions for `const` behave correctly now, meaning that code like + `const SOMECONST = 0.int; procThatTakesInt32(SOMECONST)` will be illegal now. + Simply write `const SOMECONST = 0` instead. -#### Breaking changes in the standard library -- `osproc.execProcess` now also takes a `workingDir` parameter. -- `options.UnpackError` is no longer a ref type and inherits from `system.Defect` instead of `system.ValueError`. +## Library additions -- `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`. +- `macros.newLit` now works for ref object types. +- `system.writeFile` has been overloaded to also support `openarray[byte]`. +- Added overloaded `strformat.fmt` macro that use specified characters as + delimiter instead of '{' and '}'. +- introduced new procs in `tables.nim`: `OrderedTable.pop`, `CountTable.del`, + `CountTable.pop`, `Table.pop` +- To `strtabs.nim`, added `StringTable.clear` overload that reuses the existing mode. -- The procs `parseutils.parseBiggsetInt`, `parseutils.parseInt`, `parseutils.parseBiggestUInt` and `parseutils.parseUInt` now raise a `ValueError` when the parsed integer is outside of the valid range. Previously they sometimes raised a `OverflowError` and sometimes returned `0`. -- `streams.StreamObject` now restricts its fields to only raise `system.Defect`, `system.IOError` and `system.OSError`. This change only affects custom stream implementations. +- Added `sugar.outplace` for turning in-place algorithms like `sort` and `shuffle` into + operations that work on a copy of the data and return the mutated copy. As the existing + `sorted` does. +- Added `sugar.collect` that does comprehension for seq/set/table collections. -- nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or - `nil`/`""`, respectivly. Use the newly added `n in p.captures` method to - check if a group is captured, otherwise you'll recieve an exception. +- Added `sugar.capture` for capturing some local loop variables when creating a closure. + This is an enhanced version of `closureScope`. -- nre's `RegexMatch.{captureBounds,captures}.toTable` no longer accept a - default parameter. Instead uncaptured entries are left empty. Use - `Table.getOrDefault()` if you need defaults. +- Added `typetraits.lenTuple` to get number of elements of a tuple/type tuple, + and `typetraits.get` to get the ith element of a type tuple. +- Added `typetraits.genericParams` to return a tuple of generic params from a generic instantiation -- nre's `RegexMatch.captures.{items,toSeq}` now returns an `Option[string]` - instead of a `string`. With the removal of `nil` strings, this is the only - way to indicate a missing match. Inside your loops, instead of `capture == - ""` or `capture == nil`, use `capture.isSome` to check if a capture is - present, and `capture.get` to get its value. +- Added `os.normalizePathEnd` for additional path sanitization. -- nre's `replace()` no longer throws `ValueError` when the replacement string - has missing captures. It instead throws `KeyError` for named captures, and - `IndexError` for un-named captures. This is consistant with - `RegexMatch.{captureBounds,captures}[]`. +- Added `times.fromUnixFloat,toUnixFloat`, subsecond resolution versions of `fromUnix`,`toUnixFloat`. -- splitFile now correctly handles edge cases, see #10047 +## Library changes -- `isNil` is no longer false for undefined in the JavaScript backend: now it's true for both nil and undefined. Use `isNull` or `isUndefined` if you need exact equallity: `isNil` is consistent with `===`, `isNull` and `isUndefined` with `==`. +- `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations` + and only returns once all pending async operations are guaranteed to have completed. +- `asyncdispatch.drain` now consistently uses the passed timeout value for all + iterations of the event loop, and not just the first iteration. + This is more consistent with the other asyncdispatch apis, and allows + `asyncdispatch.drain` to be more efficient. +- `base64.encode` and `base64.decode` was made faster by about 50%. +- `htmlgen` adds [MathML](https://wikipedia.org/wiki/MathML) support + (ISO 40314). +- `macros.eqIdent` is now invariant to export markers and backtick quotes. +- `htmlgen.html` allows `lang` on the `` tag and common valid attributes. +- `macros.basename` and `basename=` got support for `PragmaExpr`, + so that an expression like `MyEnum {.pure.}` is handled correctly. -- several deprecated modules were removed: `ssl`, `matchers`, `httpserver`, - `unsigned`, `actors`, `parseurl` -- two poorly documented and not used modules (`subexes`, `scgi`) were moved to - graveyard (they are available as Nimble packages) +## Language additions +- An `align` pragma can now be used for variables and object fields, similar + to the `alignas` declaration modifier in C/C++. +- `=sink` type bound operator is now optional. Compiler can now use combination + of `=destroy` and `copyMem` to move objects efficiently. -#### Breaking changes in the compiler -- The compiler now implements the "generic symbol prepass" for `when` statements - in generics, see bug #8603. This means that code like this does not compile - anymore: +## Language changes -```nim -proc enumToString*(enums: openArray[enum]): string = - # typo: 'e' instead 'enums' - when e.low.ord >= 0 and e.high.ord < 256: - result = newString(enums.len) - else: - result = newString(enums.len * 2) -``` +- Unsigned integer operators have been fixed to allow promotion of the first operand. +- Conversions to unsigned integers are unchecked at runtime, imitating earlier Nim + versions. The documentation was improved to acknowledge this special case. + See https://github.com/nim-lang/RFCs/issues/175 for more details. -- ``discard x`` is now illegal when `x` is a function symbol. -- Implicit imports via ``--import: module`` in a config file are now restricted - to the main package. +### Tool changes -### Library additions - -- There is a new stdlib module `std/editdistance` as a replacement for the - deprecated `strutils.editDistance`. - -- There is a new stdlib module `std/wordwrap` as a replacement for the - deprecated `strutils.wordwrap`. - -- Added `split`, `splitWhitespace`, `size`, `alignLeft`, `align`, - `strip`, `repeat` procs and iterators to `unicode.nim`. - -- Added `or` for `NimNode` in `macros`. - -- Added `system.typeof` for more control over how `type` expressions - can be deduced. - -- Added `macros.isInstantiationOf` for checking if the proc symbol - is instantiation of generic proc symbol. - -- Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc. - -- There is a new stdlib module `std/diff` to compute the famous "diff" - of two texts by line. - -- Added `os.relativePath`. - -- Added `parseopt.remainingArgs`. - -- Added `os.getCurrentCompilerExe` (implmented as `getAppFilename` at CT), - can be used to retrieve the currently executing compiler. - -- Added `xmltree.toXmlAttributes`. - -- Added ``std/sums`` module for fast summation functions. - -- Added `Rusage`, `getrusage`, `wait4` to posix interface. - - - -### Library changes - -- The string output of `macros.lispRepr` proc has been tweaked - slightly. The `dumpLisp` macro in this module now outputs an - indented proper Lisp, devoid of commas. - -- In `strutils` empty strings now no longer matched as substrings - anymore. - -- Complex type is now generic and not a tuple anymore. - -- The `ospaths` module is now deprecated, use `os` instead. Note that - `os` is available in a NimScript environment but unsupported - operations produce a compile-time error. - -- The `parseopt` module now supports a new flag `allowWhitespaceAfterColon` - (default value: true) that can be set to `false` for better Posix - interoperability. (Bug #9619.) - -- `os.joinPath` and `os.normalizePath` handle edge cases like ``"a/b/../../.."`` - differently. - -- `securehash` is moved to `lib/deprecated` - - -### Language additions - -- Vm support for float32<->int32 and float64<->int64 casts was added. -- There is a new pragma block `noSideEffect` that works like - the `gcsafe` pragma block. -- added os.getCurrentProcessId() -- User defined pragmas are now allowed in the pragma blocks -- Pragma blocks are no longer eliminated from the typed AST tree to preserve - pragmas for further analysis by macros -- Custom pragmas are now supported for `var` and `let` symbols. - - -### Language changes - -- The standard extension for SCF (source code filters) files was changed from - ``.tmpl`` to ``.nimf``, - it's more recognizable and allows tools like github to recognize it as Nim, - see [#9647](https://github.com/nim-lang/Nim/issues/9647). - The previous extension will continue to work. -- Pragma syntax is now consistent. Previous syntax where type pragmas did not - follow the type name is now deprecated. Also pragma before generic parameter - list is deprecated to be consistent with how pragmas are used with a proc. See - [#8514](https://github.com/nim-lang/Nim/issues/8514) and - [#1872](https://github.com/nim-lang/Nim/issues/1872) for further details. -### Tool changes -- `jsondoc` now include a `moduleDescription` field with the module - description. `jsondoc0` shows comments as it's own objects as shown in the - documentation. -- `nimpretty`: --backup now defaults to `off` instead of `on` and the flag was - un-documented; use `git` instead of relying on backup files. +### Compiler changes +- JS target indent is all spaces, instead of mixed spaces and tabs, for + generated JavaScript. +- The Nim compiler now supports the ``--asm`` command option for easier + inspection of the produced assembler code. +- The Nim compiler now supports a new pragma called ``.localPassc`` to + pass specific compiler options to the C(++) backend for the C(++) file + that was produced from the current Nim module. -### Compiler changes -- The deprecated `fmod` proc is now unavailable on the VM'. +## Bugfixes -### Bugfixes +- The `FD` variant of `selector.unregister` for `ioselector_epoll` and + `ioselector_select` now properly handle the `Event.User` select event type. diff --git a/changelogs/changelog_0_18_1.md b/changelogs/changelog_0_18_1.md index 3dab051d43c8..d0b7fd8ee5cf 100644 --- a/changelogs/changelog_0_18_1.md +++ b/changelogs/changelog_0_18_1.md @@ -100,7 +100,7 @@ - We changed how array accesses "from backwards" like ``a[^1]`` or ``a[0..^1]`` are implemented. These are now implemented purely in ``system.nim`` without compiler - support. There is a new "heterogenous" slice type ``system.HSlice`` that takes 2 + support. There is a new "heterogeneous" slice type ``system.HSlice`` that takes 2 generic parameters which can be ``BackwardsIndex`` indices. ``BackwardsIndex`` is produced by ``system.^``. This means if you overload ``[]`` or ``[]=`` you need to ensure they also work @@ -660,4 +660,4 @@ for i in a..b: - Fixed "C++: SIGABRT instead of IndexError for out-of-bounds" ([#6512](https://github.com/nim-lang/Nim/issues/6512)) - Fixed "An uncaught exception in cpp mode doesn't show the exception name/msg" - ([#6431](https://github.com/nim-lang/Nim/issues/6431)) \ No newline at end of file + ([#6431](https://github.com/nim-lang/Nim/issues/6431)) diff --git a/changelogs/changelog_0_20_0.md b/changelogs/changelog_0_20_0.md new file mode 100644 index 000000000000..9ddfd36dcc5c --- /dev/null +++ b/changelogs/changelog_0_20_0.md @@ -0,0 +1,294 @@ +## v0.20.0 - 2019-06-06 + + +### Changes affecting backwards compatibility + +- `shr` is now sign preserving. Use `-d:nimOldShiftRight` to enable + the old behavior globally. + +- The ``isLower``, ``isUpper`` family of procs in strutils/unicode + operating on **strings** have been + deprecated since it was unclear what these do. Note that the much more + useful procs that operate on ``char`` or ``Rune`` are not affected. + +- `strutils.editDistance` has been deprecated, + use `editdistance.editDistance` or `editdistance.editDistanceAscii` + instead. + +- The OpenMP parallel iterator \``||`\` now supports any `#pragma omp directive` + and not just `#pragma omp parallel for`. See + [OpenMP documentation](https://www.openmp.org/wp-content/uploads/OpenMP-4.5-1115-CPP-web.pdf). + + The default annotation is `parallel for`, if you used OpenMP without annotation + the change is transparent, if you used annotations you will have to prefix + your previous annotations with `parallel for`. + + Furthermore, an overload with positive stepping is available. + +- The `unchecked` pragma was removed, instead use `system.UncheckedArray`. + +- The undocumented ``#? strongSpaces`` parsing mode has been removed. + +- The `not` operator is now always a unary operator, this means that code like + ``assert not isFalse(3)`` compiles. + +- `getImpl` on a `var` or `let` symbol will now return the full `IdentDefs` + tree from the symbol declaration instead of just the initializer portion. + +- Methods are now ordinary "single" methods, only the first parameter is + used to select the variant at runtime. For backwards compatibility + use the new `--multimethods:on` switch. + +- Generic methods are now deprecated; they never worked well. + +- Compile time checks for integer and float conversions are now stricter. + For example, `const x = uint32(-1)` now gives a compile time error instead + of being equivalent to `const x = 0xFFFFFFFF'u32`. + +- Using `typed` as the result type in templates/macros now means + "expression with a type". The old meaning of `typed` is preserved + as `void` or no result type at all. + +- A bug allowed `macro foo(): int = 123` to compile even though a + macro has to return a `NimNode`. This has been fixed. + +- With the exception of `uint` and `uint64`, conversion to unsigned types + are now range checked during runtime. + +- Macro arguments of type `typedesc` are now passed to the macro as + `NimNode` like every other type except `static`. Use `typed` for a + behavior that is identical in new and old + Nim. See the RFC [Pass typedesc as NimNode to macros](https://github.com/nim-lang/RFCs/issues/148) + for more details. + + +#### Breaking changes in the standard library + +- `osproc.execProcess` now also takes a `workingDir` parameter. + +- `std/sha1.secureHash` now accepts `openArray[char]`, not `string`. (Former + successful matches should keep working, though former failures will not.) + +- `options.UnpackError` is no longer a ref type and inherits from `system.Defect` + instead of `system.ValueError`. + +- `system.ValueError` now inherits from `system.CatchableError` instead of `system.Defect`. + +- The procs `parseutils.parseBiggestInt`, `parseutils.parseInt`, + `parseutils.parseBiggestUInt` and `parseutils.parseUInt` now raise a + `ValueError` when the parsed integer is outside of the valid range. + Previously they sometimes raised an `OverflowError` and sometimes they + returned `0`. + +- The procs `parseutils.parseBin`, `parseutils.parseOct` and `parseutils.parseHex` + were not clearing their `var` parameter `number` and used to push its value to + the left when storing the parsed string into it. Now they always set the value + of the parameter to `0` before storing the result of the parsing, unless the + string to parse is not valid (then the value of `number` is not changed). + +- `streams.StreamObject` now restricts its fields to only raise `system.Defect`, + `system.IOError` and `system.OSError`. + This change only affects custom stream implementations. + +- nre's `RegexMatch.{captureBounds,captures}[]` no longer return `Option` or + `nil`/`""`, respectively. Use the newly added `n in p.captures` method to + check if a group is captured, otherwise you'll receive an exception. + +- nre's `RegexMatch.{captureBounds,captures}.toTable` no longer accept a + default parameter. Instead uncaptured entries are left empty. Use + `Table.getOrDefault()` if you need defaults. + +- nre's `RegexMatch.captures.{items,toSeq}` now returns an `Option[string]` + instead of a `string`. With the removal of `nil` strings, this is the only + way to indicate a missing match. Inside your loops, instead + of `capture == ""` or `capture == nil`, use `capture.isSome` to check if a capture is + present, and `capture.get` to get its value. + +- nre's `replace()` no longer throws `ValueError` when the replacement string + has missing captures. It instead throws `KeyError` for named captures, and + `IndexError` for unnamed captures. This is consistent with + `RegexMatch.{captureBounds,captures}[]`. + +- `splitFile` now correctly handles edge cases, see #10047. + +- `isNil` is no longer false for undefined in the JavaScript backend: + now it's true for both nil and undefined. + Use `isNull` or `isUndefined` if you need exact equality: + `isNil` is consistent with `===`, `isNull` and `isUndefined` with `==`. + +- several deprecated modules were removed: `ssl`, `matchers`, `httpserver`, + `unsigned`, `actors`, `parseurl` + +- two poorly documented and not used modules (`subexes`, `scgi`) were moved to + graveyard (they are available as Nimble packages) + +- procs `string.add(int)` and `string.add(float)` which implicitly convert + ints and floats to string have been deprecated. + Use `string.addInt(int)` and `string.addFloat(float)` instead. + +- ``case object`` branch transitions via ``system.reset`` are deprecated. + Compile your code with ``-d:nimOldCaseObjects`` for a transition period. + +- base64 module: The default parameter `newLine` for the `encode` procs + was changed from `"\13\10"` to the empty string `""`. + + +#### Breaking changes in the compiler + +- The compiler now implements the "generic symbol prepass" for `when` statements + in generics, see bug #8603. This means that code like this does not compile + anymore: + +```nim +proc enumToString*(enums: openArray[enum]): string = + # typo: 'e' instead 'enums' + when e.low.ord >= 0 and e.high.ord < 256: + result = newString(enums.len) + else: + result = newString(enums.len * 2) +``` + +- ``discard x`` is now illegal when `x` is a function symbol. + +- Implicit imports via ``--import: module`` in a config file are now restricted + to the main package. + + +### Library additions + +- There is a new stdlib module `std/editdistance` as a replacement for the + deprecated `strutils.editDistance`. + +- There is a new stdlib module `std/wordwrap` as a replacement for the + deprecated `strutils.wordwrap`. + +- Added `split`, `splitWhitespace`, `size`, `alignLeft`, `align`, + `strip`, `repeat` procs and iterators to `unicode.nim`. + +- Added `or` for `NimNode` in `macros`. + +- Added `system.typeof` for more control over how `type` expressions + can be deduced. + +- Added `macros.isInstantiationOf` for checking if the proc symbol + is instantiation of generic proc symbol. + +- Added the parameter ``isSorted`` for the ``sequtils.deduplicate`` proc. + +- There is a new stdlib module `std/diff` to compute the famous "diff" + of two texts by line. + +- Added `os.relativePath`. + +- Added `parseopt.remainingArgs`. + +- Added `os.getCurrentCompilerExe` (implemented as `getAppFilename` at CT), + can be used to retrieve the currently executing compiler. + +- Added `xmltree.toXmlAttributes`. + +- Added ``std/sums`` module for fast summation functions. + +- Added `Rusage`, `getrusage`, `wait4` to the posix interface. + +- Added the `posix_utils` module. + +- Added `system.default`. + +- Added `sequtils.items` for closure iterators, allows closure iterators + to be used by the `mapIt`, `filterIt`, `allIt`, `anyIt`, etc. + + +### Library changes + +- The string output of `macros.lispRepr` proc has been tweaked + slightly. The `dumpLisp` macro in this module now outputs an + indented proper Lisp, devoid of commas. + +- Added `macros.signatureHash` that returns a stable identifier + derived from the signature of a symbol. + +- In `strutils` empty strings now no longer matched as substrings + anymore. + +- The `Complex` type is now a generic object and not a tuple anymore. + +- The `ospaths` module is now deprecated, use `os` instead. Note that + `os` is available in a NimScript environment but unsupported + operations produce a compile-time error. + +- The `parseopt` module now supports a new flag `allowWhitespaceAfterColon` + (default value: true) that can be set to `false` for better Posix + interoperability. (Bug #9619.) + +- `os.joinPath` and `os.normalizePath` handle edge cases like ``"a/b/../../.."`` + differently. + +- `securehash` was moved to `lib/deprecated`. + +- The switch ``-d:useWinAnsi`` is not supported anymore. + +- In `times` module, procs `format` and `parse` accept a new optional + `DateTimeLocale` argument for formatting/parsing dates in other languages. + + +### Language additions + +- Vm support for float32<->int32 and float64<->int64 casts was added. +- There is a new pragma block `noSideEffect` that works like + the `gcsafe` pragma block. +- added `os.getCurrentProcessId`. +- User defined pragmas are now allowed in the pragma blocks. +- Pragma blocks are no longer eliminated from the typed AST tree to preserve + pragmas for further analysis by macros. +- Custom pragmas are now supported for `var` and `let` symbols. +- Tuple unpacking is now supported for constants and for loop variables. +- Case object branches can be initialized with a runtime discriminator if + possible discriminator values are constrained within a case statement. + +### Language changes + +- The standard extension for SCF (source code filters) files was changed from + ``.tmpl`` to ``.nimf``, + it's more recognizable and allows tools like Github to recognize it as Nim, + see [#9647](https://github.com/nim-lang/Nim/issues/9647). + The previous extension will continue to work. + +- Pragma syntax is now consistent. Previous syntax where type pragmas did not + follow the type name is now deprecated. Also pragma before generic parameter + list is deprecated to be consistent with how pragmas are used with a proc. See + [#8514](https://github.com/nim-lang/Nim/issues/8514) and + [#1872](https://github.com/nim-lang/Nim/issues/1872) for further details. + +- Hash sets and tables are initialized by default. The explicit `initHashSet`, + `initTable`, etc. are not needed anymore. + + +### Tool changes + +- `jsondoc` now includes a `moduleDescription` field with the module + description. `jsondoc0` shows comments as its own objects as shown in the + documentation. +- `nimpretty`: --backup now defaults to `off` instead of `on` and the flag was + undocumented; use `git` instead of relying on backup files. +- `koch` now defaults to build the latest *stable* Nimble version unless you + explicitly ask for the latest master version via `--latest`. + + +### Compiler changes + +- The deprecated `fmod` proc is now unavailable on the VM. +- A new `--outdir` option was added. +- The compiled JavaScript file for the project produced by executing `nim js` + will no longer be placed in the nimcache directory. +- The `--hotCodeReloading` has been implemented for the native targets. + The compiler also provides a new more flexible API for handling the + hot code reloading events in the code. +- The compiler now supports a ``--expandMacro:macroNameHere`` switch + for easy introspection into what a macro expands into. +- The `-d:release` switch now does not disable runtime checks anymore. + For a release build that also disables runtime checks + use `-d:release -d:danger` or simply `-d:danger`. + + +### Bugfixes diff --git a/changelogs/changelog_0_20_2.md b/changelogs/changelog_0_20_2.md new file mode 100644 index 000000000000..4f09ae3c5e17 --- /dev/null +++ b/changelogs/changelog_0_20_2.md @@ -0,0 +1,55 @@ +# v0.20.2 - 2019-07-17 + + +## Changes affecting backwards compatibility + +- All `strutils.rfind` procs now take `start` and `last` like `strutils.find` + with the same data slice/index meaning. This is backwards compatible for + calls *not* changing the `rfind` `start` parameter from its default. (#11487) + + In the unlikely case that you were using `rfind X, start=N`, or `rfind X, N`, + then you need to change that to `rfind X, last=N` or `rfind X, 0, N`. (This + should minimize gotchas porting code from other languages like Python or C++.) + +- On Windows stderr/stdout/stdin are not opened as binary files anymore. Use the switch + `-d:nimBinaryStdFiles` for a transition period. + +### Breaking changes in the standard library + +- Mac OS X / BSD: TSa_Family is now the ``uint8`` type, so type + conversions like ``x.sin_family = uint16 toInt(nativesockets.AF_INET)`` + need to be changed into ``x.sin_family = TSa_Family toInt(nativesockets.AF_INET)``. + + +### Breaking changes in the compiler + + +## Library additions + +- `toOpenArray` is now available for the JS target. + +## Library changes + +- Fix async IO operations stalling even after socket is closed. (#11232) + +- More informative error message for `streams.openFileStream`. (#11438) + + +## Language additions + + +## Language changes + + +### Tool changes + + +### Compiler changes + +- Better error message for IndexError for empty containers. (#11476) + +- Fix regression in semfold for old right shift. (#11477) + +- Fix for passing tuples as static params to macros. (#11423) + +## Bugfixes diff --git a/changelogs/changelog_1_0_0.md b/changelogs/changelog_1_0_0.md new file mode 100644 index 000000000000..5a9185ecb7f9 --- /dev/null +++ b/changelogs/changelog_1_0_0.md @@ -0,0 +1,138 @@ +# v1.0 - 2019-09-23 + + +## Changes affecting backwards compatibility + +- The switch ``-d:nimBinaryStdFiles`` does not exist anymore. Instead + stdin/stdout/stderr are binary files again. This change only affects + Windows. + +- On Windows console applications the code-page is set at program startup + to UTF-8. Use the new switch `-d:nimDontSetUtf8CodePage` to disable this + feature. + +- The language definition and compiler are now stricter about ``gensym``'ed + symbols in hygienic templates. See the section in the + [manual](https://nim-lang.org/docs/manual.html#templates-hygiene-in-templates) + for further details. Use the compiler switch `--oldgensym:on` for a + transition period. + + +### Breaking changes in the standard library + +- We removed `unicode.Rune16` without any deprecation period as the name + was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details) + and we didn't find any usages of it in the wild. If you still need it, add this + piece of code to your project: +```nim +type + Rune16* = distinct int16 +``` + +- `exportc` now uses C instead of C++ mangling with `nim cpp`, matching behavior + of `importc`, see #10578. + Use the new `exportcpp` to mangle as C++ when using `nim cpp`. + + +### Breaking changes in the compiler + +- A bug allowing `int` to be implicitly converted to range types of smaller size + (e.g `range[0'i8..10'i8]`) has been fixed. + + + +## Library additions + +- `encodings.getCurrentEncoding` now distinguishes between the console's + encoding and the OS's encoding. This distinction is only meaningful on + Windows. +- Added `system.getOsFileHandle` which is usually more useful + than `system.getFileHandle`. This distinction is only meaningful on + Windows. +- Added a `json.parseJsonFragments` iterator that can be used to speedup + JSON processing substantially when there are JSON fragments separated + by whitespace. + + + +## Library changes + +- Added `os.delEnv` and `nimscript.delEnv`. (#11466) + +- Enabled Oid usage in hashtables. (#11472) + +- Added `unsafeColumnAt` procs, that return unsafe cstring from InstantRow. (#11647) + +- Make public `Sha1Digest` and `Sha1State` types and `newSha1State`, + `update` and `finalize` procedures from `sha1` module. (#11694) + +- Added the `std/monotimes` module which implements monotonic timestamps. + +- Consistent error handling of two `exec` overloads. (#10967) + +- Officially the following modules now have an unstable API: + - std/varints + - core/allocators + - core/hotcodereloading + - asyncstreams + - base64 + - browsers + - collections/rtarrays + - collections/sharedlist + - collections/sharedtable + - concurrency/atomics + - concurrency/cpuload + - concurrency/threadpool + - coro + - endians + - httpcore + - parsesql + - pathnorm + - reservedmem + - typetraits + + Every other stdlib module is API stable with respect to version 1. + + + +## Language additions + +- Inline iterators returning `lent T` types are now supported, similarly to + iterators returning `var T`: +```nim +iterator myitems[T](x: openarray[T]): lent T +iterator mypairs[T](x: openarray[T]): tuple[idx: int, val: lent T] +``` + +- Added an `importjs` pragma that can now be used instead of `importcpp` + and `importc` to import symbols from JavaScript. `importjs` for routines always + takes a "pattern" for maximum flexibility. + + + +## Language changes + +- `uint64` is now finally a regular ordinal type. This means `high(uint64)` compiles + and yields the correct value. + + +### Tool changes + +- The Nim compiler now does not recompile the Nim project via ``nim c -r`` if + no dependent Nim file changed. This feature can be overridden by + the ``--forceBuild`` command line option. +- The Nim compiler now warns about unused module imports. You can use a + top level ``{.used.}`` pragma in the module that you want to be importable + without producing this warning. +- The "testament" testing tool's name was changed + from `tester` to `testament` and is generally available as a tool to run Nim + tests automatically. + + +### Compiler changes + +- VM can now cast integer type arbitrarily. (#11459) + + + +## Bugfixes diff --git a/changelogs/changelog_1_0_2.md b/changelogs/changelog_1_0_2.md new file mode 100644 index 000000000000..aa01c503f6ef --- /dev/null +++ b/changelogs/changelog_1_0_2.md @@ -0,0 +1,56 @@ +# v1.0.2 - 2019-10-23 + + +## Bugfixes + +* fixes the --verbosity:2 regression +* Fixed "Fail to compile a file twice under Windows (v1.0 bug)." [#12242](https://github.com/nim-lang/Nim/issues/12242) +* fix nimpretty removing space before pragma +* JS: gensym is stricter for 'this' +* Fixed "VM Assertion Error with newruntime" [#12294](https://github.com/nim-lang/Nim/issues/12294) +* Fixed "Assertion error when running `nim check` on compiler/nim.nim" [#12281](https://github.com/nim-lang/Nim/issues/12281) +* Fixed "Compiler crash with empty array and generic instantiation with int as parameter" [#12264](https://github.com/nim-lang/Nim/issues/12264) +* Fixed "Regression in JS backend codegen "Error: request to generate code for .compileTime proc"" [#12240](https://github.com/nim-lang/Nim/issues/12240) +* Fix how `relativePath` handle case sensitiviy +* Fixed "SIGSEGV in compiler when using generic types and seqs" [#12336](https://github.com/nim-lang/Nim/issues/12336) +* Fixed "[1.0.0] weird interaction between `import os` and casting integer to char on macosx trigger bad codegen" [#12291](https://github.com/nim-lang/Nim/issues/12291) +* VM: no special casing for big endian machines +* Fixed "`internal error: environment misses` with a simple template inside one of Jester macros" [#12323](https://github.com/nim-lang/Nim/issues/12323) +* nimsuggest: fix tcp socket leak +* nimsuggest: fix tcp socket leak for epc backend +* Fixed "`writeFile` and `write(f, str)` skip null bytes on Windows" [#12315](https://github.com/nim-lang/Nim/issues/12315) +* Fixed "Crash in intsets symmetric_difference" [#12366](https://github.com/nim-lang/Nim/issues/12366) +* Fixed "[regression] VM crash when dealing with var param of a proc result" [#12244](https://github.com/nim-lang/Nim/issues/12244) +* fixes a koch regression that made 'koch boot --listcmd' not work anymore +* Fixed "[regression] inconsistent signed int `mod` operator between runtime, compiletime, and semfold" [#12332](https://github.com/nim-lang/Nim/issues/12332) +* Fixed "Boehm disables interior pointer checking" [#12286](https://github.com/nim-lang/Nim/issues/12286) +* Fixes semCustomPragma when nkSym +* Fixed yield in nkCheckedFieldExpr +* Fixed "`randomize()` from `random` not working on JS" [#12418](https://github.com/nim-lang/Nim/issues/12418) +* Fixed "Compiler crash with invalid object variant" [#12379](https://github.com/nim-lang/Nim/issues/12379) +* fix type's case in random.nim +* Fixed "Update docs with a better way to signal unimplemented methods" [#10804](https://github.com/nim-lang/Nim/issues/10804) +* Fixed "Nim language manual, push pragma is not explained well" [#10824](https://github.com/nim-lang/Nim/issues/10824) +* Fixed "[regression] Importing more than one module with same name from different packages produce bad codegen" [#12420](https://github.com/nim-lang/Nim/issues/12420) +* Namespace unittest enums to avoid name conflicts +* Fixed "VM checks unsigned integers for overflow." [#12310](https://github.com/nim-lang/Nim/issues/12310) +* Fixed "line directive is not generated for first line of function definition" [#12426](https://github.com/nim-lang/Nim/issues/12426) + + + +## Documentation improvements + +* threadpool: fix link in docs (#12258) +* Fix spellings (#12277) +* fix #12278, don't expose internal PCRE documentation +* Fixed "Documentation of quitprocs is wrong" [#12279(https://github.com/nim-lang/Nim/issues/12279) +* Fix typo in docs +* Fix reference to parseSpec proc in readme +* [doc/tut1] removed discard discussion in comments +* Documentation improvements around the db interface +* Easier build instructions for windows - just run `build_all.bat`. +* fix a few dead links and a missing sentence in documentation +* Macro docs additions +* Updated the code example in the os module to use better grammar. +* Mention "lambdas" and `=>` in the manual +* Better documentation on Garbage Collector diff --git a/changelogs/changelog_X_XX_X.md b/changelogs/changelog_X_XX_X.md index 7ca4a1b9a6ff..45a8cbc449ee 100644 --- a/changelogs/changelog_X_XX_X.md +++ b/changelogs/changelog_X_XX_X.md @@ -1,27 +1,34 @@ -## v0.XX.0 - XX/XX/2018 +# vx.xx.x - yyyy-mm-dd -### Changes affecting backwards compatibility +This is an example file. +The changes should go to changelog.md! -- Example item: ``Foo`` changed to ``Bar``. -#### Breaking changes in the standard library +## Changes affecting backwards compatibility +- Example item: `Foo` changed to `Bar`. -#### Breaking changes in the compiler -### Library additions +### Breaking changes in the standard library -### Library changes +### Breaking changes in the compiler -### Language additions +## Library additions -### Language changes +## Library changes + +## Language additions + + +## Language changes ### Tool changes + ### Compiler changes -### Bugfixes + +## Bugfixes diff --git a/compiler/aliases.nim b/compiler/aliases.nim index f79210dd7378..0006c9fe636b 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -10,7 +10,7 @@ ## Simple alias analysis for the HLO and the code generators. import - ast, astalgo, types, trees, intsets, msgs + ast, astalgo, types, trees, intsets type TAnalysisResult* = enum @@ -22,17 +22,17 @@ proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult = result = arNo case n.kind of nkRecList: - for i in countup(0, sonsLen(n) - 1): - result = isPartOfAux(n.sons[i], b, marker) + for i in 0..= 2 and len(b) >= 2: + if a.len >= 2 and b.len >= 2: # array accesses: if result == arYes and isDeepConstExpr(a[1]) and isDeepConstExpr(b[1]): # we know it's the same array and we have 2 constant indexes; @@ -186,4 +186,14 @@ proc isPartOf*(a, b: PNode): TAnalysisResult = if res != arNo: result = res if res == arYes: break + of nkCallKinds: + result = arNo + for i in 1.. 0: + result = isPartOf(a, b[0]) else: discard diff --git a/compiler/asciitables.nim b/compiler/asciitables.nim index c25d54bdea0d..39bb26a5c822 100644 --- a/compiler/asciitables.nim +++ b/compiler/asciitables.nim @@ -71,7 +71,7 @@ iterator parseTableCells*(s: string, delim = '\t'): Cell = proc alignTable*(s: string, delim = '\t', fill = ' ', sep = " "): string = ## formats a `delim`-delimited `s` representing a table; each cell is aligned ## to a width that's computed for each column; consecutive columns are - ## delimted by `sep`, and alignment space is filled using `fill`. + ## delimited by `sep`, and alignment space is filled using `fill`. ## More customized formatting can be done by calling `parseTableCells` directly. for cell in parseTableCells(s, delim): result.add cell.text diff --git a/compiler/ast.nim b/compiler/ast.nim index fbe6132b3769..733f60090413 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -10,8 +10,9 @@ # abstract syntax tree + symbol table import - lineinfos, hashes, nversion, options, strutils, std / sha1, ropes, idents, - intsets, idgen + lineinfos, hashes, options, ropes, idents, idgen, int128 + +export int128 type TCallingConvention* = enum @@ -184,7 +185,7 @@ type nkStmtListExpr, # a statement list followed by an expr; this is used # to allow powerful multi-line templates nkBlockExpr, # a statement block ending in an expr; this is used - # to allowe powerful multi-line templates that open a + # to allow powerful multi-line templates that open a # temporary scope nkStmtListType, # a statement list ending in a type; for macros nkBlockType, # a statement block ending in a type; for macros @@ -216,7 +217,7 @@ type nkEnumFieldDef, # `ident = expr` in an enumeration nkArgList, # argument list nkPattern, # a special pattern; used for matching - nkReturnToken, # token used for interpretation + nkHiddenTryStmt, # token used for interpretation nkClosure, # (prc, env)-pair (internally used for code gen) nkGotoState, # used for the state machine (for iterators) nkState, # give a label to a code section (for iterators) @@ -227,7 +228,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # already 33 flags! + TSymFlag* = enum # 40 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 @@ -238,6 +239,7 @@ type sfForward, # symbol is forward declared sfImportc, # symbol is external; imported sfExportc, # symbol is exported (under a specified name) + sfMangleCpp, # mangle as cpp (combines with `sfExportc`) sfVolatile, # variable is volatile sfRegister, # variable should be placed in a register sfPure, # object is "pure" that means it has no type-information @@ -270,21 +272,26 @@ type sfNamedParamCall, # symbol needs named parameter call syntax in target # language; for interfacing with Objective C sfDiscardable, # returned value may be discarded implicitly - sfOverriden, # proc is overriden - sfCallSideLineinfo# A flag for template symbols to tell the + 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 # implementation. sfGenSym # symbol is 'gensym'ed; do not add to symbol table + sfNonReloadable # symbol will be left as-is when hot code reloading is on - + # meaning that it won't be renamed and/or changed in any way + sfGeneratedOp # proc is a generated '='; do not inject destructors in it + # variable is generated closure environment; requires early + # destruction for --newruntime. + sfTemplateParam # symbol is a template parameter + sfCursor # variable/field is a cursor, see RFC 177 for details + sfInjectDestructors # whether the proc needs the 'injectdestructors' transformation TSymFlags* = set[TSymFlag] const sfNoInit* = sfMainModule # don't generate code to init the variable - sfImmediate* = sfDispatcher - # macro or template is immediately expanded - # without considering any possible overloads sfAllUntyped* = sfVolatile # macro or template is immediately expanded \ # in a generic context @@ -338,7 +345,7 @@ type # (apparently something with bootstrapping) # if you need to add a type, they can apparently be reused tyNone, tyBool, tyChar, - tyEmpty, tyAlias, tyNil, tyExpr, tyStmt, tyTypeDesc, + tyEmpty, tyAlias, tyNil, tyUntyped, tyTyped, tyTypeDesc, tyGenericInvocation, # ``T[a, b]`` for types to invoke tyGenericBody, # ``T[a, b, body]`` last parameter is the body tyGenericInst, # ``T[a, b, realInstance]`` instantiated generic type @@ -365,7 +372,7 @@ type tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers tyFloat, tyFloat32, tyFloat64, tyFloat128, tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64, - tyOptAsRef, tySink, tyLent, + tyOwned, tySink, tyLent, tyVarargs, tyUncheckedArray # An array with boundaries [0,+∞] @@ -439,7 +446,7 @@ const tyUserTypeClass, tyUserTypeClassInst, tyAnd, tyOr, tyNot, tyAnything} - tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyExpr} + tyTypeClasses + tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyUntyped} + tyTypeClasses tyUserTypeClasses* = {tyUserTypeClass, tyUserTypeClassInst} type @@ -462,21 +469,23 @@ type nfExplicitCall # x.y() was used instead of x.y nfExprCall # this is an attempt to call a regular expression nfIsRef # this node is a 'ref' node; used for the VM + nfIsPtr # this node is a 'ptr' node; used for the VM nfPreventCg # this node should be ignored by the codegen nfBlockArg # this a stmtlist appearing in a call (e.g. a do block) nfFromTemplate # a top-level node returned from a template nfDefaultParam # an automatically inserter default parameter nfDefaultRefsParam # a default param value references another parameter # the flag is applied to proc default values and to calls + nfExecuteOnReload # A top-level statement that will be executed during reloads TNodeFlags* = set[TNodeFlag] - TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that) + TTypeFlag* = enum # keep below 32 for efficiency reasons (now: ~40) tfVarargs, # procedure has C styled varargs # tyArray type represeting a varargs list tfNoSideEffect, # procedure type does not allow side effects tfFinal, # is the object final? tfInheritable, # is the object inheritable? - tfAcyclic, # type is acyclic (for GC optimization) + tfHasOwned, # type contains an 'owned' type and must be moved tfEnumHasHoles, # enum cannot be mapped into a range tfShallow, # type can be shallow copied on assignment tfThread, # proc type is marked as ``thread``; alias for ``gcsafe`` @@ -524,9 +533,13 @@ type tfTriggersCompileTime # uses the NimNode type which make the proc # implicitly '.compiletime' tfRefsAnonObj # used for 'ref object' and 'ptr object' - tfCovariant # covariant generic param mimicing a ptr type - tfWeakCovariant # covariant generic param mimicing a seq/array type + tfCovariant # covariant generic param mimicking a ptr type + tfWeakCovariant # covariant generic param mimicking a seq/array type tfContravariant # contravariant generic param + tfCheckedForDestructor # type was checked for having a destructor. + # If it has one, t.destructor is not nil. + tfAcyclic # object type was annotated as .acyclic + tfIncompleteStruct # treat this type as if it had sizeof(pointer) TTypeFlags* = set[TTypeFlag] @@ -568,7 +581,6 @@ type const routineKinds* = {skProc, skFunc, skMethod, skIterator, skConverter, skMacro, skTemplate} - tfIncompleteStruct* = tfVarargs tfUnion* = tfNoSideEffect tfGcSafe* = tfThread tfObjHasKids* = tfEnumHasHoles @@ -597,7 +609,6 @@ type mAddF64, mSubF64, mMulF64, mDivF64, mShrI, mShlI, mAshrI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, - mMinF64, mMaxF64, mAddU, mSubU, mMulU, mDivU, mModU, mEqI, mLeI, mLtI, mEqF64, mLeF64, mLtF64, @@ -610,13 +621,7 @@ type mXor, mEqCString, mEqProc, mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, - mUnaryPlusF64, mUnaryMinusF64, mAbsF64, - mZe8ToI, mZe8ToI64, - mZe16ToI, mZe16ToI64, - mZe32ToI64, mZeIToI64, - mToU8, mToU16, mToU32, - mToFloat, mToBiggestFloat, - mToInt, mToBiggestInt, + mUnaryPlusF64, mUnaryMinusF64, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, @@ -632,7 +637,7 @@ type mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mParseBiggestFloat, mMove, mWasMoved, mDestroy, - mReset, + mDefault, mUnown, mAccessEnv, mAccessTypeInfo, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mOrdinal, @@ -644,7 +649,6 @@ type mVoidType, mPNimrodNode, mShared, mGuarded, mLock, mSpawn, mDeepCopy, mIsMainModule, mCompileDate, mCompileTime, mProcCall, mCpuEndian, mHostOS, mHostCPU, mBuildOS, mBuildCPU, mAppType, - mNaN, mInf, mNegInf, mCompileOption, mCompileOptionArg, mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind, mNSymKind, @@ -654,14 +658,15 @@ type mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, - mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, + mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNSigHash, mNSizeOf, mNBindSym, mLocals, mNCallSite, mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym, mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples, mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, - mSymIsInstantiationOf + mSymIsInstantiationOf, mNodeId + # things that we can evaluate safely at compile time, even if not asked for it: const @@ -674,7 +679,6 @@ const mAddF64, mSubF64, mMulF64, mDivF64, mShrI, mShlI, mBitandI, mBitorI, mBitxorI, mMinI, mMaxI, - mMinF64, mMaxF64, mAddU, mSubU, mMulU, mDivU, mModU, mEqI, mLeI, mLtI, mEqF64, mLeF64, mLtF64, @@ -685,13 +689,7 @@ const mEqB, mLeB, mLtB, mEqRef, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, - mUnaryPlusF64, mUnaryMinusF64, mAbsF64, - mZe8ToI, mZe8ToI64, - mZe16ToI, mZe16ToI64, - mZe32ToI64, mZeIToI64, - mToU8, mToU16, mToU32, - mToFloat, mToBiggestFloat, - mToInt, mToBiggestInt, + mUnaryPlusF64, mUnaryMinusF64, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, @@ -756,6 +754,10 @@ type lfHeader, # include header file for symbol lfImportCompilerProc, # ``importc`` of a compilerproc lfSingleUse # no location yet and will only be used once + lfEnforceDeref # a copyMem is required to dereference if this a + # ptr array due to C array limitations. + # See #1181, #6422, #11171 + lfPrepareForMutation # string location is about to be mutated (V2) TStorageLoc* = enum OnUnknown, # location is unknown (stack, heap or static) OnStatic, # in a static section @@ -824,6 +826,7 @@ type of skLet, skVar, skField, skForVar: guard*: PSym bitsize*: int + alignment*: int # for alignment else: nil magic*: TMagic typ*: PType @@ -850,11 +853,13 @@ type # for modules, an unique index corresponding # to the module's fileIdx # for variables a slot index for the evaluator - # for routines a superop-ID offset*: int # offset of record field loc*: TLoc annex*: PLib # additional fields (seldom used, so we use a - # reference to another object to safe space) + # reference to another object to save space) + when hasFFI: + cname*: string # resolved C declaration name in importc decl, eg: + # proc fun() {.importc: "$1aux".} => cname = funaux constraint*: PNode # additional constraints like 'lit|result'; also # misused for the codegenDecl pragma in the hope # it won't cause problems @@ -865,6 +870,15 @@ type TTypeSeq* = seq[PType] TLockLevel* = distinct int16 + + TTypeAttachedOp* = enum ## as usual, order is important here + attachedDestructor, + attachedAsgn, + attachedSink, + attachedTrace, + attachedDispose, + attachedDeepCopy + TType* {.acyclic.} = object of TIdObj # \ # types are identical iff they have the # same id; there may be multiple copies of a type @@ -877,7 +891,7 @@ type # for range types a nkRange node # for record types a nkRecord node # for enum types a list of symbols - # for tyInt it can be the int literal + # if kind == tyInt: it is an 'int literal(x)' type # for procs and tyGenericBody, it's the # formal param list # for concepts, the concept body @@ -885,16 +899,12 @@ type owner*: PSym # the 'owner' of the type sym*: PSym # types have the sym associated with them # it is used for converting types to strings - destructor*: PSym # destructor. warning: nil here may not necessary - # mean that there is no destructor. - # see instantiateDestructor in semdestruct.nim - deepCopy*: PSym # overriden 'deepCopy' operation - assignment*: PSym # overriden '=' operation - sink*: PSym # overriden '=sink' operation + attachedOps*: array[TTypeAttachedOp, PSym] # destructors, etc. methods*: seq[(int,PSym)] # attached methods size*: BiggestInt # the size of the type in bytes # -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 @@ -972,13 +982,15 @@ const tyTuple, tySequence} NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tyProc, tyError} + PtrLikeKinds*: TTypeKinds = {tyPointer, tyPtr} # for VM ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType, skIterator, skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfDotSetter, nfDotField, - nfIsRef, nfPreventCg, nfLL, - nfFromTemplate, nfDefaultRefsParam} + nfIsRef, nfIsPtr, nfPreventCg, nfLL, + nfFromTemplate, nfDefaultRefsParam, + nfExecuteOnReload} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 @@ -987,7 +999,7 @@ const miscPos* = 5 # used for undocumented and hacky stuff bodyPos* = 6 # position of body; use rodread.getBody() instead! resultPos* = 7 - dispatcherPos* = 8 # caution: if method has no 'result' it can be position 7! + dispatcherPos* = 8 nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit, nkHiddenCallConv} @@ -1008,6 +1020,10 @@ const skProcKinds* = {skProc, skFunc, skTemplate, skMacro, skIterator, skMethod, skConverter} + defaultSize = -1 + defaultAlignment = -1 + defaultOffset = -1 + var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things #var # gMainPackageId*: int @@ -1017,31 +1033,31 @@ proc isCallExpr*(n: PNode): bool = proc discardSons*(father: PNode) -proc len*(n: PNode): int {.inline.} = +type Indexable = PNode | PType + +proc len*(n: Indexable): int {.inline.} = when defined(nimNoNilSeqs): - result = len(n.sons) + result = n.sons.len else: if isNil(n.sons): result = 0 - else: result = len(n.sons) + else: result = n.sons.len proc safeLen*(n: PNode): int {.inline.} = ## works even for leaves. if n.kind in {nkNone..nkNilLit}: result = 0 - else: result = len(n) + else: result = n.len proc safeArrLen*(n: PNode): int {.inline.} = ## works for array-like objects (strings passed as openArray in VM). - if n.kind in {nkStrLit..nkTripleStrLit}:result = len(n.strVal) + if n.kind in {nkStrLit..nkTripleStrLit}:result = n.strVal.len elif n.kind in {nkNone..nkFloat128Lit}: result = 0 - else: result = len(n) + else: result = n.len -proc add*(father, son: PNode) = +proc add*(father, son: Indexable) = assert son != nil when not defined(nimNoNilSeqs): if isNil(father.sons): father.sons = @[] - add(father.sons, son) - -type Indexable = PNode | PType + father.sons.add(son) template `[]`*(n: Indexable, i: int): Indexable = n.sons[i] template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x @@ -1050,16 +1066,11 @@ template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int] template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x when defined(useNodeIds): - const nodeIdToDebug* = -1 # 299750 # 300761 #300863 # 300879 + const nodeIdToDebug* = -1 # 2322968 var gNodeId: int proc newNode*(kind: TNodeKind): PNode = - new(result) - result.kind = kind - #result.info = UnknownLineInfo() inlined: - result.info.fileIndex = InvalidFileIdx - result.info.col = int16(-1) - result.info.line = uint16(0) + result = PNode(kind: kind, info: unknownLineInfo) when defined(useNodeIds): result.id = gNodeId if result.id == nodeIdToDebug: @@ -1079,15 +1090,8 @@ template previouslyInferred*(t: PType): PType = proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, info: TLineInfo; options: TOptions = {}): PSym = # generates a symbol and initializes the hash field too - new(result) - result.name = name - result.kind = symKind - result.flags = {} - result.info = info - result.options = options - result.owner = owner - result.offset = -1 - result.id = getID() + result = PSym(name: name, kind: symKind, flags: {}, info: info, id: getID(), + options: options, owner: owner, offset: defaultOffset) when debugIds: registerId(result) @@ -1140,18 +1144,18 @@ const # for all kind of hash tables: proc copyStrTable*(dest: var TStrTable, src: TStrTable) = dest.counter = src.counter - setLen(dest.data, len(src.data)) - for i in countup(0, high(src.data)): dest.data[i] = src.data[i] + setLen(dest.data, src.data.len) + for i in 0..high(src.data): dest.data[i] = src.data[i] proc copyIdTable*(dest: var TIdTable, src: TIdTable) = dest.counter = src.counter - newSeq(dest.data, len(src.data)) - for i in countup(0, high(src.data)): dest.data[i] = src.data[i] + newSeq(dest.data, src.data.len) + for i in 0..high(src.data): dest.data[i] = src.data[i] proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) = dest.counter = src.counter - setLen(dest.data, len(src.data)) - for i in countup(0, high(src.data)): dest.data[i] = src.data[i] + setLen(dest.data, src.data.len) + for i in 0..high(src.data): dest.data[i] = src.data[i] proc discardSons*(father: PNode) = when defined(nimNoNilSeqs): @@ -1181,9 +1185,7 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode = result.info = info proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode = - new(result) - result.kind = kind - result.info = info + result = PNode(kind: kind, info: info) when defined(useNodeIds): result.id = gNodeId if result.id == nodeIdToDebug: @@ -1192,9 +1194,7 @@ proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode = inc gNodeId proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode = - new(result) - result.kind = kind - result.info = info + result = PNode(kind: kind, info: info) if children > 0: newSeq(result.sons, children) when defined(useNodeIds): @@ -1205,12 +1205,9 @@ proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode = inc gNodeId proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[], - typ: PType = nil): PNode = - new(result) - result.kind = kind - result.info = info - result.typ = typ + typ: PType = nil): PNode = # XXX use shallowCopy here for ownership transfer: + result = PNode(kind: kind, info: info, typ: typ) result.sons = sons when defined(useNodeIds): result.id = gNodeId @@ -1228,10 +1225,48 @@ proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = result = newNode(kind) result.intVal = intVal -proc newIntTypeNode*(kind: TNodeKind, intVal: BiggestInt, typ: PType): PNode = - result = newIntNode(kind, intVal) +proc newIntNode*(kind: TNodeKind, intVal: Int128): PNode = + result = newNode(kind) + result.intVal = castToInt64(intVal) + +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 + ## doesn't contain a specific type/types - it is often the case that only the + ## 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 = lastSon(result) + +proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode = + + # this is dirty. abstractVarRange isn't defined yet and therefore it + # is duplicated here. + const abstractVarRange = {tyGenericInst, tyRange, tyVar, tyDistinct, tyOrdinal, + tyTypeDesc, tyAlias, tyInferred, tySink, tyOwned} + case skipTypes(typ, abstractVarRange).kind + of tyInt: result = newNode(nkIntLit) + of tyInt8: result = newNode(nkInt8Lit) + of tyInt16: result = newNode(nkInt16Lit) + of tyInt32: result = newNode(nkInt32Lit) + of tyInt64: result = newNode(nkInt64Lit) + of tyChar: result = newNode(nkCharLit) + of tyUInt: result = newNode(nkUIntLit) + of tyUInt8: result = newNode(nkUInt8Lit) + of tyUInt16: result = newNode(nkUInt16Lit) + of tyUInt32: result = newNode(nkUInt32Lit) + of tyUInt64: result = newNode(nkUInt64Lit) + else: # tyBool, tyEnum + # XXX: does this really need to be the kind nkIntLit? + result = newNode(nkIntLit) + result.intVal = intVal result.typ = typ +proc newIntTypeNode*(intVal: Int128, typ: PType): PNode = + # XXX: introduce range check + newIntTypeNode(castToInt64(intVal), typ) + proc newFloatNode*(kind: TNodeKind, floatVal: BiggestFloat): PNode = result = newNode(kind) result.floatVal = floatVal @@ -1244,12 +1279,6 @@ proc newStrNode*(strVal: string; info: TLineInfo): PNode = result = newNodeI(nkStrLit, info) result.strVal = strVal -proc addSon*(father, son: PNode) = - assert son != nil - when not defined(nimNoNilSeqs): - if isNil(father.sons): father.sons = @[] - add(father.sons, son) - proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, params, name, pattern, genericParams, @@ -1262,6 +1291,8 @@ const UnspecifiedLockLevel* = TLockLevel(-1'i16) MaxLockLevel* = 1000'i16 UnknownLockLevel* = TLockLevel(1001'i16) + AttachedOpToStr*: array[TTypeAttachedOp, string] = [ + "=destroy", "=", "=sink", "=trace", "=dispose", "=deepcopy"] proc `$`*(x: TLockLevel): string = if x.ord == UnspecifiedLockLevel.ord: result = "" @@ -1275,14 +1306,10 @@ proc `$`*(s: PSym): string = result = "" proc newType*(kind: TTypeKind, owner: PSym): PType = - new(result) - result.kind = kind - result.owner = owner - result.size = -1 - result.align = -1 # default alignment - result.id = getID() - result.uniqueId = result.id - result.lockLevel = UnspecifiedLockLevel + let id = getID() + result = PType(kind: kind, owner: owner, size: defaultSize, + align: defaultAlignment, id: id, uniqueId: id, + lockLevel: UnspecifiedLockLevel) when debugIds: registerId(result) when false: @@ -1297,7 +1324,7 @@ proc mergeLoc(a: var TLoc, b: TLoc) = if a.lode == nil: a.lode = b.lode if a.r == nil: a.r = b.r -proc newSons*(father: PNode, length: int) = +proc newSons*(father: Indexable, length: int) = when defined(nimNoNilSeqs): setLen(father.sons, length) else: @@ -1306,21 +1333,6 @@ proc newSons*(father: PNode, length: int) = else: setLen(father.sons, length) -proc newSons*(father: PType, length: int) = - when defined(nimNoNilSeqs): - setLen(father.sons, length) - else: - if isNil(father.sons): - newSeq(father.sons, length) - else: - setLen(father.sons, length) - -proc sonsLen*(n: PType): int = n.sons.len -proc len*(n: PType): int = n.sons.len -proc sonsLen*(n: PNode): int = n.sons.len -proc lastSon*(n: PNode): PNode = n.sons[^1] -proc lastSon*(n: PType): PType = n.sons[^1] - proc assignType*(dest, src: PType) = dest.kind = src.kind dest.flags = src.flags @@ -1328,10 +1340,7 @@ proc assignType*(dest, src: PType) = dest.n = src.n dest.size = src.size dest.align = src.align - dest.destructor = src.destructor - dest.deepCopy = src.deepCopy - dest.sink = src.sink - dest.assignment = src.assignment + dest.attachedOps = src.attachedOps dest.lockLevel = src.lockLevel # this fixes 'type TLock = TSysLock': if src.sym != nil: @@ -1341,8 +1350,8 @@ proc assignType*(dest, src: PType) = mergeLoc(dest.sym.loc, src.sym.loc) else: dest.sym = src.sym - newSons(dest, sonsLen(src)) - for i in countup(0, sonsLen(src) - 1): dest.sons[i] = src.sons[i] + newSons(dest, src.len) + for i in 0..= nkNone and n.kind <= nkNilLit proc isEmptyType*(t: PType): bool {.inline.} = ## 'void' and 'stmt' types are often equivalent to 'nil' these days: - result = t == nil or t.kind in {tyVoid, tyStmt} + result = t == nil or t.kind in {tyVoid, tyTyped} proc makeStmtList*(n: PNode): PNode = if n.kind == nkStmtList: @@ -1703,7 +1764,7 @@ proc makeStmtList*(n: PNode): PNode = proc skipStmtList*(n: PNode): PNode = if n.kind in {nkStmtList, nkStmtListExpr}: - for i in 0 .. n.len-2: + for i in 0.. 0)) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 48a651632b99..f228de1cadb5 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -31,6 +31,15 @@ when declared(echo): proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.} proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.} + proc typekinds*(t: PType) {.deprecated.} = + var t = t + var s = "" + while t != nil and t.len > 0: + s.add $t.kind + s.add " " + t = t.lastSon + echo s + template debug*(x: PSym|PType|PNode) {.deprecated.} = when compiles(c.config): debug(c.config, x) @@ -77,7 +86,6 @@ proc idNodeTablePut*(t: var TIdNodeTable, key: PIdObj, val: PNode) # --------------------------------------------------------------------------- -proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym proc lookupInRecord*(n: PNode, field: PIdent): PSym proc mustRehash*(length, counter: int): bool proc nextTry*(h, maxHash: Hash): Hash {.inline.} @@ -108,16 +116,16 @@ proc skipConvAndClosure*(n: PNode): PNode = case result.kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64, nkClosure: - result = result.sons[0] + result = result[0] of nkHiddenStdConv, nkHiddenSubConv, nkConv: - result = result.sons[1] + result = result[1] else: break proc sameValue*(a, b: PNode): bool = result = false case a.kind of nkCharLit..nkUInt64Lit: - if b.kind in {nkCharLit..nkUInt64Lit}: result = a.intVal == b.intVal + if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) == getInt(b) of nkFloatLit..nkFloat64Lit: if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: @@ -131,8 +139,8 @@ proc leValue*(a, b: PNode): bool = # a <= b? result = false case a.kind - of nkCharLit..nkUInt32Lit: - if b.kind in {nkCharLit..nkUInt32Lit}: result = a.intVal <= b.intVal + of nkCharLit..nkUInt64Lit: + if b.kind in {nkCharLit..nkUInt64Lit}: result = getInt(a) <= getInt(b) of nkFloatLit..nkFloat64Lit: if b.kind in {nkFloatLit..nkFloat64Lit}: result = a.floatVal <= b.floatVal of nkStrLit..nkTripleStrLit: @@ -152,17 +160,17 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym = result = nil case n.kind of nkRecList: - for i in countup(0, sonsLen(n) - 1): - result = lookupInRecord(n.sons[i], field) + for i in 0.. 0 and a[alen] != '`': dec(alen) + if alen <= 0: alen = a.len + + var i = 1 + var j = 1 + while true: + while i < alen and a[i] == '_': inc i + while j < b.len and b[j] == '_': inc j + var aa = if i < alen: toLowerAscii(a[i]) else: '\0' + var bb = if j < b.len: toLowerAscii(b[j]) else: '\0' + if aa != bb: return false + + # the characters are identical: + if i >= alen: + # both cursors at the end: + if j >= b.len: return true + # not yet at the end of 'b': + return false + elif j >= b.len: + return false + inc i + inc j + +proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym = + ## Named parameters are special because a named parameter can be + ## gensym'ed and then they have '`' suffix that we need to + ## ignore, see compiler / evaltempl.nim, snippet: + ## + ##..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.. 0: - addf(result, "$N$1\"flags\": $2", [istr, flagsToStr(n.flags)]) - addf(result, "$N$1\"magic\": $2", [istr, makeYamlString($n.magic)]) - addf(result, "$N$1\"ast\": $2", [istr, ast]) - addf(result, "$N$1\"options\": $2", [istr, flagsToStr(n.options)]) - addf(result, "$N$1\"position\": $2", [istr, rope(n.position)]) - addf(result, "$N$1\"k\": $2", [istr, makeYamlString($n.loc.k)]) - addf(result, "$N$1\"storage\": $2", [istr, makeYamlString($n.loc.storage)]) + result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)]) + result.addf("$N$1\"magic\": $2", [istr, makeYamlString($n.magic)]) + result.addf("$N$1\"ast\": $2", [istr, ast]) + result.addf("$N$1\"options\": $2", [istr, flagsToStr(n.options)]) + result.addf("$N$1\"position\": $2", [istr, rope(n.position)]) + result.addf("$N$1\"k\": $2", [istr, makeYamlString($n.loc.k)]) + result.addf("$N$1\"storage\": $2", [istr, makeYamlString($n.loc.storage)]) if card(n.loc.flags) > 0: - addf(result, "$N$1\"flags\": $2", [istr, makeYamlString($n.loc.flags)]) - addf(result, "$N$1\"r\": $2", [istr, n.loc.r]) - addf(result, "$N$1\"lode\": $2", [istr, treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1)]) - addf(result, "$N$1}", [rspaces(indent)]) + result.addf("$N$1\"flags\": $2", [istr, makeYamlString($n.loc.flags)]) + result.addf("$N$1\"r\": $2", [istr, n.loc.r]) + result.addf("$N$1\"lode\": $2", [istr, treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1)]) + result.addf("$N$1}", [rspaces(indent)]) proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int, maxRecDepth: int): Rope = @@ -279,27 +327,27 @@ proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int, sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope( strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))] else: - if sonsLen(n) > 0: + if n.len > 0: sonsRope = rope("[") - for i in countup(0, sonsLen(n) - 1): - if i > 0: add(sonsRope, ",") - addf(sonsRope, "$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n.sons[i], + for i in 0.. 0: sonsRope.add(",") + sonsRope.addf("$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n[i], marker, indent + 4, maxRecDepth - 1)]) - addf(sonsRope, "$N$1]", [rspaces(indent + 2)]) + sonsRope.addf("$N$1]", [rspaces(indent + 2)]) else: sonsRope = rope("null") let istr = rspaces(indent + 2) result = rope("{") - addf(result, "$N$1\"kind\": $2", [istr, makeYamlString($n.kind)]) - addf(result, "$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth - 1)]) - addf(result, "$N$1\"n\": $2", [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)]) + result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)]) + result.addf("$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth - 1)]) + result.addf("$N$1\"n\": $2", [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)]) if card(n.flags) > 0: - addf(result, "$N$1\"flags\": $2", [istr, flagsToStr(n.flags)]) - addf(result, "$N$1\"callconv\": $2", [istr, makeYamlString(CallingConvToStr[n.callConv])]) - addf(result, "$N$1\"size\": $2", [istr, rope(n.size)]) - addf(result, "$N$1\"align\": $2", [istr, rope(n.align)]) - addf(result, "$N$1\"sons\": $2", [istr, sonsRope]) + result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)]) + result.addf("$N$1\"callconv\": $2", [istr, makeYamlString(CallingConvToStr[n.callConv])]) + result.addf("$N$1\"size\": $2", [istr, rope(n.size)]) + result.addf("$N$1\"align\": $2", [istr, rope(n.align)]) + result.addf("$N$1\"sons\": $2", [istr, sonsRope]) proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int, maxRecDepth: int): Rope = @@ -310,34 +358,34 @@ proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int, result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)] if maxRecDepth != 0: if conf != nil: - addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)]) + result.addf(",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)]) case n.kind of nkCharLit..nkInt64Lit: - addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)]) + result.addf(",$N$1\"intVal\": $2", [istr, rope(n.intVal)]) of nkFloatLit, nkFloat32Lit, nkFloat64Lit: - addf(result, ",$N$1\"floatVal\": $2", + result.addf(",$N$1\"floatVal\": $2", [istr, rope(n.floatVal.toStrMaxPrecision)]) of nkStrLit..nkTripleStrLit: - addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) + result.addf(",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) of nkSym: - addf(result, ",$N$1\"sym\": $2", + result.addf(",$N$1\"sym\": $2", [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)]) of nkIdent: if n.ident != nil: - addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)]) + result.addf(",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)]) else: - addf(result, ",$N$1\"ident\": null", [istr]) + result.addf(",$N$1\"ident\": null", [istr]) else: - if sonsLen(n) > 0: - addf(result, ",$N$1\"sons\": [", [istr]) - for i in countup(0, sonsLen(n) - 1): - if i > 0: add(result, ",") - addf(result, "$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n.sons[i], + if n.len > 0: + result.addf(",$N$1\"sons\": [", [istr]) + for i in 0.. 0: result.add(",") + result.addf("$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n[i], marker, indent + 4, maxRecDepth - 1)]) - addf(result, "$N$1]", [istr]) - addf(result, ",$N$1\"typ\": $2", + result.addf("$N$1]", [istr]) + result.addf(",$N$1\"typ\": $2", [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)]) - addf(result, "$N$1}", [rspaces(indent)]) + result.addf("$N$1}", [rspaces(indent)]) proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope = var marker = initIntSet() @@ -351,105 +399,258 @@ proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1 var marker = initIntSet() result = symToYamlAux(conf, n, marker, indent, maxRecDepth) -proc debugTree*(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; renderType=false): Rope -proc debugType(conf: ConfigRef; n: PType, maxRecDepth=100): Rope = +import tables + +const backrefStyle = "\e[90m" +const enumStyle = "\e[34m" +const numberStyle = "\e[33m" +const stringStyle = "\e[32m" +const resetStyle = "\e[0m" + +type + DebugPrinter = object + conf: ConfigRef + visited: Table[pointer, int] + renderSymType: bool + indent: int + currentLine: int + firstItem: bool + useColor: bool + res: string + +proc indentMore(this: var DebugPrinter) = + this.indent += 2 + +proc indentLess(this: var DebugPrinter) = + this.indent -= 2 + +proc newlineAndIndent(this: var DebugPrinter) = + this.res.add "\n" + this.currentLine += 1 + for i in 0.. 0) and maxRecDepth != 0: - add(result, "(") - for i in countup(0, sonsLen(n) - 1): - if i > 0: add(result, ", ") - if n.sons[i] == nil: - add(result, "null") - else: - add(result, debugType(conf, n.sons[i], maxRecDepth-1)) - if n.kind == tyObject and n.n != nil: - add(result, ", n: ") - add(result, debugTree(conf, n.n, 2, maxRecDepth-1, renderType=true)) - add(result, ")") - -proc debugTree(conf: ConfigRef; n: PNode, indent: int, maxRecDepth: int; - renderType=false): Rope = - if n == nil: - result = rope("null") + if this.useColor: + this.res.add backrefStyle + this.res.add "" + if this.useColor: + this.res.add resetStyle + return + +proc value(this: var DebugPrinter; value: PType) +proc value(this: var DebugPrinter; value: PNode) +proc value(this: var DebugPrinter; value: PSym) = + earlyExit(this, value) + + this.openCurly + this.key("kind") + this.value(value.kind) + this.key("name") + this.value(value.name.s) + this.key("id") + this.value(value.id) + if value.kind in {skField, skEnumField, skParam}: + this.key("position") + this.value(value.position) + + if card(value.flags) > 0: + this.key("flags") + this.value(value.flags) + + if this.renderSymType and value.typ != nil: + this.key "typ" + this.value(value.typ) + + this.closeCurly + +proc value(this: var DebugPrinter; value: PType) = + earlyExit(this, value) + + this.openCurly + this.key "kind" + this.value value.kind + + this.key "id" + this.value value.id + + if value.sym != nil: + this.key "sym" + this.value value.sym + #this.value value.sym.name.s + + if card(value.flags) > 0: + this.key "flags" + this.value value.flags + + if value.kind in IntegralTypes and value.n != nil: + this.key "n" + this.value value.n + + if value.len > 0: + this.key "sons" + this.openBracket + for i in 0.. 0: + this.key "flags" + this.value value.flags + + case value.kind + of nkCharLit..nkUInt64Lit: + this.key "intVal" + this.value value.intVal + of nkFloatLit, nkFloat32Lit, nkFloat64Lit: + this.key "floatVal" + this.value value.floatVal.toStrMaxPrecision + of nkStrLit..nkTripleStrLit: + this.key "strVal" + this.value value.strVal + of nkSym: + this.key "sym" + this.value value.sym + #this.value value.sym.name.s + of nkIdent: + if value.ident != nil: + this.key "ident" + this.value value.ident.s else: - var istr = rspaces(indent + 2) - result = "{$N$1\"kind\": $2" % - [istr, makeYamlString($n.kind)] - when defined(useNodeIds): - addf(result, ",$N$1\"id\": $2", [istr, rope(n.id)]) - if conf != nil: - addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)]) - if maxRecDepth != 0: - if card(n.flags) > 0: - addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)]) - case n.kind - of nkCharLit..nkUInt64Lit: - addf(result, ",$N$1\"intVal\": $2", [istr, rope(n.intVal)]) - of nkFloatLit, nkFloat32Lit, nkFloat64Lit: - addf(result, ",$N$1\"floatVal\": $2", - [istr, rope(n.floatVal.toStrMaxPrecision)]) - of nkStrLit..nkTripleStrLit: - addf(result, ",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)]) - of nkSym: - let s = n.sym - var symStr = "" - symStr.add "\"kind\": " - symStr.add $s.kind - symStr.add ", \"name\": " - symStr.add s.name.s - symStr.add ", \"id\": " - symStr.add s.id - if s.kind in {skField, skEnumField, skParam}: - symStr.add ", \"position\": " - symStr.add s.position - addf(result, ",$N$1\"sym\": {$2}", [ - istr, rope(symStr)]) - - if renderType and n.sym.typ != nil: - addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.sym.typ, 2)]) - of nkIdent: - if n.ident != nil: - addf(result, ",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)]) - else: - addf(result, ",$N$1\"ident\": null", [istr]) - else: - if renderType and n.typ != nil: - addf(result, ",$N$1\"typ\": $2", [istr, debugType(conf, n.typ, 2)]) - if sonsLen(n) > 0: - addf(result, ",$N$1\"sons\": [", [istr]) - for i in countup(0, sonsLen(n) - 1): - if i > 0: add(result, ",") - addf(result, "$N$1$2", [rspaces(indent + 4), debugTree(conf, n.sons[i], - indent + 4, maxRecDepth - 1, renderType)]) - addf(result, "$N$1]", [istr]) - addf(result, "$N$1}", [rspaces(indent)]) + if this.renderSymType and value.typ != nil: + this.key "typ" + this.value value.typ + if value.len > 0: + this.key "sons" + this.openBracket + for i in 0..= 0: swap data[h], data[favPos] + while data[h] != nil: + if data[h] == n: + # allowed for 'export' feature: + #InternalError(n.info, "StrTableRawInsert: " & n.name.s) + return + h = nextTry(h, high(data)) + assert(data[h] == nil) + data[h] = n proc symTabReplaceRaw(data: var seq[PSym], prevSym: PSym, newSym: PSym) = assert prevSym.name.h == newSym.name.h @@ -555,13 +739,13 @@ proc symTabReplace*(t: var TStrTable, prevSym: PSym, newSym: PSym) = proc strTableEnlarge(t: var TStrTable) = var n: seq[PSym] - newSeq(n, len(t.data) * GrowthFactor) - for i in countup(0, high(t.data)): + newSeq(n, t.data.len * GrowthFactor) + for i in 0..high(t.data): if t.data[i] != nil: strTableRawInsert(n, t.data[i]) swap(t.data, n) proc strTableAdd*(t: var TStrTable, n: PSym) = - if mustRehash(len(t.data), t.counter): strTableEnlarge(t) + if mustRehash(t.data.len, t.counter): strTableEnlarge(t) strTableRawInsert(t.data, n) inc(t.counter) @@ -588,7 +772,7 @@ proc strTableInclReportConflict*(t: var TStrTable, n: PSym; if not onConflictKeepOld: t.data[replaceSlot] = n # overwrite it with newer definition! return t.data[replaceSlot] # found it - elif mustRehash(len(t.data), t.counter): + elif mustRehash(t.data.len, t.counter): strTableEnlarge(t) strTableRawInsert(t.data, n) else: @@ -692,7 +876,7 @@ iterator items*(tab: TStrTable): PSym = s = nextIter(it, tab) proc hasEmptySlot(data: TIdPairSeq): bool = - for h in countup(0, high(data)): + for h in 0..high(data): if data[h].key == nil: return true result = false @@ -745,9 +929,9 @@ proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) = assert(t.data[index].key != nil) t.data[index].val = val else: - if mustRehash(len(t.data), t.counter): - newSeq(n, len(t.data) * GrowthFactor) - for i in countup(0, high(t.data)): + if mustRehash(t.data.len, t.counter): + newSeq(n, t.data.len * GrowthFactor) + for i in 0..high(t.data): if t.data[i].key != nil: idTableRawInsert(n, t.data[i].key, t.data[i].val) assert(hasEmptySlot(n)) @@ -756,7 +940,7 @@ proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) = inc(t.counter) iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] = - for i in 0 .. high(t.data): + for i in 0..high(t.data): if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int = @@ -790,10 +974,10 @@ proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) = assert(t.data[index].key != nil) t.data[index].val = val else: - if mustRehash(len(t.data), t.counter): + if mustRehash(t.data.len, t.counter): var n: TIdNodePairSeq - newSeq(n, len(t.data) * GrowthFactor) - for i in countup(0, high(t.data)): + newSeq(n, t.data.len * GrowthFactor) + for i in 0..high(t.data): if t.data[i].key != nil: idNodeTableRawInsert(n, t.data[i].key, t.data[i].val) swap(t.data, n) @@ -801,13 +985,13 @@ proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) = inc(t.counter) iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] = - for i in 0 .. high(t.data): + for i in 0..high(t.data): if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val) proc initIITable(x: var TIITable) = x.counter = 0 newSeq(x.data, StartSize) - for i in countup(0, StartSize - 1): x.data[i].key = InvalidKey + for i in 0..= 0) - x[int(elem div ElemSize)] = x[int(elem div ElemSize)] or - toU8(int(1 shl (elem mod ElemSize))) + x[int(elem.divElemSize)] = x[int(elem.divElemSize)] or + (One shl elem.modElemSize) proc bitSetExcl(x: var TBitSet, elem: BiggestInt) = - x[int(elem div ElemSize)] = x[int(elem div ElemSize)] and - not toU8(int(1 shl (elem mod ElemSize))) + x[int(elem.divElemSize)] = x[int(elem.divElemSize)] and + not(One shl elem.modElemSize) proc bitSetInit(b: var TBitSet, length: int) = newSeq(b, length) proc bitSetUnion(x: var TBitSet, y: TBitSet) = - for i in countup(0, high(x)): x[i] = x[i] or y[i] + for i in 0..high(x): x[i] = x[i] or y[i] proc bitSetDiff(x: var TBitSet, y: TBitSet) = - for i in countup(0, high(x)): x[i] = x[i] and not y[i] + for i in 0..high(x): x[i] = x[i] and not y[i] proc bitSetSymDiff(x: var TBitSet, y: TBitSet) = - for i in countup(0, high(x)): x[i] = x[i] xor y[i] + for i in 0..high(x): x[i] = x[i] xor y[i] proc bitSetIntersect(x: var TBitSet, y: TBitSet) = - for i in countup(0, high(x)): x[i] = x[i] and y[i] + for i in 0..high(x): x[i] = x[i] and y[i] proc bitSetEquals(x, y: TBitSet): bool = - for i in countup(0, high(x)): + for i in 0..high(x): if x[i] != y[i]: return false result = true proc bitSetContains(x, y: TBitSet): bool = - for i in countup(0, high(x)): - if (x[i] and not y[i]) != int8(0): + for i in 0..high(x): + if (x[i] and not y[i]) != Zero: return false result = true # Number of set bits for all values of int8 -const populationCount: array[low(int8)..high(int8), int8] = block: - var arr: array[low(int8)..high(int8), int8] +const populationCount: array[uint8, uint8] = block: + var arr: array[uint8, uint8] - proc countSetBits(x: int8): int8 = + proc countSetBits(x: uint8): uint8 = return - ( x and 0b00000001'i8) + - ((x and 0b00000010'i8) shr 1) + - ((x and 0b00000100'i8) shr 2) + - ((x and 0b00001000'i8) shr 3) + - ((x and 0b00010000'i8) shr 4) + - ((x and 0b00100000'i8) shr 5) + - ((x and 0b01000000'i8) shr 6) + - ((x and 0b10000000'i8) shr 7) - - - for it in low(int8)..high(int8): - arr[it] = countSetBits(it) + ( x and 0b00000001'u8) + + ((x and 0b00000010'u8) shr 1) + + ((x and 0b00000100'u8) shr 2) + + ((x and 0b00001000'u8) shr 3) + + ((x and 0b00010000'u8) shr 4) + + ((x and 0b00100000'u8) shr 5) + + ((x and 0b01000000'u8) shr 6) + + ((x and 0b10000000'u8) shr 7) + + + for it in low(uint8)..high(uint8): + arr[it] = countSetBits(cast[uint8](it)) arr proc bitSetCard(x: TBitSet): BiggestInt = for it in x: - result.inc populationCount[it] + result.inc int(populationCount[it]) diff --git a/compiler/btrees.nim b/compiler/btrees.nim index 6cd6e51f4c8b..4f9031a6b36c 100644 --- a/compiler/btrees.nim +++ b/compiler/btrees.nim @@ -37,34 +37,34 @@ template eq(a, b): bool = cmp(a, b) == 0 proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val = var x = b.root while x.isInternal: - for j in 0 ..< x.entries: + for j in 0..') + result.add('>') proc encodeType(w: PRodWriter, t: PType, result: var string) = if t == nil: @@ -277,47 +277,47 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) = if t.kind == tyForward: internalError("encodeType: tyForward") # for the new rodfile viewer we use a preceding [ so that the data section # can easily be disambiguated: - add(result, '[') + result.add('[') encodeVInt(ord(t.kind), result) - add(result, '+') + result.add('+') encodeVInt(t.id, result) if t.n != nil: - encodeNode(w, unknownLineInfo(), t.n, result) + encodeNode(w, unknownLineInfo, t.n, result) if t.flags != {}: - add(result, '$') + result.add('$') encodeVInt(cast[int32](t.flags), result) if t.callConv != low(t.callConv): - add(result, '?') + result.add('?') encodeVInt(ord(t.callConv), result) if t.owner != nil: - add(result, '*') + result.add('*') encodeVInt(t.owner.id, result) pushSym(w, t.owner) if t.sym != nil: - add(result, '&') + result.add('&') encodeVInt(t.sym.id, result) pushSym(w, t.sym) if t.size != - 1: - add(result, '/') + result.add('/') encodeVBiggestInt(t.size, result) if t.align != - 1: - add(result, '=') + result.add('=') encodeVInt(t.align, result) encodeLoc(w, t.loc, result) - for i in countup(0, sonsLen(t) - 1): - if t.sons[i] == nil: - add(result, "^()") + for i in 0.. 0: + skipped = true + q = q.lastSon if getMagic(q) == mSlice: # magic: pass slice to openArray: + if skipped: + q = skipConv(n) + while q.kind == nkStmtListExpr and q.len > 0: + for i in 0.. 1: add(pl, ~", ") + if typ[0] != nil: + 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 leftAppearsOnRightSide(le, ri): # Great, we can use 'd': if d.k == locNone: - getTemp(p, typ.sons[0], d, needsInit=true) + 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)" - add(pl, addrLoc(p.config, d)) + pl.add(addrLoc(p.config, d)) genCallPattern() else: var tmp: TLoc - getTemp(p, typ.sons[0], tmp, needsInit=true) - add(pl, addrLoc(p.config, tmp)) + getTemp(p, typ[0], tmp, needsInit=true) + pl.add(addrLoc(p.config, tmp)) genCallPattern() genAssignment(p, d, tmp, {}) # no need for deep copying else: - if d.k == locNone: getTemp(p, typ.sons[0], d) + if d.k == locNone: getTemp(p, typ[0], d) assert(d.t != nil) # generate an assignment to d: var list: TLoc initLoc(list, locCall, d.lode, OnUnknown) - list.r = callPattern % [op.r, pl, pl.addComma, rawProc] + if tfIterator in typ.flags: + list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc] + else: + list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc] + genAssignment(p, d, list, {}) # no need for deep copying else: genCallPattern() proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = - if i < sonsLen(typ): + 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.sons[i] + let paramType = typ.n[i] assert(paramType.kind == nkSym) if paramType.typ.isCompileTimeOnly: result = nil - elif typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr: - result = genArgNoParam(p, ri.sons[i][0]) + elif typ[i].kind == tyVar and ri[i].kind == nkHiddenAddr: + result = genArgNoParam(p, ri[i][0]) else: - result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym) + 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: - result = genArgNoParam(p, ri.sons[i]) + result = genArgNoParam(p, ri[i]) discard """ Dot call syntax in C++ @@ -303,16 +338,16 @@ proc skipAddrDeref(node: PNode): PNode = var isAddr = false case n.kind of nkAddr, nkHiddenAddr: - n = n.sons[0] + n = n[0] isAddr = true of nkDerefExpr, nkHiddenDeref: - n = n.sons[0] + n = n[0] else: return n - if n.kind == nkObjDownConv: n = n.sons[0] + if n.kind == nkObjDownConv: n = n[0] if isAddr and n.kind in {nkDerefExpr, nkHiddenDeref}: - result = n.sons[0] + result = n[0] elif n.kind in {nkAddr, nkHiddenAddr}: - result = n.sons[0] + result = n[0] else: result = node @@ -320,13 +355,13 @@ 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. - internalAssert p.config, i < sonsLen(typ) - assert(typ.n.sons[i].kind == nkSym) + internalAssert p.config, i < typ.len + assert(typ.n[i].kind == nkSym) # if the parameter is lying (tyVar) and thus we required an additional deref, # skip the deref: var ri = ri[i] while ri.kind == nkObjDownConv: ri = ri[0] - let t = typ.sons[i].skipTypes({tyGenericInst, tyAlias, tySink}) + let t = typ[i].skipTypes({tyGenericInst, tyAlias, tySink}) if t.kind == tyVar: let x = if ri.kind == nkHiddenAddr: ri[0] else: ri if x.typ.kind == tyPtr: @@ -348,7 +383,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = else: ri = skipAddrDeref(ri) if ri.kind in {nkAddr, nkHiddenAddr}: ri = ri[0] - result = genArgNoParam(p, ri) #, typ.n.sons[i].sym) + result = genArgNoParam(p, ri) #, typ.n[i].sym) result.add(".") proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope = @@ -357,33 +392,36 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope = while i < pat.len: case pat[i] of '@': - if j < ri.len: - result.add genOtherArg(p, ri, j, typ) - for k in j+1 ..< ri.len: - result.add(~", ") - result.add genOtherArg(p, ri, k, typ) + var first = true + for k in j.. 0: + if not first: + result.add(~", ") + first = false + result.add arg inc i of '#': - if pat[i+1] in {'+', '@'}: + if i+1 < pat.len and pat[i+1] in {'+', '@'}: let ri = ri[j] if ri.kind in nkCallKinds: - let typ = skipTypes(ri.sons[0].typ, abstractInst) - if pat[i+1] == '+': result.add genArgNoParam(p, ri.sons[0]) + let typ = skipTypes(ri[0].typ, abstractInst) + if pat[i+1] == '+': result.add genArgNoParam(p, ri[0]) result.add(~"(") if 1 < ri.len: result.add genOtherArg(p, ri, 1, typ) - for k in j+1 ..< ri.len: + for k in j+1..= start: - add(result, substr(pat, start, i - 1)) + result.add(substr(pat, start, i - 1)) proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = var op: TLoc - initLocExpr(p, ri.sons[0], op) + initLocExpr(p, ri[0], op) # getUniqueType() is too expensive here: - var typ = skipTypes(ri.sons[0].typ, abstractInst) + var typ = skipTypes(ri[0].typ, abstractInst) assert(typ.kind == tyProc) - var length = sonsLen(ri) - assert(sonsLen(typ) == sonsLen(typ.n)) + assert(typ.len == typ.n.len) # don't call '$' here for efficiency: - let pat = ri.sons[0].sym.loc.r.data + let pat = ri[0].sym.loc.r.data internalAssert p.config, pat.len > 0 if pat.contains({'#', '(', '@', '\''}): var pl = genPatternCall(p, ri, pat, typ) # simpler version of 'fixupCall' that works with the pl+params combination: - var typ = skipTypes(ri.sons[0].typ, abstractInst) - if typ.sons[0] != nil: + var typ = skipTypes(ri[0].typ, abstractInst) + if typ[0] != nil: if p.module.compileToCpp and lfSingleUse in d.flags: # do not generate spurious temporaries for C++! For C we're better off # with them to prevent undefined behaviour and because the codegen @@ -429,116 +466,119 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) = d.r = pl excl d.flags, lfSingleUse else: - if d.k == locNone: getTemp(p, typ.sons[0], d) + if d.k == locNone: getTemp(p, typ[0], d) assert(d.t != nil) # generate an assignment to d: var list: TLoc initLoc(list, locCall, d.lode, OnUnknown) list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: - add(pl, ~";$n") + pl.add(~";$n") line(p, cpsStmts, pl) else: var pl: Rope = nil - #var param = typ.n.sons[1].sym + #var param = typ.n[1].sym if 1 < ri.len: - add(pl, genThisArg(p, ri, 1, typ)) - add(pl, op.r) + pl.add(genThisArg(p, ri, 1, typ)) + pl.add(op.r) var params: Rope - for i in countup(2, length - 1): + for i in 2.. 0 var start = 3 if ' ' in pat: start = 1 - add(pl, op.r) - if length > 1: - add(pl, ~": ") - add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) + pl.add(op.r) + if ri.len > 1: + pl.add(~": ") + pl.add(genArg(p, ri[1], typ.n[1].sym, ri)) start = 2 else: - if length > 1: - add(pl, genArg(p, ri.sons[1], typ.n.sons[1].sym, ri)) - add(pl, ~" ") - add(pl, op.r) - if length > 2: - add(pl, ~": ") - add(pl, genArg(p, ri.sons[2], typ.n.sons[2].sym, ri)) - for i in countup(start, length-1): - assert(sonsLen(typ) == sonsLen(typ.n)) - if i >= sonsLen(typ): + if ri.len > 1: + pl.add(genArg(p, ri[1], typ.n[1].sym, ri)) + pl.add(~" ") + pl.add(op.r) + if ri.len > 2: + pl.add(~": ") + pl.add(genArg(p, ri[2], typ.n[2].sym, ri)) + for i in start..= typ.len: internalError(p.config, ri.info, "varargs for objective C method?") - assert(typ.n.sons[i].kind == nkSym) - var param = typ.n.sons[i].sym - add(pl, ~" ") - add(pl, param.name.s) - add(pl, ~": ") - add(pl, genArg(p, ri.sons[i], param, ri)) - if typ.sons[0] != nil: - if isInvalidReturnType(p.config, typ.sons[0]): - if sonsLen(ri) > 1: add(pl, ~" ") + assert(typ.n[i].kind == nkSym) + var param = typ.n[i].sym + pl.add(~" ") + pl.add(param.name.s) + pl.add(~": ") + pl.add(genArg(p, ri[i], param, ri)) + if typ[0] != nil: + 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.sons[0], d, needsInit=true) - add(pl, ~"Result: ") - add(pl, addrLoc(p.config, d)) - add(pl, ~"];$n") + if d.k == locNone: getTemp(p, typ[0], d, needsInit=true) + pl.add(~"Result: ") + pl.add(addrLoc(p.config, d)) + pl.add(~"];$n") line(p, cpsStmts, pl) else: var tmp: TLoc - getTemp(p, typ.sons[0], tmp, needsInit=true) - add(pl, addrLoc(p.config, tmp)) - add(pl, ~"];$n") + getTemp(p, typ[0], tmp, needsInit=true) + pl.add(addrLoc(p.config, tmp)) + pl.add(~"];$n") line(p, cpsStmts, pl) genAssignment(p, d, tmp, {}) # no need for deep copying else: - add(pl, ~"]") - if d.k == locNone: getTemp(p, typ.sons[0], d) + pl.add(~"]") + if d.k == locNone: getTemp(p, typ[0], d) assert(d.t != nil) # generate an assignment to d: var list: TLoc initLoc(list, locCall, ri, OnUnknown) list.r = pl genAssignment(p, d, list, {}) # no need for deep copying else: - add(pl, ~"];$n") + pl.add(~"];$n") line(p, cpsStmts, pl) proc genCall(p: BProc, e: PNode, d: var TLoc) = - if e.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}).callConv == ccClosure: + if e[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure: genClosureCall(p, nil, e, d) - elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags: + elif e[0].kind == nkSym and sfInfixCall in e[0].sym.flags: genInfixCall(p, nil, e, d) - elif e.sons[0].kind == nkSym and sfNamedParamCall in e.sons[0].sym.flags: + elif e[0].kind == nkSym and sfNamedParamCall in e[0].sym.flags: genNamedParamCall(p, e, d) else: genPrefixCall(p, nil, e, d) postStmtActions(p) + if p.config.exc == excGoto and canRaise(e[0]): + raiseExit(p) proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = - if ri.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}).callConv == ccClosure: + if ri[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure: genClosureCall(p, le, ri, d) - elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags: + elif ri[0].kind == nkSym and sfInfixCall in ri[0].sym.flags: genInfixCall(p, le, ri, d) - elif ri.sons[0].kind == nkSym and sfNamedParamCall in ri.sons[0].sym.flags: + elif ri[0].kind == nkSym and sfNamedParamCall in ri[0].sym.flags: genNamedParamCall(p, ri, d) else: genPrefixCall(p, le, ri, d) postStmtActions(p) + if p.config.exc == excGoto and canRaise(ri[0]): + raiseExit(p) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 8ccca9813109..e52be902b131 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -9,6 +9,10 @@ # included from cgen.nim +proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, + result: var Rope; count: var int; + isConst: bool, info: TLineInfo) + # -------------------------- constant expressions ------------------------ proc int64Literal(i: BiggestInt): Rope = @@ -30,6 +34,9 @@ proc intLiteral(i: BiggestInt): Rope = else: result = ~"(IL64(-9223372036854775807) - IL64(1))" +proc intLiteral(i: Int128): Rope = + intLiteral(toInt64(i)) + proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = case n.kind of nkCharLit..nkUInt64Lit: @@ -61,7 +68,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = if id == p.module.labels: # not found in cache: inc(p.module.labels) - addf(p.module.s[cfsData], + p.module.s[cfsData].addf( "static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n", [getTypeDesc(p.module, ty), result]) else: @@ -76,7 +83,7 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = # with the new semantics for 'nil' strings, we can map "" to nil and # save tons of allocations: if n.strVal.len == 0 and optNilSeqs notin p.options and - p.config.selectedGc != gcDestructors: + optSeqDestructors notin p.config.globalOptions: result = genNilStringLiteral(p.module, n.info) else: result = genStringLiteral(p.module, n) @@ -93,35 +100,30 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = proc genLiteral(p: BProc, n: PNode): Rope = result = genLiteral(p, n, n.typ) -proc bitSetToWord(s: TBitSet, size: int): BiggestInt = +proc bitSetToWord(s: TBitSet, size: int): BiggestUInt = result = 0 - when true: - for j in countup(0, size - 1): - if j < len(s): result = result or `shl`(ze64(s[j]), j * 8) - else: - # not needed, too complex thinking: - if CPU[platform.hostCPU].endian == CPU[targetCPU].endian: - for j in countup(0, size - 1): - if j < len(s): result = result or `shl`(Ze64(s[j]), j * 8) - else: - for j in countup(0, size - 1): - if j < len(s): result = result or `shl`(Ze64(s[j]), (Size - 1 - j) * 8) + for j in 0.. 8: - result = "{$n" % [] - for i in countup(0, size - 1): + var res = "{\n" + for i in 0.. $3) #raiseOverflow();$n", - result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t))) + [result, intLiteral(firstOrd(p.config, t)), intLiteral(lastOrd(p.config, t))]) + result proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = const prc: array[mAddI..mPred, string] = [ - "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n", - "$# = #mulInt($#, $#);$n", "$# = #divInt($#, $#);$n", - "$# = #modInt($#, $#);$n", - "$# = #addInt($#, $#);$n", "$# = #subInt($#, $#);$n"] + "addInt", "subInt", + "mulInt", "divInt", "modInt", + "addInt", "subInt" + ] prc64: array[mAddI..mPred, string] = [ - "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n", - "$# = #mulInt64($#, $#);$n", "$# = #divInt64($#, $#);$n", - "$# = #modInt64($#, $#);$n", - "$# = #addInt64($#, $#);$n", "$# = #subInt64($#, $#);$n"] - opr: array[mAddI..mPred, string] = [ - "($#)($# + $#)", "($#)($# - $#)", "($#)($# * $#)", - "($#)($# / $#)", "($#)($# % $#)", - "($#)($# + $#)", "($#)($# - $#)"] + "addInt64", "subInt64", + "mulInt64", "divInt64", "modInt64", + "addInt64", "subInt64" + ] + opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"] var a, b: TLoc - assert(e.sons[1].typ != nil) - assert(e.sons[2].typ != nil) - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) + assert(e[1].typ != nil) + assert(e[2].typ != nil) + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) # skipping 'range' is correct here as we'll generate a proper range check # later via 'chckRange' let t = e.typ.skipTypes(abstractRange) if optOverflowCheck notin p.options: - let res = opr[m] % [getTypeDesc(p.module, e.typ), rdLoc(a), rdLoc(b)] + let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)] putIntoDest(p, d, e, res) else: let res = binaryArithOverflowRaw(p, t, a, b, - if t.kind == tyInt64: prc64[m] else: prc[m]) + if t.kind == tyInt64: prc64[m] else: prc[m]) putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res]) proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = - const - opr: array[mUnaryMinusI..mAbsI, string] = [ - mUnaryMinusI: "((NI$2)-($1))", - mUnaryMinusI64: "-($1)", - mAbsI: "($1 > 0? ($1) : -($1))"] var a: TLoc t: PType - assert(e.sons[1].typ != nil) - initLocExpr(p, e.sons[1], a) + assert(e[1].typ != nil) + initLocExpr(p, e[1], a) t = skipTypes(e.typ, abstractRange) if optOverflowCheck in p.options: linefmt(p, cpsStmts, "if ($1 == $2) #raiseOverflow();$n", - rdLoc(a), intLiteral(firstOrd(p.config, t))) - putIntoDest(p, d, e, opr[m] % [rdLoc(a), rope(getSize(p.config, t) * 8)]) + [rdLoc(a), intLiteral(firstOrd(p.config, t))]) + case m + of mUnaryMinusI: + putIntoDest(p, d, e, "((NI$2)-($1))" % [rdLoc(a), rope(getSize(p.config, t) * 8)]) + of mUnaryMinusI64: + putIntoDest(p, d, e, "-($1)" % [rdLoc(a)]) + of mAbsI: + putIntoDest(p, d, e, "($1 > 0? ($1) : -($1))" % [rdLoc(a)]) + else: + assert(false, $m) proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = - const - binArithTab: array[mAddF64..mXor, string] = [ - "(($4)($1) + ($4)($2))", # AddF64 - "(($4)($1) - ($4)($2))", # SubF64 - "(($4)($1) * ($4)($2))", # MulF64 - "(($4)($1) / ($4)($2))", # DivF64 - "($4)((NU$5)($1) >> (NU$3)($2))", # ShrI - "($4)((NU$3)($1) << (NU$3)($2))", # ShlI - "($4)((NI$3)($1) >> (NU$3)($2))", # AshrI - "($4)($1 & $2)", # BitandI - "($4)($1 | $2)", # BitorI - "($4)($1 ^ $2)", # BitxorI - "(($1 <= $2) ? $1 : $2)", # MinI - "(($1 >= $2) ? $1 : $2)", # MaxI - "(($1 <= $2) ? $1 : $2)", # MinF64 - "(($1 >= $2) ? $1 : $2)", # MaxF64 - "($4)((NU$3)($1) + (NU$3)($2))", # AddU - "($4)((NU$3)($1) - (NU$3)($2))", # SubU - "($4)((NU$3)($1) * (NU$3)($2))", # MulU - "($4)((NU$3)($1) / (NU$3)($2))", # DivU - "($4)((NU$3)($1) % (NU$3)($2))", # ModU - "($1 == $2)", # EqI - "($1 <= $2)", # LeI - "($1 < $2)", # LtI - "($1 == $2)", # EqF64 - "($1 <= $2)", # LeF64 - "($1 < $2)", # LtF64 - "((NU$3)($1) <= (NU$3)($2))", # LeU - "((NU$3)($1) < (NU$3)($2))", # LtU - "((NU64)($1) <= (NU64)($2))", # LeU64 - "((NU64)($1) < (NU64)($2))", # LtU64 - "($1 == $2)", # EqEnum - "($1 <= $2)", # LeEnum - "($1 < $2)", # LtEnum - "((NU8)($1) == (NU8)($2))", # EqCh - "((NU8)($1) <= (NU8)($2))", # LeCh - "((NU8)($1) < (NU8)($2))", # LtCh - "($1 == $2)", # EqB - "($1 <= $2)", # LeB - "($1 < $2)", # LtB - "($1 == $2)", # EqRef - "($1 == $2)", # EqPtr - "($1 <= $2)", # LePtr - "($1 < $2)", # LtPtr - "($1 != $2)"] # Xor var a, b: TLoc s, k: BiggestInt - assert(e.sons[1].typ != nil) - assert(e.sons[2].typ != nil) - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) + assert(e[1].typ != nil) + assert(e[2].typ != nil) + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) # BUGFIX: cannot use result-type here, as it may be a boolean s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8 k = getSize(p.config, a.t) * 8 - putIntoDest(p, d, e, - binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s), - getSimpleTypeDesc(p.module, e.typ), rope(k)]) + + template applyFormat(frmt: untyped) = + putIntoDest(p, d, e, frmt % [ + rdLoc(a), rdLoc(b), rope(s), + getSimpleTypeDesc(p.module, e.typ), rope(k)] + ) + + case op + of mAddF64: applyFormat("(($4)($1) + ($4)($2))") + of mSubF64: applyFormat("(($4)($1) - ($4)($2))") + of mMulF64: applyFormat("(($4)($1) * ($4)($2))") + of mDivF64: applyFormat("(($4)($1) / ($4)($2))") + of mShrI: applyFormat("($4)((NU$5)($1) >> (NU$3)($2))") + of mShlI: applyFormat("($4)((NU$3)($1) << (NU$3)($2))") + of mAshrI: applyFormat("($4)((NI$3)($1) >> (NU$3)($2))") + of mBitandI: applyFormat("($4)($1 & $2)") + of mBitorI: applyFormat("($4)($1 | $2)") + of mBitxorI: applyFormat("($4)($1 ^ $2)") + of mMinI: applyFormat("(($1 <= $2) ? $1 : $2)") + of mMaxI: applyFormat("(($1 >= $2) ? $1 : $2)") + of mAddU: applyFormat("($4)((NU$3)($1) + (NU$3)($2))") + of mSubU: applyFormat("($4)((NU$3)($1) - (NU$3)($2))") + of mMulU: applyFormat("($4)((NU$3)($1) * (NU$3)($2))") + of mDivU: applyFormat("($4)((NU$3)($1) / (NU$3)($2))") + of mModU: applyFormat("($4)((NU$3)($1) % (NU$3)($2))") + of mEqI: applyFormat("($1 == $2)") + of mLeI: applyFormat("($1 <= $2)") + of mLtI: applyFormat("($1 < $2)") + of mEqF64: applyFormat("($1 == $2)") + of mLeF64: applyFormat("($1 <= $2)") + of mLtF64: applyFormat("($1 < $2)") + of mLeU: applyFormat("((NU$3)($1) <= (NU$3)($2))") + of mLtU: applyFormat("((NU$3)($1) < (NU$3)($2))") + of mLeU64: applyFormat("((NU64)($1) <= (NU64)($2))") + of mLtU64: applyFormat("((NU64)($1) < (NU64)($2))") + of mEqEnum: applyFormat("($1 == $2)") + of mLeEnum: applyFormat("($1 <= $2)") + of mLtEnum: applyFormat("($1 < $2)") + of mEqCh: applyFormat("((NU8)($1) == (NU8)($2))") + of mLeCh: applyFormat("((NU8)($1) <= (NU8)($2))") + of mLtCh: applyFormat("((NU8)($1) < (NU8)($2))") + of mEqB: applyFormat("($1 == $2)") + of mLeB: applyFormat("($1 <= $2)") + of mLtB: applyFormat("($1 < $2)") + of mEqRef: applyFormat("($1 == $2)") + of mEqUntracedRef: applyFormat("($1 == $2)") + of mLePtr: applyFormat("($1 <= $2)") + of mLtPtr: applyFormat("($1 < $2)") + of mXor: applyFormat("($1 != $2)") + else: + assert(false, $op) proc genEqProc(p: BProc, e: PNode, d: var TLoc) = var a, b: TLoc - assert(e.sons[1].typ != nil) - assert(e.sons[2].typ != nil) - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) - if a.t.skipTypes(abstractInst).callConv == ccClosure: + assert(e[1].typ != nil) + assert(e[2].typ != nil) + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) + if a.t.skipTypes(abstractInstOwned).callConv == ccClosure: putIntoDest(p, d, e, "($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)]) else: putIntoDest(p, d, e, "($1 == $2)" % [rdLoc(a), rdLoc(b)]) proc genIsNil(p: BProc, e: PNode, d: var TLoc) = - let t = skipTypes(e.sons[1].typ, abstractRange) + let t = skipTypes(e[1].typ, abstractRange) if t.kind == tyProc and t.callConv == ccClosure: unaryExpr(p, e, d, "($1.ClP_0 == 0)") else: unaryExpr(p, e, d, "($1 == 0)") proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = - const - unArithTab: array[mNot..mToBiggestInt, string] = ["!($1)", # Not - "$1", # UnaryPlusI - "($3)((NU$2) ~($1))", # BitnotI - "$1", # UnaryPlusF64 - "-($1)", # UnaryMinusF64 - "($1 < 0? -($1) : ($1))", # AbsF64; BUGFIX: fabs() makes problems - # for Tiny C, so we don't use it - "(($3)(NU)(NU8)($1))", # mZe8ToI - "(($3)(NU64)(NU8)($1))", # mZe8ToI64 - "(($3)(NU)(NU16)($1))", # mZe16ToI - "(($3)(NU64)(NU16)($1))", # mZe16ToI64 - "(($3)(NU64)(NU32)($1))", # mZe32ToI64 - "(($3)(NU64)(NU)($1))", # mZeIToI64 - "(($3)(NU8)(NU)($1))", # ToU8 - "(($3)(NU16)(NU)($1))", # ToU16 - "(($3)(NU32)(NU64)($1))", # ToU32 - "((double) ($1))", # ToFloat - "((double) ($1))", # ToBiggestFloat - "float64ToInt32($1)", # ToInt - "float64ToInt64($1)"] # ToBiggestInt var a: TLoc t: PType - assert(e.sons[1].typ != nil) - initLocExpr(p, e.sons[1], a) + assert(e[1].typ != nil) + initLocExpr(p, e[1], a) t = skipTypes(e.typ, abstractRange) - putIntoDest(p, d, e, - unArithTab[op] % [rdLoc(a), rope(getSize(p.config, t) * 8), + + template applyFormat(frmt: untyped) = + putIntoDest(p, d, e, frmt % [rdLoc(a), rope(getSize(p.config, t) * 8), getSimpleTypeDesc(p.module, e.typ)]) + case op + of mNot: + applyFormat("!($1)") + of mUnaryPlusI: + applyFormat("$1") + of mBitnotI: + applyFormat("($3)((NU$2) ~($1))") + of mUnaryPlusF64: + applyFormat("$1") + of mUnaryMinusF64: + applyFormat("-($1)") + else: + assert false, $op proc isCppRef(p: BProc; typ: PType): bool {.inline.} = result = p.module.compileToCpp and - skipTypes(typ, abstractInst).kind == tyVar and - tfVarIsPtr notin skipTypes(typ, abstractInst).flags + skipTypes(typ, abstractInstOwned).kind == tyVar and + tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags -proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = - let mt = mapType(p.config, e.sons[0].typ) - if mt in {ctArray, ctPtrToArray} and not enforceDeref: +proc genDeref(p: BProc, e: PNode, d: var TLoc) = + let mt = mapType(p.config, e[0].typ) + if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags: # XXX the amount of hacks for C's arrays is incredible, maybe we should # simply wrap them in a struct? --> Losing auto vectorization then? - #if e[0].kind != nkBracketExpr: - # message(e.info, warnUser, "CAME HERE " & renderTree(e)) - expr(p, e.sons[0], d) - if e.sons[0].typ.skipTypes(abstractInst).kind == tyRef: + expr(p, e[0], d) + if e[0].typ.skipTypes(abstractInstOwned).kind == tyRef: d.storage = OnHeap else: var a: TLoc - var typ = e.sons[0].typ + var typ = e[0].typ if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass: typ = typ.lastSon - typ = typ.skipTypes(abstractInst) - if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.sons[0].kind == nkHiddenAddr: + typ = typ.skipTypes(abstractInstOwned) + if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr: initLocExprSingleUse(p, e[0][0], d) return else: - initLocExprSingleUse(p, e.sons[0], a) + initLocExprSingleUse(p, e[0], a) if d.k == locNone: # dest = *a; <-- We do not know that 'dest' is on the heap! # It is completely wrong to set 'd.storage' here, unless it's not yet @@ -714,36 +714,36 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) = e.kind == nkHiddenDeref: putIntoDest(p, d, e, rdLoc(a), a.storage) return - if enforceDeref and mt == ctPtrToArray: + if mt == ctPtrToArray and lfEnforceDeref in d.flags: # we lie about the type for better C interop: 'ptr array[3,T]' is # translated to 'ptr T', but for deref'ing this produces wrong code. # See tmissingderef. So we get rid of the deref instead. The codegen # ends up using 'memcpy' for the array assignment, # so the '&' and '*' cancel out: - putIntoDest(p, d, lodeTyp(a.t.sons[0]), rdLoc(a), a.storage) + putIntoDest(p, d, lodeTyp(a.t[0]), rdLoc(a), a.storage) else: putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage) proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: - if e.sons[0].typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}: + if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}: var a: TLoc - initLocExpr(p, e.sons[0], a) + initLocExpr(p, e[0], a) putIntoDest(p, d, e, "&" & a.r, a.storage) #Message(e.info, warnUser, "HERE NEW &") - elif mapType(p.config, e.sons[0].typ) == ctArray or isCppRef(p, e.typ): - expr(p, e.sons[0], d) + elif mapType(p.config, e[0].typ) == ctArray or isCppRef(p, e.typ): + expr(p, e[0], d) else: var a: TLoc - initLocExpr(p, e.sons[0], a) + initLocExpr(p, e[0], a) putIntoDest(p, d, e, addrLoc(p.config, a), a.storage) template inheritLocation(d: var TLoc, a: TLoc) = if d.k == locNone: d.storage = a.storage proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) = - initLocExpr(p, e.sons[0], a) - if e.sons[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux") + initLocExpr(p, e[0], a) + if e[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux") d.inheritLocation(a) discard getTypeDesc(p.module, a.t) # fill the record's fields.loc @@ -751,16 +751,16 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) = var a: TLoc i: int - initLocExpr(p, e.sons[0], a) - let tupType = a.t.skipTypes(abstractInst) + initLocExpr(p, e[0], a) + let tupType = a.t.skipTypes(abstractInst+{tyVar}) assert tupType.kind == tyTuple d.inheritLocation(a) discard getTypeDesc(p.module, a.t) # fill the record's fields.loc var r = rdLoc(a) - case e.sons[1].kind - of nkIntLit..nkUInt64Lit: i = int(e.sons[1].intVal) + case e[1].kind + of nkIntLit..nkUInt64Lit: i = int(e[1].intVal) else: internalError(p.config, e.info, "genTupleElem") - addf(r, ".Field$1", [rope(i)]) + r.addf(".Field$1", [rope(i)]) putIntoDest(p, d, e, r, a.storage) proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; @@ -774,84 +774,82 @@ proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope; if result != nil: if resTyp != nil: resTyp[] = ty break - if not p.module.compileToCpp: add(r, ".Sup") - ty = ty.sons[0] + if not p.module.compileToCpp: r.add(".Sup") + ty = ty[0] if result == nil: internalError(p.config, field.info, "genCheckedRecordField") proc genRecordField(p: BProc, e: PNode, d: var TLoc) = var a: TLoc genRecordFieldAux(p, e, d, a) var r = rdLoc(a) - var f = e.sons[1].sym - let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses) + var f = e[1].sym + let ty = skipTypes(a.t, abstractInstOwned + tyUserTypeClasses) if ty.kind == tyTuple: # we found a unique tuple type which lacks field information # so we use Field$i - addf(r, ".Field$1", [rope(f.position)]) + r.addf(".Field$1", [rope(f.position)]) putIntoDest(p, d, e, r, a.storage) else: var rtyp: PType let field = lookupFieldAgain(p, ty, f, r, addr rtyp) if field.loc.r == nil and rtyp != nil: fillObjectFields(p.module, rtyp) if field.loc.r == nil: internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty)) - addf(r, ".$1", [field.loc.r]) + r.addf(".$1", [field.loc.r]) putIntoDest(p, d, e, r, a.storage) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = var test, u, v: TLoc - for i in countup(1, sonsLen(e) - 1): - var it = e.sons[i] + for i in 1.. lastOrd(p.config, ty)): linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)) #raiseIndexError2($1, $2);$n", - rdCharLoc(b), intLiteral(lastOrd(p.config, ty))) + [rdCharLoc(b), intLiteral(lastOrd(p.config, ty))]) else: linefmt(p, cpsStmts, "if ($1 < $2 || $1 > $3) #raiseIndexError3($1, $2, $3);$n", - rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))) + [rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))]) else: let idx = getOrdValue(y) if idx < firstOrd(p.config, ty) or idx > lastOrd(p.config, ty): localError(p.config, x.info, formatErrorIndexBound(idx, firstOrd(p.config, ty), lastOrd(p.config, ty))) d.inheritLocation(a) putIntoDest(p, d, n, - ropecg(p.module, "$1[($2)- $3]", rdLoc(a), rdCharLoc(b), first), a.storage) + ropecg(p.module, "$1[($2)- $3]", [rdLoc(a), rdCharLoc(b), first]), a.storage) proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -885,7 +883,7 @@ proc genCStringElem(p: BProc, n, x, y: PNode, d: var TLoc) = initLocExpr(p, y, b) inheritLocation(d, a) putIntoDest(p, d, n, - ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) + ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = let ty = skipTypes(arr.t, abstractVarRange) @@ -894,18 +892,18 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & "((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))) #raiseIndexError();$n", - rdLoc(a), rdLoc(b), rdLoc(arr)) + [rdLoc(a), rdLoc(b), rdLoc(arr)]) of tyArray: let first = intLiteral(firstOrd(p.config, ty)) linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & "($2-$1 < -1 || $1 < $3 || $1 > $4 || $2 < $3 || $2 > $4)) #raiseIndexError();$n", - rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))) + [rdCharLoc(a), rdCharLoc(b), first, intLiteral(lastOrd(p.config, ty))]) of tySequence, tyString: linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & "((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)) #raiseIndexError();$n", - rdLoc(a), rdLoc(b), lenExpr(p, arr)) + [rdLoc(a), rdLoc(b), lenExpr(p, arr)]) else: discard proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = @@ -914,10 +912,10 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = initLocExpr(p, y, b) # emit range check: if optBoundsCheck in p.options: linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)) #raiseIndexError2($1,$2Len_0-1);$n", - rdLoc(b), rdLoc(a)) # BUGFIX: ``>=`` and not ``>``! + [rdLoc(b), rdLoc(a)]) # BUGFIX: ``>=`` and not ``>``! inheritLocation(d, a) putIntoDest(p, d, n, - ropecg(p.module, "$1[$2]", rdLoc(a), rdCharLoc(b)), a.storage) + ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -930,28 +928,50 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options): linefmt(p, cpsStmts, "if ((NU)($1) > (NU)$2) #raiseIndexError2($1,$2);$n", - rdLoc(b), lenExpr(p, a)) + [rdLoc(b), lenExpr(p, a)]) else: linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)$2) #raiseIndexError2($1,$2-1);$n", - rdLoc(b), lenExpr(p, a)) + [rdLoc(b), lenExpr(p, a)]) if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: - a.r = ropecg(p.module, "(*$1)", a.r) + a.r = ropecg(p.module, "(*$1)", [a.r]) + + if lfPrepareForMutation in d.flags and ty.kind == tyString and + optSeqDestructors in p.config.globalOptions: + linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) putIntoDest(p, d, n, - ropecg(p.module, "$1$3[$2]", rdLoc(a), rdCharLoc(b), dataField(p)), a.storage) + ropecg(p.module, "$1$3[$2]", [rdLoc(a), rdCharLoc(b), dataField(p)]), a.storage) proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = - var ty = skipTypes(n.sons[0].typ, abstractVarRange + tyUserTypeClasses) + var ty = skipTypes(n[0].typ, abstractVarRange + tyUserTypeClasses) if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind - of tyUncheckedArray: genUncheckedArrayElem(p, n, n.sons[0], n.sons[1], d) - of tyArray: genArrayElem(p, n, n.sons[0], n.sons[1], d) - of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, n.sons[0], n.sons[1], d) - of tySequence, tyString: genSeqElem(p, n, n.sons[0], n.sons[1], d) - of tyCString: genCStringElem(p, n, n.sons[0], n.sons[1], d) + of tyUncheckedArray: genUncheckedArrayElem(p, n, n[0], n[1], d) + of tyArray: genArrayElem(p, n, n[0], n[1], d) + of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, n[0], n[1], d) + of tySequence, tyString: genSeqElem(p, n, n[0], n[1], d) + of tyCString: genCStringElem(p, n, n[0], n[1], d) of tyTuple: genTupleElem(p, n, d) else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') + discard getTypeDesc(p.module, n.typ) + +proc isSimpleExpr(n: PNode): bool = + # calls all the way down --> can stay expression based + case n.kind + of nkCallKinds, nkDotExpr, nkPar, nkTupleConstr, + nkObjConstr, nkBracket, nkCurly, nkHiddenDeref, nkDerefExpr, nkHiddenAddr, + nkHiddenStdConv, nkHiddenSubConv, nkConv, nkAddr: + for c in n: + if not isSimpleExpr(c): return false + result = true + of nkStmtListExpr: + for i in 0..data, $1->len)", [rdLoc(a)])) + args.add(ropecg(p.module, ", Genode::Cstring($1->data, $1->len)", [rdLoc(a)])) p.module.includeHeader("") p.module.includeHeader("") - linefmt(p, cpsStmts, """Genode::log(""$1);$n""", args) + linefmt(p, cpsStmts, """Genode::log(""$1);$n""", [args]) else: if n.len == 0: - linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", n.len.rope) + linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", [n.len]) else: var a: TLoc initLocExpr(p, n, a) - linefmt(p, cpsStmts, "#echoBinSafe($1, $2);$n", a.rdLoc, n.len.rope) + linefmt(p, cpsStmts, "#echoBinSafe($1, $2);$n", [a.rdLoc, n.len]) when false: p.module.includeHeader("") linefmt(p, cpsStmts, "printf($1$2);$n", - makeCString(repeat("%s", n.len) & "\L"), args) - linefmt(p, cpsStmts, "fflush(stdout);$n") + makeCString(repeat("%s", n.len) & "\L"), [args]) + linefmt(p, cpsStmts, "fflush(stdout);$n", []) proc gcUsage(conf: ConfigRef; n: PNode) = if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree) proc strLoc(p: BProc; d: TLoc): Rope = - if p.config.selectedGc == gcDestructors: + if optSeqDestructors in p.config.globalOptions: result = byRefLoc(p, d) else: result = rdLoc(d) @@ -1054,21 +1091,21 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = var L = 0 var appends: Rope = nil var lens: Rope = nil - for i in countup(0, sonsLen(e) - 2): + for i in 0.. # seq = (typeof seq) incrSeq(&seq->Sup, sizeof(x)); # seq->data[seq->len-1] = x; - let seqAppendPattern = if not p.module.compileToCpp: - "($2) #incrSeqV3((TGenericSeq*)($1), $3)" - else: - "($2) #incrSeqV3($1, $3)" var a, b, dest, tmpL, call: TLoc - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) - let seqType = skipTypes(e.sons[1].typ, {tyVar}) + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) + let seqType = skipTypes(e[1].typ, {tyVar}) initLoc(call, locCall, e, OnHeap) - call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), - getTypeDesc(p.module, e.sons[1].typ), - genTypeInfo(p.module, seqType, e.info)]) + if not p.module.compileToCpp: + const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)" + call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), + getTypeDesc(p.module, e[1].typ), + genTypeInfo(p.module, seqType, e.info)]) + else: + const seqAppendPattern = "($2) #incrSeqV3($1, $3)" + call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), + getTypeDesc(p.module, e[1].typ), + genTypeInfo(p.module, seqType, e.info)]) # emit the write barrier if required, but we can always move here, so # use 'genRefAssign' for the seq. genRefAssign(p, a, call) #if bt != b.t: # echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t) - initLoc(dest, locExpr, e.sons[2], OnHeap) + initLoc(dest, locExpr, e[2], OnHeap) getIntTemp(p, tmpL) - lineCg(p, cpsStmts, "$1 = $2->$3++;$n", tmpL.r, rdLoc(a), lenField(p)) - dest.r = ropecg(p.module, "$1$3[$2]", rdLoc(a), tmpL.r, dataField(p)) + lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)]) + dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)]) genAssignment(p, dest, b, {needToCopy}) gcUsage(p.config, e) proc genReset(p: BProc, n: PNode) = var a: TLoc - initLocExpr(p, n.sons[1], a) + initLocExpr(p, n[1], a) linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", - addrLoc(p.config, a), - genTypeInfo(p.module, skipTypes(a.t, {tyVar}), n.info)) + [addrLoc(p.config, a), + genTypeInfo(p.module, skipTypes(a.t, {tyVar}), n.info)]) + +proc genDefault(p: BProc; n: PNode; d: var TLoc) = + if d.k == locNone: getTemp(p, n.typ, d, needsInit=true) + else: resetLoc(p, d) -proc rawGenNew(p: BProc, a: TLoc, sizeExpr: Rope) = +proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope) = var sizeExpr = sizeExpr let typ = a.t var b: TLoc initLoc(b, locExpr, a.lode, OnHeap) - let refType = typ.skipTypes(abstractInst) + let refType = typ.skipTypes(abstractInstOwned) assert refType.kind == tyRef let bt = refType.lastSon if sizeExpr.isNil: - sizeExpr = "sizeof($1)" % - [getTypeDesc(p.module, bt)] - - let ti = genTypeInfo(p.module, typ, a.lode.info) - if bt.destructor != nil: - # the prototype of a destructor is ``=destroy(x: var T)`` and that of a - # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling - # convention at least: - if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccDefault: - localError(p.module.config, a.lode.info, - "the destructor that is turned into a finalizer needs " & - "to have the 'nimcall' calling convention") - var f: TLoc - initLocExpr(p, newSymNode(bt.destructor), f) - addf(p.module.s[cfsTypeInit3], "$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) - - let args = [getTypeDesc(p.module, typ), ti, sizeExpr] - if a.storage == OnHeap and usesWriteBarrier(p.config): - if canFormAcycle(a.t): - linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", a.rdLoc) - else: - linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", a.rdLoc) - if p.config.selectedGC == gcGo: - # newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to - # implement the write barrier - b.r = ropecg(p.module, "($1) #newObj($2, $3)", args) - linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(p.config, a), b.rdLoc) - else: - # use newObjRC1 as an optimization - b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", args) - linefmt(p, cpsStmts, "$1 = $2;$n", a.rdLoc, b.rdLoc) + sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)] + + if optTinyRtti in p.config.globalOptions: + b.r = ropecg(p.module, "($1) #nimNewObj($2)", + [getTypeDesc(p.module, typ), sizeExpr]) + genAssignment(p, a, b, {}) else: - b.r = ropecg(p.module, "($1) #newObj($2, $3)", args) - genAssignment(p, a, b, {}) # set the object type: - genObjectInit(p, cpsStmts, bt, a, false) + let ti = genTypeInfo(p.module, typ, a.lode.info) + if bt.destructor != nil and not isTrivialProc(bt.destructor): + # the prototype of a destructor is ``=destroy(x: var T)`` and that of a + # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling + # convention at least: + if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccDefault: + localError(p.module.config, a.lode.info, + "the destructor that is turned into a finalizer needs " & + "to have the 'nimcall' calling convention") + var f: TLoc + initLocExpr(p, newSymNode(bt.destructor), f) + p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) + + if a.storage == OnHeap and usesWriteBarrier(p.config): + if canFormAcycle(a.t): + linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [a.rdLoc]) + else: + linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [a.rdLoc]) + if p.config.selectedGC == gcGo: + # newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to + # implement the write barrier + b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) + linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", + [addrLoc(p.config, a), b.rdLoc]) + else: + # use newObjRC1 as an optimization + b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) + linefmt(p, cpsStmts, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) + else: + b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr]) + genAssignment(p, a, b, {}) + # set the object type: + genObjectInit(p, cpsStmts, bt, a, constructRefObj) proc genNew(p: BProc, e: PNode) = var a: TLoc - initLocExpr(p, e.sons[1], a) + initLocExpr(p, e[1], a) # 'genNew' also handles 'unsafeNew': if e.len == 3: var se: TLoc - initLocExpr(p, e.sons[2], se) + initLocExpr(p, e[2], se) rawGenNew(p, a, se.rdLoc) else: rawGenNew(p, a, nil) @@ -1212,39 +1261,40 @@ proc genNew(p: BProc, e: PNode) = proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = let seqtype = skipTypes(dest.t, abstractVarRange) - let args = [getTypeDesc(p.module, seqtype), - genTypeInfo(p.module, seqtype, dest.lode.info), length] var call: TLoc initLoc(call, locExpr, dest.lode, OnHeap) if dest.storage == OnHeap and usesWriteBarrier(p.config): if canFormAcycle(dest.t): - linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", dest.rdLoc) + linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [dest.rdLoc]) else: - linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", dest.rdLoc) + linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [dest.rdLoc]) if not lenIsZero: if p.config.selectedGC == gcGo: # we need the write barrier - call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args) - linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", addrLoc(p.config, dest), call.rdLoc) + call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), + genTypeInfo(p.module, seqtype, dest.lode.info), length]) + linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), call.rdLoc]) else: - call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", args) - linefmt(p, cpsStmts, "$1 = $2;$n", dest.rdLoc, call.rdLoc) + call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype), + genTypeInfo(p.module, seqtype, dest.lode.info), length]) + linefmt(p, cpsStmts, "$1 = $2;$n", [dest.rdLoc, call.rdLoc]) else: if lenIsZero: call.r = rope"NIM_NIL" else: - call.r = ropecg(p.module, "($1) #newSeq($2, $3)", args) + call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), + genTypeInfo(p.module, seqtype, dest.lode.info), length]) genAssignment(p, dest, call, {}) proc genNewSeq(p: BProc, e: PNode) = var a, b: TLoc - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) - if p.config.selectedGC == gcDestructors: - let seqtype = skipTypes(e.sons[1].typ, abstractVarRange) + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) + if optSeqDestructors in p.config.globalOptions: + let seqtype = skipTypes(e[1].typ, abstractVarRange) linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", - a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon), - getSeqPayloadType(p.module, seqtype)) + [a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon), + getSeqPayloadType(p.module, seqtype)]) else: let lenIsZero = optNilSeqs notin p.options and e[2].kind == nkIntLit and e[2].intVal == 0 @@ -1254,25 +1304,33 @@ proc genNewSeq(p: BProc, e: PNode) = proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) var a: TLoc - initLocExpr(p, e.sons[1], a) - putIntoDest(p, d, e, ropecg(p.module, - "($1)#nimNewSeqOfCap($2, $3)", [ - getTypeDesc(p.module, seqtype), - genTypeInfo(p.module, seqtype, e.info), a.rdLoc])) - gcUsage(p.config, e) + initLocExpr(p, e[1], a) + if optSeqDestructors in p.config.globalOptions: + if d.k == locNone: getTemp(p, e.typ, d, needsInit=false) + linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", + [d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon), + getSeqPayloadType(p.module, seqtype)]) + else: + putIntoDest(p, d, e, ropecg(p.module, + "($1)#nimNewSeqOfCap($2, $3)", [ + getTypeDesc(p.module, seqtype), + genTypeInfo(p.module, seqtype, e.info), a.rdLoc])) + gcUsage(p.config, e) + +proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) = + let t = n.typ + discard getTypeDesc(p.module, t) # so that any fields are initialized + let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) + fillLoc(d, locData, n, p.module.tmpBase & rope(id), OnStatic) + if id == p.module.labels: + # expression not found in the cache: + inc(p.module.labels) + p.module.s[cfsData].addf("NIM_CONST $1 $2 = $3;$n", + [getTypeDesc(p.module, t), d.r, genBracedInit(p, n, isConst = true)]) -proc genConstExpr(p: BProc, n: PNode): Rope proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr: - let t = n.typ - discard getTypeDesc(p.module, t) # so that any fields are initialized - let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) - fillLoc(d, locData, n, p.module.tmpBase & rope(id), OnStatic) - if id == p.module.labels: - # expression not found in the cache: - inc(p.module.labels) - addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, t), d.r, genConstExpr(p, n)]) + rawConstExpr(p, n, d) result = true else: result = false @@ -1283,7 +1341,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = # we skip this step here: if not p.module.compileToCpp: if handleConstExpr(p, e, d): return - var t = e.typ.skipTypes(abstractInst) + var t = e.typ.skipTypes(abstractInstOwned) let isRef = t.kind == tyRef # check if we need to construct the object in a temporary @@ -1299,7 +1357,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = r = rdLoc(tmp) if isRef: rawGenNew(p, tmp, nil) - t = t.lastSon.skipTypes(abstractInst) + t = t.lastSon.skipTypes(abstractInstOwned) r = "(*$1)" % [r] gcUsage(p.config, e) else: @@ -1309,25 +1367,25 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = r = rdLoc(d) discard getTypeDesc(p.module, t) let ty = getUniqueType(t) - for i in 1 ..< e.len: - let it = e.sons[i] + for i in 1..finalizer = (void*)$2;$n", [ti, rdLoc(f)]) + p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ getTypeDesc(p.module, refType), ti, getTypeDesc(p.module, skipTypes(refType.lastSon, abstractRange))]) genAssignment(p, a, b, {}) # set the object type: bt = skipTypes(refType.lastSon, abstractRange) - genObjectInit(p, cpsStmts, bt, a, false) + genObjectInit(p, cpsStmts, bt, a, constructRefObj) gcUsage(p.config, e) proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope = - # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we - # have to call it here first: - let ti = genTypeInfo(p.module, dest, info) - if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and - tfObjHasKids notin dest.flags): - result = "$1.m_type == $2" % [a, ti] - else: - discard cgsym(p.module, "TNimType") - inc p.module.labels - let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope - addf(p.module.s[cfsVars], "static TNimType* $#[2];$n", [cache]) - result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", a, ti, cache) - when false: - # former version: + if optTinyRtti in p.config.globalOptions: result = ropecg(p.module, "#isObj($1.m_type, $2)", - a, genTypeInfo(p.module, dest, info)) + [a, genTypeInfo2Name(p.module, dest)]) + else: + # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we + # have to call it here first: + let ti = genTypeInfo(p.module, dest, info) + if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and + tfObjHasKids notin dest.flags): + result = "$1.m_type == $2" % [a, ti] + else: + discard cgsym(p.module, "TNimType") + inc p.module.labels + let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope + p.module.s[cfsVars].addf("static TNimType* $#[2];$n", [cache]) + result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache]) + when false: + # former version: + result = ropecg(p.module, "#isObj($1.m_type, $2)", + [a, genTypeInfo(p.module, dest, info)]) proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = var a: TLoc @@ -1453,33 +1515,35 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = var dest = skipTypes(typ, typedescPtrs) var r = rdLoc(a) var nilCheck: Rope = nil - var t = skipTypes(a.t, abstractInst) + var t = skipTypes(a.t, abstractInstOwned) while t.kind in {tyVar, tyLent, tyPtr, tyRef}: if t.kind notin {tyVar, tyLent}: nilCheck = r if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: - r = ropecg(p.module, "(*$1)", r) - t = skipTypes(t.lastSon, typedescInst) + r = ropecg(p.module, "(*$1)", [r]) + t = skipTypes(t.lastSon, typedescInst+{tyOwned}) discard getTypeDesc(p.module, t) if not p.module.compileToCpp: - while t.kind == tyObject and t.sons[0] != nil: - add(r, ~".Sup") - t = skipTypes(t.sons[0], skipPtrs) + while t.kind == tyObject and t[0] != nil: + r.add(~".Sup") + t = skipTypes(t[0], skipPtrs) if isObjLackingTypeField(t): globalError(p.config, x.info, "no 'of' operator available for pure objects") if nilCheck != nil: - r = ropecg(p.module, "(($1) && ($2))", nilCheck, genOfHelper(p, dest, r, x.info)) + r = ropecg(p.module, "(($1) && ($2))", [nilCheck, genOfHelper(p, dest, r, x.info)]) else: - r = ropecg(p.module, "($1)", genOfHelper(p, dest, r, x.info)) + r = ropecg(p.module, "($1)", [genOfHelper(p, dest, r, x.info)]) putIntoDest(p, d, x, r, a.storage) proc genOf(p: BProc, n: PNode, d: var TLoc) = - genOf(p, n.sons[1], n.sons[2].typ, d) + genOf(p, n[1], n[2].typ, d) proc genRepr(p: BProc, e: PNode, d: var TLoc) = + if optTinyRtti in p.config.globalOptions: + localError(p.config, e.info, "'repr' is not available for --newruntime") var a: TLoc - initLocExpr(p, e.sons[1], a) - var t = skipTypes(e.sons[1].typ, abstractVarRange) + initLocExpr(p, e[1], a) + var t = skipTypes(e[1].typ, abstractVarRange) case t.kind of tyInt..tyInt64, tyUInt..tyUInt64: putIntoDest(p, d, e, @@ -1510,7 +1574,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = of tyArray: putIntoDest(p, b, e, "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage) - else: internalError(p.config, e.sons[0].info, "genRepr()") + else: internalError(p.config, e[0].info, "genRepr()") putIntoDest(p, d, e, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), genTypeInfo(p.module, elemType(t), e.info)]), a.storage) @@ -1527,20 +1591,22 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = gcUsage(p.config, e) proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = - let t = e.sons[1].typ + discard cgsym(p.module, "TNimType") + let t = e[1].typ putIntoDest(p, d, e, genTypeInfo(p.module, t, e.info)) -proc genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = +template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = var a: TLoc - initLocExpr(p, n.sons[1], a) + initLocExpr(p, n[1], a) a.r = ropecg(p.module, frmt, [rdLoc(a)]) + a.flags = a.flags - {lfIndirect} # this flag should not be propagated here (not just for HCR) if d.k == locNone: getTemp(p, n.typ, d) genAssignment(p, d, a, {}) gcUsage(p.config, n) proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = - var a = e.sons[1] - if a.kind == nkHiddenAddr: a = a.sons[0] + var a = e[1] + if a.kind == nkHiddenAddr: a = a[0] var typ = skipTypes(a.typ, abstractVar + tyUserTypeClasses) case typ.kind of tyOpenArray, tyVarargs: @@ -1562,7 +1628,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") of tyString: var a: TLoc - initLocExpr(p, e.sons[1], a) + initLocExpr(p, e[1], a) var x = lenExpr(p, a) if op == mHigh: x = "($1-1)" % [x] putIntoDest(p, d, e, x) @@ -1573,7 +1639,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = getIntTemp(p, tmp) var x = lenExpr(p, a) if op == mHigh: x = "($1-1)" % [x] - lineCg(p, cpsStmts, "$1 = $2;$n", tmp.r, x) + lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.r, x]) putIntoDest(p, d, e, tmp.r) of tyArray: # YYY: length(sideeffect) is optimized away incorrectly? @@ -1586,41 +1652,49 @@ proc makePtrType(baseType: PType): PType = addSonSkipIntLit(result, baseType) proc makeAddr(n: PNode): PNode = - result = newTree(nkHiddenAddr, n) - result.typ = makePtrType(n.typ) + if n.kind == nkHiddenAddr: + result = n + else: + result = newTree(nkHiddenAddr, n) + result.typ = makePtrType(n.typ) proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = - if p.config.selectedGc == gcDestructors: - e.sons[1] = makeAddr(e[1]) + if optSeqDestructors in p.config.globalOptions: + e[1] = makeAddr(e[1]) genCall(p, e, d) return var a, b, call: TLoc assert(d.k == locNone) - var x = e.sons[1] + var x = e[1] if x.kind in {nkAddr, nkHiddenAddr}: x = x[0] initLocExpr(p, x, a) - initLocExpr(p, e.sons[2], b) - let t = skipTypes(e.sons[1].typ, {tyVar}) - let setLenPattern = if not p.module.compileToCpp: - "($3) #setLengthSeqV2(&($1)->Sup, $4, $2)" - else: - "($3) #setLengthSeqV2($1, $4, $2)" + initLocExpr(p, e[2], b) + let t = skipTypes(e[1].typ, {tyVar}) initLoc(call, locCall, e, OnHeap) - call.r = ropecg(p.module, setLenPattern, [ + if not p.module.compileToCpp: + const setLenPattern = "($3) #setLengthSeqV2(&($1)->Sup, $4, $2)" + call.r = ropecg(p.module, setLenPattern, [ rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)]) + + else: + const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)" + call.r = ropecg(p.module, setLenPattern, [ + rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), + genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)]) + genAssignment(p, a, call, {}) gcUsage(p.config, e) proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = - if p.config.selectedGc == gcDestructors: - binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n") + if optSeqDestructors in p.config.globalOptions: + binaryStmtAddr(p, e, d, "setLengthStrV2") else: var a, b, call: TLoc if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr") - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) initLoc(call, locCall, e, OnHeap) call.r = ropecg(p.module, "#setLengthStr($1, $2)", [ @@ -1634,9 +1708,9 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) = # a = b # b = temp var a, b, tmp: TLoc - getTemp(p, skipTypes(e.sons[1].typ, abstractVar), tmp) - initLocExpr(p, e.sons[1], a) # eval a - initLocExpr(p, e.sons[2], b) # eval b + getTemp(p, skipTypes(e[1].typ, abstractVar), tmp) + initLocExpr(p, e[1], a) # eval a + initLocExpr(p, e[2], b) # eval b genAssignment(p, tmp, a, {}) genAssignment(p, a, b, {}) genAssignment(p, b, tmp, {}) @@ -1659,88 +1733,98 @@ proc fewCmps(conf: ConfigRef; s: PNode): bool = elif elemType(s.typ).kind in {tyInt, tyInt16..tyInt64}: result = true # better not emit the set if int is basetype! else: - result = sonsLen(s) <= 8 # 8 seems to be a good value + result = s.len <= 8 # 8 seems to be a good value -proc binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) = +template binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) = putIntoDest(p, d, e, frmt % [rdLoc(a), rdSetElemLoc(p.config, b, a.t)]) proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = - case int(getSize(p.config, skipTypes(e.sons[1].typ, abstractVar))) + case int(getSize(p.config, skipTypes(e[1].typ, abstractVar))) of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&7U)))!=0)") of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&15U)))!=0)") of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&31U)))!=0)") of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)") else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)") -proc binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) = +template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a, b: TLoc assert(d.k == locNone) - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) lineF(p, cpsStmts, frmt, [rdLoc(a), rdSetElemLoc(p.config, b, a.t)]) proc genInOp(p: BProc, e: PNode, d: var TLoc) = var a, b, x, y: TLoc - if (e.sons[1].kind == nkCurly) and fewCmps(p.config, e.sons[1]): + if (e[1].kind == nkCurly) and fewCmps(p.config, e[1]): # a set constructor but not a constant set: # do not emit the set, but generate a bunch of comparisons; and if we do # so, we skip the unnecessary range check: This is a semantical extension # that code now relies on. :-/ XXX - let ea = if e.sons[2].kind in {nkChckRange, nkChckRange64}: - e.sons[2].sons[0] + let ea = if e[2].kind in {nkChckRange, nkChckRange64}: + e[2][0] else: - e.sons[2] + e[2] initLocExpr(p, ea, a) initLoc(b, locExpr, e, OnUnknown) - var length = sonsLen(e.sons[1]) - if length > 0: + if e[1].len > 0: b.r = rope("(") - for i in countup(0, length - 1): - let it = e.sons[1].sons[i] + for i in 0..= $2 && $1 <= $3", + initLocExpr(p, it[0], x) + initLocExpr(p, it[1], y) + b.r.addf("$1 >= $2 && $1 <= $3", [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)]) else: initLocExpr(p, it, x) - addf(b.r, "$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) - if i < length - 1: add(b.r, " || ") - add(b.r, ")") + b.r.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)]) + if i < e[1].len - 1: b.r.add(" || ") + b.r.add(")") else: # handle the case of an empty set b.r = rope("0") putIntoDest(p, d, e, b.r) else: - assert(e.sons[1].typ != nil) - assert(e.sons[2].typ != nil) - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) + assert(e[1].typ != nil) + assert(e[2].typ != nil) + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) genInExprAux(p, e, a, b, d) proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = const lookupOpr: array[mLeSet..mSymDiffSet, string] = [ "for ($1 = 0; $1 < $2; $1++) { $n" & - " $3 = (($4[$1] & ~ $5[$1]) == 0);$n" & - " if (!$3) break;}$n", "for ($1 = 0; $1 < $2; $1++) { $n" & - " $3 = (($4[$1] & ~ $5[$1]) == 0);$n" & " if (!$3) break;}$n" & - "if ($3) $3 = (#nimCmpMem($4, $5, $2) != 0);$n", - "&", "|", "& ~", "^"] + " $3 = (($4[$1] & ~ $5[$1]) == 0);$n" & + " if (!$3) break;}$n", + "for ($1 = 0; $1 < $2; $1++) { $n" & + " $3 = (($4[$1] & ~ $5[$1]) == 0);$n" & + " if (!$3) break;}$n" & + "if ($3) $3 = (#nimCmpMem($4, $5, $2) != 0);$n", + "&", + "|", + "& ~", + "^"] var a, b, i: TLoc - var setType = skipTypes(e.sons[1].typ, abstractVar) + var setType = skipTypes(e[1].typ, abstractVar) var size = int(getSize(p.config, setType)) case size of 1, 2, 4, 8: case op of mIncl: - var ts = "NU" & $(size * 8) - binaryStmtInExcl(p, e, d, - "$1 |= ((" & ts & ")1)<<(($2)%(sizeof(" & ts & ")*8));$n") + case size + of 1: binaryStmtInExcl(p, e, d, "$1 |= ((NU8)1)<<(($2) & 7);$n") + of 2: binaryStmtInExcl(p, e, d, "$1 |= ((NU16)1)<<(($2) & 15);$n") + of 4: binaryStmtInExcl(p, e, d, "$1 |= ((NU32)1)<<(($2) & 31);$n") + of 8: binaryStmtInExcl(p, e, d, "$1 |= ((NU64)1)<<(($2) & 63);$n") + else: assert(false, $size) of mExcl: - var ts = "NU" & $(size * 8) - binaryStmtInExcl(p, e, d, "$1 &= ~(((" & ts & ")1) << (($2) % (sizeof(" & - ts & ")*8)));$n") + case size + of 1: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU8)1) << (($2) & 7));$n") + of 2: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU16)1) << (($2) & 15));$n") + of 4: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU32)1) << (($2) & 31));$n") + of 8: binaryStmtInExcl(p, e, d, "$1 &= ~(((NU64)1) << (($2) & 63));$n") + else: assert(false, $size) of mCard: if size <= 4: unaryExprChar(p, e, d, "#countBits32($1)") else: unaryExprChar(p, e, d, "#countBits64($1)") @@ -1758,21 +1842,33 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = case op of mIncl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] |=(1U<<($2&7U));$n") of mExcl: binaryStmtInExcl(p, e, d, "$1[(NU)($2)>>3] &= ~(1U<<($2&7U));$n") - of mCard: unaryExprChar(p, e, d, "#cardSet($1, " & $size & ')') + of mCard: + var a: TLoc + initLocExpr(p, e[1], a) + putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [rdCharLoc(a), size])) of mLtSet, mLeSet: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) # our counter - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) - if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyBool), d) - linefmt(p, cpsStmts, lookupOpr[op], - [rdLoc(i), rope(size), rdLoc(d), rdLoc(a), rdLoc(b)]) + getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) + if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyBool), d) + if op == mLtSet: + linefmt(p, cpsStmts, lookupOpr[mLtSet], + [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)]) + else: + linefmt(p, cpsStmts, lookupOpr[mLeSet], + [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)]) of mEqSet: - binaryExprChar(p, e, d, "(#nimCmpMem($1, $2, " & $(size) & ")==0)") + var a, b: TLoc + assert(e[1].typ != nil) + assert(e[2].typ != nil) + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) + putIntoDest(p, d, e, ropecg(p.module, "(#nimCmpMem($1, $2, $3)==0)", [a.rdCharLoc, b.rdCharLoc, size])) of mMulSet, mPlusSet, mMinusSet, mSymDiffSet: # we inline the simple for loop for better code generation: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), i) # our counter - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) + getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) if d.k == locNone: getTemp(p, setType, d) lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) $n" & @@ -1787,24 +1883,27 @@ proc genOrd(p: BProc, e: PNode, d: var TLoc) = proc genSomeCast(p: BProc, e: PNode, d: var TLoc) = const - ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs} + ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs, tyUncheckedArray} # we use whatever C gives us. Except if we have a value-type, we need to go # through its address: var a: TLoc - initLocExpr(p, e.sons[1], a) - let etyp = skipTypes(e.typ, abstractRange) + initLocExpr(p, e[1], a) + let etyp = skipTypes(e.typ, abstractRange+{tyOwned}) + let srcTyp = skipTypes(e[1].typ, abstractRange) if etyp.kind in ValueTypes and lfIndirect notin a.flags: putIntoDest(p, d, e, "(*($1*) ($2))" % [getTypeDesc(p.module, e.typ), addrLoc(p.config, a)], a.storage) - elif etyp.kind == tyProc and etyp.callConv == ccClosure: + elif etyp.kind == tyProc and etyp.callConv == ccClosure and srcTyp.callConv != ccClosure: putIntoDest(p, d, e, "(($1) ($2))" % [getClosureType(p.module, etyp, clHalfWithEnv), rdCharLoc(a)], a.storage) else: - let srcTyp = skipTypes(e.sons[1].typ, abstractRange) # C++ does not like direct casts from pointer to shorter integral types if srcTyp.kind in {tyPtr, tyPointer} and etyp.kind in IntegralTypes: putIntoDest(p, d, e, "(($1) (ptrdiff_t) ($2))" % [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) + elif optSeqDestructors in p.config.globalOptions and etyp.kind in {tySequence, tyString}: + putIntoDest(p, d, e, "(*($1*) (&$2))" % + [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) else: putIntoDest(p, d, e, "(($1) ($2))" % [getTypeDesc(p.module, e.typ), rdCharLoc(a)], a.storage) @@ -1813,7 +1912,7 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = const ValueTypes = {tyFloat..tyFloat128, tyTuple, tyObject, tyArray} let destt = skipTypes(e.typ, abstractRange) - srct = skipTypes(e.sons[1].typ, abstractRange) + srct = skipTypes(e[1].typ, abstractRange) if destt.kind in ValueTypes or srct.kind in ValueTypes: # 'cast' and some float type involved? --> use a union. inc(p.labels) @@ -1821,12 +1920,12 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = var tmp: TLoc tmp.r = "LOC$1.source" % [lbl] linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n", - getTypeDesc(p.module, e.sons[1].typ), getTypeDesc(p.module, e.typ), lbl) + [getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl]) tmp.k = locExpr tmp.lode = lodeTyp srct tmp.storage = OnStack tmp.flags = {} - expr(p, e.sons[1], tmp) + expr(p, e[1], tmp) putIntoDest(p, d, e, "LOC$#.dest" % [lbl], tmp.storage) else: # I prefer the shorter cast version for pointer types -> generate less @@ -1836,29 +1935,29 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) = proc genRangeChck(p: BProc, n: PNode, d: var TLoc, magic: string) = var a: TLoc var dest = skipTypes(n.typ, abstractVar) - # range checks for unsigned turned out to be buggy and annoying: - if optRangeCheck notin p.options or dest.skipTypes({tyRange}).kind in - {tyUInt..tyUInt64}: - initLocExpr(p, n.sons[0], a) + if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and + checkUnsignedConversions notin p.config.legacyFeatures): + initLocExpr(p, n[0], a) putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage) else: - initLocExpr(p, n.sons[0], a) + let mm = if dest.kind in {tyUInt32, tyUInt64, tyUInt}: "chckRangeU" else: magic + initLocExpr(p, n[0], a) putIntoDest(p, d, lodeTyp dest, ropecg(p.module, "(($1)#$5($2, $3, $4))", [ getTypeDesc(p.module, dest), rdCharLoc(a), - genLiteral(p, n.sons[1], dest), genLiteral(p, n.sons[2], dest), - rope(magic)]), a.storage) + genLiteral(p, n[1], dest), genLiteral(p, n[2], dest), + mm]), a.storage) proc genConv(p: BProc, e: PNode, d: var TLoc) = let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink}) - if sameBackendType(destType, e.sons[1].typ): - expr(p, e.sons[1], d) + if sameBackendType(destType, e[1].typ): + expr(p, e[1], d) else: genSomeCast(p, e, d) proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc - initLocExpr(p, n.sons[0], a) + initLocExpr(p, n[0], a) putIntoDest(p, d, n, ropecg(p.module, "#nimToCStringConv($1)", [rdLoc(a)]), # "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc], @@ -1866,7 +1965,7 @@ proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) = proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = var a: TLoc - initLocExpr(p, n.sons[0], a) + initLocExpr(p, n[0], a) putIntoDest(p, d, n, ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]), a.storage) @@ -1874,16 +1973,16 @@ proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) = proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = var x: TLoc - var a = e.sons[1] - var b = e.sons[2] + var a = e[1] + var b = e[2] if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "": - initLocExpr(p, e.sons[2], x) + initLocExpr(p, e[2], x) putIntoDest(p, d, e, - ropecg(p.module, "($1 == 0)", lenExpr(p, x))) + ropecg(p.module, "($1 == 0)", [lenExpr(p, x)])) elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "": - initLocExpr(p, e.sons[1], x) + initLocExpr(p, e[1], x) putIntoDest(p, d, e, - ropecg(p.module, "($1 == 0)", lenExpr(p, x))) + ropecg(p.module, "($1 == 0)", [lenExpr(p, x)])) else: binaryExpr(p, e, d, "#eqStrings($1, $2)") @@ -1891,17 +1990,17 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) = if {optNaNCheck, optInfCheck} * p.options != {}: const opr: array[mAddF64..mDivF64, string] = ["+", "-", "*", "/"] var a, b: TLoc - assert(e.sons[1].typ != nil) - assert(e.sons[2].typ != nil) - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) + assert(e[1].typ != nil) + assert(e[2].typ != nil) + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) putIntoDest(p, d, e, ropecg(p.module, "(($4)($2) $1 ($4)($3))", - rope(opr[m]), rdLoc(a), rdLoc(b), - getSimpleTypeDesc(p.module, e[1].typ))) + [opr[m], rdLoc(a), rdLoc(b), + getSimpleTypeDesc(p.module, e[1].typ)])) if optNaNCheck in p.options: - linefmt(p, cpsStmts, "#nanCheck($1);$n", rdLoc(d)) + linefmt(p, cpsStmts, "#nanCheck($1);$n", [rdLoc(d)]) if optInfCheck in p.options: - linefmt(p, cpsStmts, "#infCheck($1);$n", rdLoc(d)) + linefmt(p, cpsStmts, "#infCheck($1);$n", [rdLoc(d)]) else: binaryArith(p, e, d, m) @@ -1913,19 +2012,100 @@ proc genWasMoved(p: BProc; n: PNode) = initLocExpr(p, n[1].skipAddr, a) resetLoc(p, a) #linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", - # addrLoc(p.config, a), getTypeDesc(p.module, a.t)) + # [addrLoc(p.config, a), getTypeDesc(p.module, a.t)]) proc genMove(p: BProc; n: PNode; d: var TLoc) = - if d.k == locNone: getTemp(p, n.typ, d) var a: TLoc initLocExpr(p, n[1].skipAddr, a) - genAssignment(p, d, a, {}) - resetLoc(p, a) + if n.len == 4: + # generated by liftdestructors: + var src: TLoc + initLocExpr(p, n[2], src) + linefmt(p, cpsStmts, "if ($1.len && $1.p != $2.p) {", [rdLoc(a), rdLoc(src)]) + genStmts(p, n[3]) + linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)]) + else: + if d.k == locNone: getTemp(p, n.typ, d) + genAssignment(p, d, a, {}) + resetLoc(p, a) + +proc genDestroy(p: BProc; n: PNode) = + if optSeqDestructors in p.config.globalOptions: + let arg = n[1].skipAddr + let t = arg.typ.skipTypes(abstractInst) + case t.kind + of tyString: + var a: TLoc + initLocExpr(p, arg, a) + linefmt(p, cpsStmts, "if ($1.p && $1.p->allocator) {$n" & + " $1.p->allocator->dealloc($1.p->allocator, $1.p, $1.p->cap + 1 + sizeof(NI) + sizeof(void*));$n" & + " $1.p = NIM_NIL; }$n", + [rdLoc(a)]) + of tySequence: + var a: TLoc + initLocExpr(p, arg, a) + linefmt(p, cpsStmts, "if ($1.p && $1.p->allocator) {$n" & + " $1.p->allocator->dealloc($1.p->allocator, $1.p, ($1.p->cap * sizeof($2)) + sizeof(NI) + sizeof(void*));$n" & + " $1.p = NIM_NIL; }$n", + [rdLoc(a), getTypeDesc(p.module, t.lastSon)]) + else: discard "nothing to do" + else: + let t = n[1].typ.skipTypes(abstractVar) + if t.destructor != nil and t.destructor.ast[bodyPos].len != 0: + internalError(p.config, n.info, "destructor turned out to be not trivial") + discard "ignore calls to the default destructor" + +proc genDispose(p: BProc; n: PNode) = + when false: + let elemType = n[1].typ.skipTypes(abstractVar).lastSon + + var a: TLoc + initLocExpr(p, n[1].skipAddr, a) + + if isFinal(elemType): + if elemType.destructor != nil: + var destroyCall = newNodeI(nkCall, n.info) + genStmts(p, destroyCall) + lineCg(p, cpsStmts, ["#nimRawDispose($#)", rdLoc(a)]) + else: + # ``nimRawDisposeVirtual`` calls the ``finalizer`` which is the same as the + # destructor, but it uses the runtime type. Afterwards the memory is freed: + lineCg(p, cpsStmts, ["#nimDestroyAndDispose($#)", rdLoc(a)]) + +proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) = + const ToStringProcSlot = -4 + let t = e[1].typ.skipTypes(abstractInst+{tyRange}) + var toStrProc: PSym = nil + for idx, p in items(t.methods): + if idx == ToStringProcSlot: + toStrProc = p + break + if toStrProc == nil: + toStrProc = genEnumToStrProc(t, e.info, p.module.g.graph) + t.methods.add((ToStringProcSlot, toStrProc)) + var n = copyTree(e) + n[0] = newSymNode(toStrProc) + expr(p, n, d) + +proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope): Rope = + result = rdLoc(a) + var t = skipTypes(a.t, abstractInst) + while t.kind in {tyVar, tyLent, tyPtr, tyRef}: + if t.kind notin {tyVar, tyLent}: nilCheck = result + if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: + result = "(*$1)" % [result] + t = skipTypes(t.lastSon, abstractInst) + discard getTypeDesc(p.module, t) + if not p.module.compileToCpp: + while t.kind == tyObject and t[0] != nil: + result.add(".Sup") + t = skipTypes(t[0], skipPtrs) + result.add ".m_type" proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = case op of mOr, mAnd: genAndOr(p, e, d, op) - of mNot..mToBiggestInt: unaryArith(p, e, d, op) + of mNot..mUnaryMinusF64: unaryArith(p, e, d, op) of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op) of mAddF64..mDivF64: binaryFloatArith(p, e, d, op) of mShrI..mXor: binaryArith(p, e, d, op) @@ -1938,42 +2118,43 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if optOverflowCheck notin p.options: unaryExpr(p, e, d, "($1 - 1)") else: unaryExpr(p, e, d, "#subInt($1, 1)") of mInc, mDec: - const opr: array[mInc..mDec, string] = ["$1 += $2;$n", "$1 -= $2;$n"] - const fun64: array[mInc..mDec, string] = ["$# = #addInt64($#, $#);$n", - "$# = #subInt64($#, $#);$n"] - const fun: array[mInc..mDec, string] = ["$# = #addInt($#, $#);$n", - "$# = #subInt($#, $#);$n"] - let underlying = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyRange}) + const opr: array[mInc..mDec, string] = ["+=", "-="] + const fun64: array[mInc..mDec, string] = ["addInt64", + "subInt64"] + const fun: array[mInc..mDec, string] = ["addInt", + "subInt"] + let underlying = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyRange}) if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}: binaryStmt(p, e, d, opr[op]) else: var a, b: TLoc - assert(e.sons[1].typ != nil) - assert(e.sons[2].typ != nil) - initLocExpr(p, e.sons[1], a) - initLocExpr(p, e.sons[2], b) + assert(e[1].typ != nil) + assert(e[2].typ != nil) + initLocExpr(p, e[1], a) + initLocExpr(p, e[2], b) - let ranged = skipTypes(e.sons[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent}) + let ranged = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent}) let res = binaryArithOverflowRaw(p, ranged, a, b, if underlying.kind == tyInt64: fun64[op] else: fun[op]) - putIntoDest(p, a, e.sons[1], "($#)($#)" % [ + + putIntoDest(p, a, e[1], "($#)($#)" % [ getTypeDesc(p.module, ranged), res]) of mConStrStr: genStrConcat(p, e, d) of mAppendStrCh: - if p.config.selectedGC == gcDestructors: - binaryStmtAddr(p, e, d, "#nimAddCharV1($1, $2);$n") + if optSeqDestructors in p.config.globalOptions: + binaryStmtAddr(p, e, d, "nimAddCharV1") else: var dest, b, call: TLoc initLoc(call, locCall, e, OnHeap) - initLocExpr(p, e.sons[1], dest) - initLocExpr(p, e.sons[2], b) + initLocExpr(p, e[1], dest) + initLocExpr(p, e[2], b) call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)]) genAssignment(p, dest, call, {}) of mAppendStrStr: genStrAppend(p, e, d) of mAppendSeqElem: - if p.config.selectedGc == gcDestructors: - e.sons[1] = makeAddr(e[1]) + if optSeqDestructors in p.config.globalOptions: + e[1] = makeAddr(e[1]) genCall(p, e, d) else: genSeqElemAppend(p, e, d) @@ -1987,21 +2168,48 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)") of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)") of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") - of mStrToStr: expr(p, e.sons[1], d) - of mEnumToStr: genRepr(p, e, d) + of mStrToStr, mUnown: expr(p, e[1], d) + of mEnumToStr: + if optTinyRtti in p.config.globalOptions: + genEnumToStr(p, e, d) + else: + genRepr(p, e, d) of mOf: genOf(p, e, d) of mNew: genNew(p, e) - of mNewFinalize: genNewFinalize(p, e) + of mNewFinalize: + if optTinyRtti in p.config.globalOptions: + var a: TLoc + initLocExpr(p, e[1], a) + rawGenNew(p, a, nil) + gcUsage(p.config, e) + else: + genNewFinalize(p, e) of mNewSeq: genNewSeq(p, e) of mNewSeqOfCap: genNewSeqOfCap(p, e, d) of mSizeOf: - let t = e.sons[1].typ.skipTypes({tyTypeDesc}) + let t = e[1].typ.skipTypes({tyTypeDesc}) putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)]) of mAlignOf: - let t = e.sons[1].typ.skipTypes({tyTypeDesc}) + let t = e[1].typ.skipTypes({tyTypeDesc}) if not p.module.compileToCpp: p.module.includeHeader("") putIntoDest(p, d, e, "((NI)alignof($1))" % [getTypeDesc(p.module, t)]) + of mOffsetOf: + var dotExpr: PNode + block findDotExpr: + if e[1].kind == nkDotExpr: + dotExpr = e[1] + elif e[1].kind == nkCheckedFieldExpr: + dotExpr = e[1][0] + else: + internalError(p.config, e.info, "unknown ast") + let t = dotExpr[0].typ.skipTypes({tyTypeDesc}) + let member = + if t.kind == tyTuple: + "Field" & rope(dotExpr[1].sym.position) + else: + rope(dotExpr[1].sym.name.s) + putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [getTypeDesc(p.module, t), member]) of mChr: genSomeCast(p, e, d) of mOrd: genOrd(p, e, d) of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray: @@ -2016,12 +2224,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var a, tmp: TLoc initLocExpr(p, e[1], a) getIntTemp(p, tmp) - var frmt: FormatStr if not p.module.compileToCpp: - frmt = "$1 = $2->Sup.len;$n" + lineCg(p, cpsStmts, "$1 = $2->Sup.len;$n", [tmp.r, rdLoc(a)]) else: - frmt = "$1 = $2->len;$n" - lineCg(p, cpsStmts, frmt, tmp.r, rdLoc(a)) + lineCg(p, cpsStmts, "$1 = $2->len;$n", [tmp.r, rdLoc(a)]) putIntoDest(p, d, e, tmp.r) of mGCref: unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n") of mGCunref: unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n") @@ -2033,18 +2239,42 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mCopyStr, mCopyStrLast: genCall(p, e, d) of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat: - var opr = e.sons[0].sym + var opr = e[0].sym + # Why would anyone want to set nodecl to one of these hardcoded magics? + # - not sure, and it wouldn't work if the symbol behind the magic isn't + # somehow forward-declared from some other usage, but it is *possible* if lfNoDecl notin opr.loc.flags: + let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.r) + # HACK: + # Explicitly add this proc as declared here so the cgsym call doesn't + # add a forward declaration - without this we could end up with the same + # 2 forward declarations. That happens because the magic symbol and the original + # one that shall be used have different ids (even though a call to one is + # actually a call to the other) so checking into m.declaredProtos with the 2 different ids doesn't work. + # Why would 2 identical forward declarations be a problem? + # - in the case of hot code-reloading we generate function pointers instead + # of forward declarations and in C++ it is an error to redefine a global + let wasDeclared = containsOrIncl(p.module.declaredProtos, prc.id) + # Make the function behind the magic get actually generated - this will + # not lead to a forward declaration! The genCall will lead to one. discard cgsym(p.module, $opr.loc.r) + # make sure we have pointer-initialising code for hot code reloading + if not wasDeclared and p.hcrOn: + p.module.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n", + [mangleDynLibProc(prc), getTypeDesc(p.module, prc.loc.t), getModuleDllPath(p.module, prc)]) genCall(p, e, d) + of mDefault: genDefault(p, e, d) of mReset: genReset(p, e) of mEcho: genEcho(p, e[1].skipConv) of mArrToSeq: genArrToSeq(p, e, d) of mNLen..mNError, mSlurp..mQuoteAst: - localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e.sons[0].sym.name.s)) + localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e[0].sym.name.s)) of mSpawn: - let n = lowerings.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil) - expr(p, n, d) + when defined(leanCompiler): + quit "compiler built without support for the 'spawn' statement" + else: + let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil) + expr(p, n, d) of mParallel: when defined(leanCompiler): quit "compiler built without support for the 'parallel' statement" @@ -2055,15 +2285,21 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = var a, b: TLoc let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] initLocExpr(p, x, a) - initLocExpr(p, e.sons[2], b) + initLocExpr(p, e[2], b) genDeepCopy(p, a, b) of mDotDot, mEqCString: genCall(p, e, d) of mWasMoved: genWasMoved(p, e) of mMove: genMove(p, e, d) - of mDestroy: discard "ignore calls to the default destructor" + of mDestroy: genDestroy(p, e) + of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0") + of mAccessTypeInfo: + var a: TLoc + var dummy: Rope + initLocExpr(p, e[1], a) + putIntoDest(p, d, e, rdMType(p, a, dummy)) of mSlice: localError(p.config, e.info, "invalid context for 'toOpenArray'; " & - " 'toOpenArray' is only valid within a call expression") + "'toOpenArray' is only valid within a call expression") else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind @@ -2086,9 +2322,9 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = [rdLoc(d), getTypeDesc(p.module, e.typ)]) for it in e.sons: if it.kind == nkRange: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), idx) # our counter - initLocExpr(p, it.sons[0], a) - initLocExpr(p, it.sons[1], b) + getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter + initLocExpr(p, it[0], a) + initLocExpr(p, it[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rdSetElemLoc(p.config, b, e.typ)]) @@ -2102,18 +2338,18 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = lineF(p, cpsStmts, "$1 = 0;$n", [rdLoc(d)]) for it in e.sons: if it.kind == nkRange: - getTemp(p, getSysType(p.module.g.graph, unknownLineInfo(), tyInt), idx) # our counter - initLocExpr(p, it.sons[0], a) - initLocExpr(p, it.sons[1], b) + getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter + initLocExpr(p, it[0], a) + initLocExpr(p, it[1], b) lineF(p, cpsStmts, "for ($1 = $3; $1 <= $4; $1++) $n" & - "$2 |=((" & ts & ")(1)<<(($1)%(sizeof(" & ts & ")*8)));$n", [ + "$2 |=(($5)(1)<<(($1)%(sizeof($5)*8)));$n", [ rdLoc(idx), rdLoc(d), rdSetElemLoc(p.config, a, e.typ), - rdSetElemLoc(p.config, b, e.typ)]) + rdSetElemLoc(p.config, b, e.typ), rope(ts)]) else: initLocExpr(p, it, a) lineF(p, cpsStmts, - "$1 |=((" & ts & ")(1)<<(($2)%(sizeof(" & ts & ")*8)));$n", - [rdLoc(d), rdSetElemLoc(p.config, a, e.typ)]) + "$1 |=(($3)(1)<<(($2)%(sizeof($3)*8)));$n", + [rdLoc(d), rdSetElemLoc(p.config, a, e.typ), rope(ts)]) proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = var rec: TLoc @@ -2121,16 +2357,17 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) = let t = n.typ discard getTypeDesc(p.module, t) # so that any fields are initialized if d.k == locNone: getTemp(p, t, d) - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] - if it.kind == nkExprColonExpr: it = it.sons[1] + for i in 0.. 0: exprOrStmt if frameName != nil: - add p.s(cpsStmts), deinitFrameNoDebug(p, frameName) + p.s(cpsStmts).add deinitFrameNoDebug(p, frameName) proc genStmtListExpr(p: BProc, n: PNode, d: var TLoc) = genStmtListExprImpl: - expr(p, n[n.len - 1], d) + expr(p, n[^1], d) proc genStmtList(p: BProc, n: PNode) = genStmtListExprImpl: - genStmts(p, n[n.len - 1]) + genStmts(p, n[^1]) proc upConv(p: BProc, n: PNode, d: var TLoc) = var a: TLoc - initLocExpr(p, n.sons[0], a) + initLocExpr(p, n[0], a) let dest = skipTypes(n.typ, abstractPtrs) if optObjCheck in p.options and not isObjLackingTypeField(dest): - var r = rdLoc(a) - var nilCheck: Rope = nil - var t = skipTypes(a.t, abstractInst) - while t.kind in {tyVar, tyLent, tyPtr, tyRef}: - if t.kind notin {tyVar, tyLent}: nilCheck = r - if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: - r = "(*$1)" % [r] - t = skipTypes(t.lastSon, abstractInst) - discard getTypeDesc(p.module, t) - if not p.module.compileToCpp: - while t.kind == tyObject and t.sons[0] != nil: - add(r, ".Sup") - t = skipTypes(t.sons[0], skipPtrs) + var nilCheck = Rope(nil) + let r = rdMType(p, a, nilCheck) + let checkFor = if optTinyRtti in p.config.globalOptions: + genTypeInfo2Name(p.module, dest) + else: + genTypeInfo(p.module, dest, n.info) if nilCheck != nil: - linefmt(p, cpsStmts, "if ($1) #chckObj($2.m_type, $3);$n", - nilCheck, r, genTypeInfo(p.module, dest, n.info)) + linefmt(p, cpsStmts, "if ($1) #chckObj($2, $3);$n", + [nilCheck, r, checkFor]) else: - linefmt(p, cpsStmts, "#chckObj($1.m_type, $2);$n", - r, genTypeInfo(p.module, dest, n.info)) - if n.sons[0].typ.kind != tyObject: + linefmt(p, cpsStmts, "#chckObj($1, $2);$n", + [r, checkFor]) + if n[0].typ.kind != tyObject: putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage) else: @@ -2236,33 +2466,33 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: discard getTypeDesc(p.module, skipTypes(n[0].typ, abstractPtrs)) - expr(p, n.sons[0], d) # downcast does C++ for us + expr(p, n[0], d) # downcast does C++ for us else: var dest = skipTypes(n.typ, abstractPtrs) - var arg = n.sons[0] - while arg.kind == nkObjDownConv: arg = arg.sons[0] + var arg = n[0] + while arg.kind == nkObjDownConv: arg = arg[0] var src = skipTypes(arg.typ, abstractPtrs) discard getTypeDesc(p.module, src) var a: TLoc initLocExpr(p, arg, a) var r = rdLoc(a) - let isRef = skipTypes(arg.typ, abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent} + let isRef = skipTypes(arg.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent} if isRef: - add(r, "->Sup") + r.add("->Sup") else: - add(r, ".Sup") - for i in countup(2, abs(inheritanceDiff(dest, src))): add(r, ".Sup") + r.add(".Sup") + for i in 2..abs(inheritanceDiff(dest, src)): r.add(".Sup") if isRef: # it can happen that we end up generating '&&x->Sup' here, so we pack # the '&x->Sup' into a temporary and then those address is taken # (see bug #837). However sometimes using a temporary is not correct: # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test # this by ensuring the destination is also a pointer: - if d.k == locNone and skipTypes(n.typ, abstractInst).kind in {tyRef, tyPtr, tyVar, tyLent}: + if d.k == locNone and skipTypes(n.typ, abstractInstOwned).kind in {tyRef, tyPtr, tyVar, tyLent}: getTemp(p, n.typ, d) - linefmt(p, cpsStmts, "$1 = &$2;$n", rdLoc(d), r) + linefmt(p, cpsStmts, "$1 = &$2;$n", [rdLoc(d), r]) else: r = "&" & r putIntoDest(p, d, n, r, a.storage) @@ -2278,8 +2508,8 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = if id == p.module.labels: # expression not found in the cache: inc(p.module.labels) - addf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, t), tmp, genConstExpr(p, n)]) + p.module.s[cfsData].addf("NIM_CONST $1 $2 = $3;$n", + [getTypeDesc(p.module, t), tmp, genBracedInit(p, n, isConst = true)]) if d.k == locNone: fillLoc(d, locData, n, tmp, OnStatic) @@ -2292,6 +2522,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = proc expr(p: BProc, n: PNode, d: var TLoc) = p.currLineInfo = n.info + case n.kind of nkSym: var sym = n.sym @@ -2327,6 +2558,9 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of skVar, skForVar, skResult, skLet: if {sfGlobal, sfThread} * sym.flags != {}: genVarPrototype(p.module, n) + if sfCompileTime in sym.flags: + genSingleVar(p, sym, n, astdef(sym)) + if sym.loc.r == nil or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) @@ -2363,8 +2597,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = putIntoDest(p, d, n, genLiteral(p, n)) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: - genLineDir(p, n) - let op = n.sons[0] + genLineDir(p, n) # may be redundant, it is generated in fixupCall as well + let op = n[0] if n.typ.isNil: # discard the value: var a: TLoc @@ -2411,7 +2645,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkIfExpr, nkIfStmt: genIf(p, n, d) of nkWhen: # This should be a "when nimvm" node. - expr(p, n.sons[1].sons[0], d) + expr(p, n[1][0], d) of nkObjDownConv: downConv(p, n, d) of nkObjUpConv: upConv(p, n, d) of nkChckRangeF: genRangeChck(p, n, d, "chckRangeF") @@ -2420,7 +2654,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkStringToCString: convStrToCStr(p, n, d) of nkCStringToString: convCStrToStr(p, n, d) of nkLambdaKinds: - var sym = n.sons[namePos].sym + var sym = n[namePos].sym genProc(p.module, sym) if sym.loc.r == nil or sym.loc.lode == nil: internalError(p.config, n.info, "expr: proc not init " & sym.name.s) @@ -2451,11 +2685,14 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = initLocExprSingleUse(p, ex, a) line(p, cpsStmts, "(void)(" & a.r & ");\L") of nkAsmStmt: genAsmStmt(p, n) - of nkTryStmt: - if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions: + of nkTryStmt, nkHiddenTryStmt: + case p.config.exc + of excGoto: + genTryGoto(p, n, d) + of excCpp: genTryCpp(p, n, d) else: - genTry(p, n, d) + genTrySetjmp(p, n, d) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection: # we have to emit the type information for object types here to support @@ -2468,8 +2705,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkPragma: genPragma(p, n) of nkPragmaBlock: expr(p, n.lastSon, d) of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: - if n.sons[genericParamsPos].kind == nkEmpty: - var prc = n.sons[namePos].sym + if n[genericParamsPos].kind == nkEmpty: + var prc = n[namePos].sym # due to a bug/limitation in the lambda lifting, unused inner procs # are not transformed correctly. We work around this issue (#411) here # by ensuring it's no inner proc (owner is a module): @@ -2477,175 +2714,236 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or (prc.kind == skMethod): - # we have not only the header: - if prc.getBody.kind != nkEmpty or lfDynamicLib in prc.loc.flags: - genProc(p.module, prc) + # Generate proc even if empty body, bugfix #11651. + genProc(p.module, prc) of nkParForStmt: genParForStmt(p, n) of nkState: genState(p, n) - of nkGotoState: genGotoState(p, n) + of nkGotoState: + # simply never set it back to 0 here from here on... + inc p.splitDecls + genGotoState(p, n) of nkBreakState: genBreakState(p, n, d) else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") -proc genNamedConstExpr(p: BProc, n: PNode): Rope = - if n.kind == nkExprColonExpr: result = genConstExpr(p, n.sons[1]) - else: result = genConstExpr(p, n) +proc genNamedConstExpr(p: BProc, n: PNode; isConst: bool): Rope = + if n.kind == nkExprColonExpr: result = genBracedInit(p, n[1], isConst) + else: result = genBracedInit(p, n, isConst) proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = - var t = skipTypes(typ, abstractRange-{tyTypeDesc}) + var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc}) case t.kind of tyBool: result = rope"NIM_FALSE" of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result = rope"0" of tyFloat..tyFloat128: result = rope"0.0" - of tyCString, tyString, tyVar, tyLent, tyPointer, tyPtr, tySequence, tyExpr, - tyStmt, tyTypeDesc, tyStatic, tyRef, tyNil: + of tyCString, tyVar, tyLent, tyPointer, tyPtr, tyUntyped, + tyTyped, tyTypeDesc, tyStatic, tyRef, tyNil: result = rope"NIM_NIL" + of tyString, tySequence: + if optSeqDestructors in p.config.globalOptions: + result = rope"{0, NIM_NIL}" + else: + result = rope"NIM_NIL" of tyProc: if t.callConv != ccClosure: result = rope"NIM_NIL" else: result = rope"{NIM_NIL, NIM_NIL}" of tyObject: - if not isObjLackingTypeField(t) and not p.module.compileToCpp: - result = "{{$1}}" % [genTypeInfo(p.module, t, info)] - else: - result = rope"{}" + var count = 0 + result.add "{" + getNullValueAuxT(p, t, t, t.n, nil, result, count, true, info) + result.add "}" of tyTuple: result = rope"{" - for i in 0 ..< typ.len: + for i in 0.. 0: result.add ", " - result.add getDefaultValue(p, typ.sons[i], info) + result.add getDefaultValue(p, t[i], info) result.add "}" - of tyArray: result = rope"{}" + of tyArray: + result = rope"{" + for i in 0.. 0: result.add ", " + result.add getDefaultValue(p, t.sons[1], info) + result.add "}" + #result = rope"{}" of tySet: if mapType(p.config, t) == ctArray: result = rope"{}" else: result = rope"0" else: globalError(p.config, info, "cannot create null element for: " & $t.kind) -proc getNullValueAux(p: BProc; t: PType; obj, cons: PNode, result: var Rope; count: var int) = +proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, + result: var Rope; count: var int; + isConst: bool, info: TLineInfo) = case obj.kind of nkRecList: for it in obj.sons: - getNullValueAux(p, t, it, cons, result, count) + getNullValueAux(p, t, it, constOrNil, result, count, isConst, info) of nkRecCase: - getNullValueAux(p, t, obj.sons[0], cons, result, count) - for i in countup(1, sonsLen(obj) - 1): - getNullValueAux(p, t, lastSon(obj.sons[i]), cons, result, count) + getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info) + if count > 0: result.add ", " + # XXX select default case branch here! + #for i in 1.. 0: result.add ", " inc count let field = obj.sym - for i in 1.. 0: - add(result, genNamedConstExpr(p, n.sons[length - 1])) - addf(result, "}$n", []) + for i in 0.. 0: + result.add(genNamedConstExpr(p, n[^1], isConst)) + result.addf("}$n", []) -proc genConstSeq(p: BProc, n: PNode, t: PType): Rope = +proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool): Rope = var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope] if n.len > 0: # array part needs extra curlies: data.add(", {") - for i in countup(0, n.len - 1): + for i in 0.. 0: data.addf(",$n", []) - data.add genConstExpr(p, n.sons[i]) + data.add genBracedInit(p, n[i], isConst) data.add("}") data.add("}") result = getTempName(p.module) - let base = t.skipTypes(abstractInst).sons[0] + let base = t.skipTypes(abstractInst)[0] appcg(p.module, cfsData, - "NIM_CONST struct {$n" & + "$5 struct {$n" & " #TGenericSeq Sup;$n" & " $1 data[$2];$n" & "} $3 = $4;$n", [ - getTypeDesc(p.module, base), n.len.rope, result, data]) + getTypeDesc(p.module, base), n.len, result, data, + if isConst: "NIM_CONST" else: ""]) result = "(($1)&$2)" % [getTypeDesc(p.module, t), result] -proc genConstExpr(p: BProc, n: PNode): Rope = +proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool): Rope = + var data = rope"{" + for i in 0.. 0: data.addf(",$n", []) + data.add genBracedInit(p, n[i], isConst) + data.add("}") + + let payload = getTempName(p.module) + let base = t.skipTypes(abstractInst)[0] + + appcg(p.module, cfsData, + "static $5 struct {$n" & + " NI cap; void* allocator; $1 data[$2];$n" & + "} $3 = {$2, NIM_NIL, $4};$n", [ + getTypeDesc(p.module, base), n.len, payload, data, + if isConst: "const" else: ""]) + result = "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload] + +proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope = case n.kind of nkHiddenStdConv, nkHiddenSubConv: - result = genConstExpr(p, n.sons[1]) - of nkCurly: - var cs: TBitSet - toBitSet(p.config, n, cs) - result = genRawSetData(cs, int(getSize(p.config, n.typ))) - of nkBracket, nkPar, nkTupleConstr, nkClosure: - var t = skipTypes(n.typ, abstractInst) - if t.kind == tySequence: - result = genConstSeq(p, n, n.typ) - elif t.kind == tyProc and t.callConv == ccClosure and n.len > 1 and - n.sons[1].kind == nkNilLit: - # Conversion: nimcall -> closure. - # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL} - # this behaviour is needed since closure_var = nil must be - # expanded to {NIM_NIL,NIM_NIL} - # in VM closures are initialized with nkPar(nkNilLit, nkNilLit) - # leading to duplicate code like this: - # "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}" - if n[0].kind == nkNilLit: - result = ~"{NIM_NIL,NIM_NIL}" + result = genBracedInit(p, n[1], isConst) + else: + var ty = tyNone + if n.typ == nil: + if n.kind in nkStrKinds: + ty = tyString else: - var d: TLoc - initLocExpr(p, n[0], d) - result = "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, t, clHalfWithEnv), rdLoc(d)] + internalError(p.config, n.info, "node has no type") else: - result = genConstSimpleList(p, n) - of nkObjConstr: - result = genConstObjConstr(p, n) - of nkStrLit..nkTripleStrLit: - if p.config.selectedGc == gcDestructors: - result = genStringLiteralV2Const(p.module, n) + ty = skipTypes(n.typ, abstractInstOwned).kind + case ty + of tySet: + var cs: TBitSet + toBitSet(p.config, n, cs) + result = genRawSetData(cs, int(getSize(p.config, n.typ))) + of tySequence: + if optSeqDestructors in p.config.globalOptions: + result = genConstSeqV2(p, n, n.typ, isConst) + else: + result = genConstSeq(p, n, n.typ, isConst) + of tyProc: + if n.typ.callConv == ccClosure and n.len > 1 and n[1].kind == nkNilLit: + # Conversion: nimcall -> closure. + # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL} + # this behaviour is needed since closure_var = nil must be + # expanded to {NIM_NIL,NIM_NIL} + # in VM closures are initialized with nkPar(nkNilLit, nkNilLit) + # leading to duplicate code like this: + # "{NIM_NIL,NIM_NIL}, {NIM_NIL,NIM_NIL}" + if n[0].kind == nkNilLit: + result = ~"{NIM_NIL,NIM_NIL}" + else: + var d: TLoc + initLocExpr(p, n[0], d) + result = "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, n.typ, clHalfWithEnv), rdLoc(d)] + else: + var d: TLoc + initLocExpr(p, n, d) + result = rdLoc(d) + of tyArray, tyTuple, tyOpenArray, tyVarargs: + result = genConstSimpleList(p, n, isConst) + of tyObject: + result = genConstObjConstr(p, n, isConst) + of tyString, tyCString: + if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit: + result = genStringLiteralV2Const(p.module, n, isConst) + else: + var d: TLoc + initLocExpr(p, n, d) + result = rdLoc(d) else: var d: TLoc initLocExpr(p, n, d) result = rdLoc(d) - else: - var d: TLoc - initLocExpr(p, n, d) - result = rdLoc(d) diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim index 904d01e8112a..b51348508732 100644 --- a/compiler/ccgliterals.nim +++ b/compiler/ccgliterals.nim @@ -7,6 +7,8 @@ # distribution, for details about the copyright. # +# included from cgen.nim + ## This include file contains the logic to produce constant string ## and seq literals. The code here is responsible that ## ``const x = ["a", "b"]`` works without hidden runtime creation code. @@ -19,7 +21,7 @@ template detectVersion(field, corename) = if core == nil or core.kind != skConst: m.g.field = 1 else: - m.g.field = int ast.getInt(core.ast) + m.g.field = toInt(ast.getInt(core.ast)) result = m.g.field proc detectStrVersion(m: BModule): int = @@ -33,8 +35,8 @@ proc detectSeqVersion(m: BModule): int = proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope = discard cgsym(m, "TGenericSeq") result = getTempName(m) - addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", - [result, makeCString(s), rope(len(s))]) + m.s[cfsData].addf("STRING_LITERAL($1, $2, $3);$n", + [result, makeCString(s), rope(s.len)]) proc genStringLiteralV1(m: BModule; n: PNode): Rope = if s.isNil: @@ -47,61 +49,65 @@ proc genStringLiteralV1(m: BModule; n: PNode): Rope = [genStringLiteralDataOnlyV1(m, n.strVal)]) else: result = ropecg(m, "((#NimStringDesc*) &$1$2)", - [m.tmpBase, rope(id)]) + [m.tmpBase, id]) # ------ Version 2: destructor based strings and seqs ----------------------- -proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope = - result = getTempName(m) - addf(m.s[cfsData], "static const struct {$n" & +proc genStringLiteralDataOnlyV2(m: BModule, s: string; result: Rope; isConst: bool) = + m.s[cfsData].addf("static $4 struct {$n" & " NI cap; void* allocator; NIM_CHAR data[$2+1];$n" & "} $1 = { $2, NIM_NIL, $3 };$n", - [result, rope(len(s)), makeCString(s)]) + [result, rope(s.len), makeCString(s), + rope(if isConst: "const" else: "")]) -proc genStringLiteralV2(m: BModule; n: PNode): 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) + result = getTempName(m) discard cgsym(m, "NimStrPayload") discard cgsym(m, "NimStringV2") # string literal not found in the cache: - let pureLit = genStringLiteralDataOnlyV2(m, n.strVal) - result = getTempName(m) - addf(m.s[cfsData], "static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", - [result, rope(len(n.strVal)), pureLit]) + m.s[cfsData].addf("static $4 NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", + [result, rope(n.strVal.len), pureLit, rope(if isConst: "const" else: "")]) else: - result = m.tmpBase & rope(id+1) + 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): 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) discard cgsym(m, "NimStrPayload") discard cgsym(m, "NimStringV2") # string literal not found in the cache: - pureLit = genStringLiteralDataOnlyV2(m, n.strVal) + genStringLiteralDataOnlyV2(m, n.strVal, pureLit, isConst) else: pureLit = m.tmpBase & rope(id) - result = "{$1, (NimStrPayload*)&$2}" % [rope(len(n.strVal)), pureLit] + result = "{$1, (NimStrPayload*)&$2}" % [rope(n.strVal.len), pureLit] # ------ Version selector --------------------------------------------------- -proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope = +proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo; + isConst: bool): Rope = case detectStrVersion(m) of 0, 1: result = genStringLiteralDataOnlyV1(m, s) - of 2: result = genStringLiteralDataOnlyV2(m, s) + of 2: + result = getTempName(m) + genStringLiteralDataOnlyV2(m, s, result, isConst) else: localError(m.config, info, "cannot determine how to produce code for string literal") -proc genStringLiteralFromData(m: BModule; data: Rope; info: TLineInfo): Rope = - result = ropecg(m, "((#NimStringDesc*) &$1)", - [data]) - proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope = result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) proc genStringLiteral(m: BModule; n: PNode): Rope = case detectStrVersion(m) of 0, 1: result = genStringLiteralV1(m, n) - of 2: result = genStringLiteralV2(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") diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index 2d68a198e328..2be34aace8d0 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -11,8 +11,8 @@ ## is needed for incremental compilation. import - ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils, - intsets, platform, llstream, tables, sighashes, pathutils + ast, ropes, options, strutils, nimlexbase, cgendata, rodutils, + intsets, llstream, tables, modulegraphs, pathutils # Careful! Section marks need to contain a tabulator so that they cannot # be part of C string literals. @@ -21,17 +21,18 @@ 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", - cfsVars: "NIM_merge_VARS", cfsData: "NIM_merge_DATA", + cfsVars: "NIM_merge_VARS", cfsProcs: "NIM_merge_PROCS", cfsInitProc: "NIM_merge_INIT_PROC", - cfsDatInitProc: "NIM_merge_DATINIT_PROC", + cfsDatInitProc: "NIM_merge_DATINIT_PROC", cfsTypeInit1: "NIM_merge_TYPE_INIT1", cfsTypeInit2: "NIM_merge_TYPE_INIT2", cfsTypeInit3: "NIM_merge_TYPE_INIT3", @@ -49,9 +50,9 @@ const proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope = if compilationCachePresent(conf): result = nil - add(result, "\n/*\t") - add(result, CFileSectionNames[fs]) - add(result, ":*/\n") + result.add("\n/*\t") + result.add(CFileSectionNames[fs]) + result.add(":*/\n") proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope = if compilationCachePresent(conf): @@ -60,9 +61,9 @@ proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope = proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope = if compilationCachePresent(conf): result = rope("") - add(result, "\n/*\t") - add(result, CProcSectionNames[ps]) - add(result, ":*/\n") + result.add("\n/*\t") + result.add(CProcSectionNames[ps]) + result.add(":*/\n") proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope = if compilationCachePresent(conf): @@ -145,38 +146,34 @@ proc atEndMark(buf: cstring, pos: int): bool = proc readVerbatimSection(L: var TBaseLexer): Rope = var pos = L.bufpos - var buf = L.buf var r = newStringOfCap(30_000) while true: - case buf[pos] + case L.buf[pos] of CR: pos = nimlexbase.handleCR(L, pos) - buf = L.buf r.add('\L') of LF: pos = nimlexbase.handleLF(L, pos) - buf = L.buf r.add('\L') of '\0': doAssert(false, "ccgmerge: expected: " & NimMergeEndMark) break else: - if atEndMark(buf, pos): + if atEndMark(L.buf, pos): inc pos, NimMergeEndMark.len break - r.add(buf[pos]) + r.add(L.buf[pos]) inc pos L.bufpos = pos result = r.rope proc readKey(L: var TBaseLexer, result: var string) = var pos = L.bufpos - var buf = L.buf setLen(result, 0) - while buf[pos] in IdentChars: - result.add(buf[pos]) + while L.buf[pos] in IdentChars: + result.add(L.buf[pos]) inc pos - if buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected") + if L.buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected") L.bufpos = pos + 1 # skip ':' proc newFakeType(id: int): PType = diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index bc873539772e..ec13b991d277 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -15,18 +15,21 @@ const stringCaseThreshold = 8 # above X strings a hash-switch for strings is generated -proc registerGcRoot(p: BProc, v: PSym) = - if p.config.selectedGC in {gcMarkAndSweep, gcDestructors, gcV2, 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 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 :-) - let prc = genTraverseProcForGlobal(p.module, v, v.info) - if sfThread in v.flags: - appcg(p.module, p.module.initProc.procSec(cpsInit), - "#nimRegisterThreadLocalMarker($1);$n", [prc]) - else: - appcg(p.module, p.module.initProc.procSec(cpsInit), - "#nimRegisterGlobalMarker($1);$n", [prc]) + result = genTraverseProcForGlobal(p.module, v, v.info) + +proc registerTraverseProc(p: BProc, v: PSym, traverseProc: Rope) = + if sfThread in v.flags: + appcg(p.module, p.module.initProc.procSec(cpsInit), + "$n\t#nimRegisterThreadLocalMarker($1);$n$n", [traverseProc]) + else: + appcg(p.module, p.module.initProc.procSec(cpsInit), + "$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc]) proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} = if n.kind == nkEmpty: return false @@ -41,46 +44,90 @@ proc inExceptBlockLen(p: BProc): int = for x in p.nestedTryStmts: if x.inExcept: result.inc +proc startBlockInternal(p: BProc): int {.discardable.} = + inc(p.labels) + result = p.blocks.len + 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 + +template startBlock(p: BProc, start: FormatStr = "{$n", + args: varargs[Rope]): int = + lineCg(p, cpsStmts, start, args) + startBlockInternal(p) + +proc endBlock(p: BProc) + proc genVarTuple(p: BProc, n: PNode) = var tup, field: TLoc if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple") - var L = sonsLen(n) # if we have a something that's been captured, use the lowering instead: - for i in countup(0, L-3): + for i in 0.. 2 + # do not close and reopen blocks if this is a 'global' but inside of a block (if/while/block) + forHcr = forHcr and not isGlobalInBlock + + if forHcr: + # check with the boolean if the initializing code for the tuple should be ran + lineCg(p, cpsStmts, "if ($1)$n", [hcrCond]) + startBlock(p) + genLineDir(p, n) - initLocExpr(p, n.sons[L-1], tup) + initLocExpr(p, n[^1], tup) var t = tup.t.skipTypes(abstractInst) - for i in countup(0, L-3): - let vn = n.sons[i] + for i in 0..= 2 and n[1].kind == nkIntLit: - statesCounter = n[1].intVal + statesCounter = getInt(n[1]) let prefix = if n.len == 3 and n[2].kind == nkStrLit: n[2].strVal.rope else: rope"STATE" - for i in 0i64 .. statesCounter: + for i in 0i64..toInt64(statesCounter): lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)]) lineF(p, cpsStmts, "}$n", []) @@ -219,11 +256,11 @@ proc genBreakState(p: BProc, n: PNode, d: var TLoc) = var a: TLoc initLoc(d, locExpr, n, OnUnknown) - if n.sons[0].kind == nkClosure: - initLocExpr(p, n.sons[0].sons[1], a) + if n[0].kind == nkClosure: + initLocExpr(p, n[0][1], a) d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)] else: - initLocExpr(p, n.sons[0], a) + initLocExpr(p, n[0], a) # the environment is guaranteed to contain the 'state' field at offset 1: d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)] @@ -233,64 +270,77 @@ proc genGotoVar(p: BProc; value: PNode) = else: lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope]) -proc genSingleVar(p: BProc, a: PNode) = - let vn = a.sons[0] - let v = vn.sym - if sfCompileTime in v.flags: return +proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope + +proc potentialValueInit(p: BProc; v: PSym; value: PNode): Rope = + if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn: + 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 + result = genBracedInit(p, value, isConst = false) + else: + 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, a.sons[2]) + genGotoVar(p, value) return var targetProc = p + var traverseProc: Rope + let valueAsRope = potentialValueInit(p, v, value) if sfGlobal in v.flags: if v.flags * {sfImportc, sfExportc} == {sfImportc} and - a.sons[2].kind == nkEmpty and + value.kind == nkEmpty and v.loc.flags * {lfHeader, lfNoDecl} != {}: return if sfPure in v.flags: # v.owner.kind != skModule: targetProc = p.module.preInitProc - assignGlobalVar(targetProc, vn) + 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. - 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` - if sfThread in v.flags and emulatedThreadVars(p.config) and - isComplexValueType(v.typ): - initLocExprSingleUse(p.module.preInitProc, vn, loc) - genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, true) + 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` + if sfThread in v.flags and emulatedThreadVars(p.config) and + isComplexValueType(v.typ): + initLocExprSingleUse(p.module.preInitProc, vn, loc) + genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, constructObj) # Alternative construction using default constructor (which may zeromem): # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc) if sfExportc in v.flags and p.module.g.generatedHeader != nil: genVarPrototype(p.module.g.generatedHeader, vn) - registerGcRoot(p, v) + traverseProc = getTraverseProc(p, v) + if traverseProc != nil and not p.hcrOn: + registerTraverseProc(p, v, traverseProc) else: - let value = a.sons[2] let imm = isAssignedImmediately(p.config, value) if imm and p.module.compileToCpp and p.splitDecls == 0 and 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, a) + genLineDir(p, vn) let decl = localVarDecl(p, vn) var tmp: TLoc if value.kind in nkCallKinds and value[0].kind == nkSym and sfConstructor in value[0].sym.flags: var params: Rope - let typ = skipTypes(value.sons[0].typ, abstractInst) + let typ = skipTypes(value[0].typ, abstractInst) assert(typ.kind == tyProc) for i in 1.. 3 and v.owner.kind == skModule: + # put it in the locals section - mainly because of loops which + # use the var in a call to resetLoc() in the statements section + lineCg(targetProc, cpsLocals, "hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1);$n", + [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc]) + # nothing special left to do later on - let's avoid closing and reopening blocks + forHcr = false + + # we close and reopen the global if (nim_hcr_do_init_) blocks in the main Init function + # for the module so we can have globals and top-level code be interleaved and still + # be able to re-run it but without the top level code - just the init of globals + if forHcr: + 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 == nil: + genLineDir(targetProc, vn) + loadInto(targetProc, vn, value, v.loc) + if forHcr: + endBlock(targetProc) + +proc genSingleVar(p: BProc, a: PNode) = + let v = a[0].sym + if sfCompileTime in v.flags: return + genSingleVar(p, v, a[0], a[2]) proc genClosureVar(p: BProc, a: PNode) = - var immediateAsgn = a.sons[2].kind != nkEmpty + var immediateAsgn = a[2].kind != nkEmpty var v: TLoc - initLocExpr(p, a.sons[0], v) + initLocExpr(p, a[0], v) genLineDir(p, a) if immediateAsgn: - loadInto(p, a.sons[0], a.sons[2], v) + loadInto(p, a[0], a[2], v) else: constructLoc(p, v) @@ -321,7 +398,7 @@ proc genVarStmt(p: BProc, n: PNode) = if it.kind == nkCommentStmt: continue if it.kind == nkIdentDefs: # can be a lifted var nowadays ... - if it.sons[0].kind == nkSym: + if it[0].kind == nkSym: genSingleVar(p, it) else: genClosureVar(p, it) @@ -352,53 +429,53 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) = if d.k == locTemp and isEmptyType(n.typ): d.k = locNone if it.len == 2: startBlock(p) - initLocExprSingleUse(p, it.sons[0], a) + initLocExprSingleUse(p, it[0], a) lelse = getLabel(p) inc(p.labels) lineF(p, cpsStmts, "if (!$1) goto $2;$n", [rdLoc(a), lelse]) if p.module.compileToCpp: # avoid "jump to label crosses initialization" error: - add(p.s(cpsStmts), "{") - expr(p, it.sons[1], d) - add(p.s(cpsStmts), "}") + p.s(cpsStmts).add "{" + expr(p, it[1], d) + p.s(cpsStmts).add "}" else: - expr(p, it.sons[1], d) + expr(p, it[1], d) endBlock(p) - if sonsLen(n) > 1: + if n.len > 1: lineF(p, cpsStmts, "goto $1;$n", [lend]) fixLabel(p, lelse) elif it.len == 1: startBlock(p) - expr(p, it.sons[0], d) + expr(p, it[0], d) endBlock(p) else: internalError(p.config, n.info, "genIf()") - if sonsLen(n) > 1: fixLabel(p, lend) + if n.len > 1: fixLabel(p, lend) proc genReturnStmt(p: BProc, t: PNode) = if nfPreventCg in t.flags: return - p.beforeRetNeeded = true + p.flags.incl beforeRetNeeded genLineDir(p, t) - if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0]) + if (t[0].kind != nkEmpty): genStmts(p, t[0]) blockLeaveActions(p, howManyTrys = p.nestedTryStmts.len, howManyExcepts = p.inExceptBlockLen) - if (p.finallySafePoints.len > 0) and not isDefined(p.config, "nimQuirky"): + if (p.finallySafePoints.len > 0) and noSafePoints notin p.flags: # If we're in a finally block, and we came here by exception # consume it before we return. - var safePoint = p.finallySafePoints[p.finallySafePoints.len-1] - linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", safePoint) + var safePoint = p.finallySafePoints[^1] + linefmt(p, cpsStmts, "if ($1.status != 0) #popCurrentException();$n", [safePoint]) lineF(p, cpsStmts, "goto BeforeRet_;$n", []) proc genGotoForCase(p: BProc; caseStmt: PNode) = - for i in 1 ..< caseStmt.len: + for i in 1.. 10_000: localError(p.config, it.info, "case statement has too many cases for computed goto"); return - arraySize = aSize.int - if firstOrd(p.config, it.sons[0].typ) != 0: + arraySize = toInt(aSize) + if firstOrd(p.config, it[0].typ) != 0: localError(p.config, it.info, "case statement has to start at 0 for computed goto"); return if casePos < 0: @@ -446,54 +527,54 @@ proc genComputedGoto(p: BProc; n: PNode) = gotoArray.addf("&&TMP$#_};$n", [rope(id+arraySize)]) line(p, cpsLocals, gotoArray) - for j in 0 ..< casePos: - genStmts(p, n.sons[j]) + for j in 0.. 0 and p.nestedTryStmts[^1].inExcept: +proc raiseExit(p: BProc) = + assert p.config.exc == excGoto + p.flags.incl nimErrorFlagAccessed + if p.nestedTryStmts.len == 0: + p.flags.incl beforeRetNeeded + # easy case, simply goto 'ret': + lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto BeforeRet_;$n", []) + else: + lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n", + [p.nestedTryStmts[^1].label]) + +proc finallyActions(p: BProc) = + if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: # if the current try stmt have a finally block, # we must execute it before reraising - var finallyBlock = p.nestedTryStmts[^1].n[^1] - if finallyBlock.kind == nkFinally: + let finallyBlock = p.nestedTryStmts[^1].fin + if finallyBlock != nil: genSimpleBlock(p, finallyBlock[0]) + +proc genRaiseStmt(p: BProc, t: PNode) = + if p.config.exc == excCpp: + discard cgsym(p.module, "popCurrentExceptionEx") if t[0].kind != nkEmpty: var a: TLoc initLocExprSingleUse(p, t[0], a) + finallyActions(p) var e = rdLoc(a) var typ = skipTypes(t[0].typ, abstractPtrs) genLineDir(p, t) @@ -615,28 +722,41 @@ proc genRaiseStmt(p: BProc, t: PNode) = lineCg(p, cpsStmts, "#raiseExceptionEx((#Exception*)$1, $2, $3, $4, $5);$n", [e, makeCString(typ.sym.name.s), makeCString(if p.prc != nil: p.prc.name.s else: p.module.module.name.s), - makeCString(toFileName(p.config, t.info)), rope(toLinenumber(t.info))]) + quotedFilename(p.config, t.info), toLinenumber(t.info)]) + if optOwnedRefs in p.config.globalOptions: + lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [e]) else: + finallyActions(p) genLineDir(p, t) # reraise the last exception: - if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions: + if p.config.exc == excCpp: line(p, cpsStmts, ~"throw;$n") else: - linefmt(p, cpsStmts, "#reraiseException();$n") + linefmt(p, cpsStmts, "#reraiseException();$n", []) + if p.config.exc == excGoto: + let L = p.nestedTryStmts.len + if L == 0: + p.flags.incl beforeRetNeeded + # easy case, simply goto 'ret': + lineCg(p, cpsStmts, "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. + lineCg(p, cpsStmts, "goto LA$1_;$n", + [p.nestedTryStmts[L-1].label]) + # + ord(p.nestedTryStmts[L-1].inExcept)]) -proc genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, +template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc, rangeFormat, eqFormat: FormatStr, labl: TLabel) = - var - x, y: TLoc - var length = sonsLen(b) - for i in countup(0, length - 2): - if b.sons[i].kind == nkRange: - initLocExpr(p, b.sons[i].sons[0], x) - initLocExpr(p, b.sons[i].sons[1], y) + var x, y: TLoc + for i in 0.. stringCaseThreshold: var bitMask = math.nextPowerOfTwo(strings) - 1 var branches: seq[Rope] newSeq(branches, bitMask + 1) var a: TLoc - initLocExpr(p, t.sons[0], a) # fist pass: gnerate ifs+goto: + initLocExpr(p, t[0], a) # fist pass: generate ifs+goto: var labId = p.labels - for i in countup(1, sonsLen(t) - 1): + for i in 1.. RangeExpandLimit: + if (b[i].kind == nkRange) and + b[i][1].intVal - b[i][0].intVal > RangeExpandLimit: return true proc ifSwitchSplitPoint(p: BProc, n: PNode): int = - for i in 1..n.len-1: + for i in 1.. 0: genIfForCaseUntil(p, n, d, rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n", eqFormat = "if ($1 == $2) goto $3;$n", @@ -779,7 +898,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) = if splitPoint+1 < n.len: lineF(p, cpsStmts, "switch ($1) {$n", [rdCharLoc(a)]) var hasDefault = false - for i in splitPoint+1 ..< n.len: + for i in splitPoint+1..= $2 && $1 <= $3) goto $4;$n", "if ($1 == $2) goto $3;$n") else: - if t.sons[0].kind == nkSym and sfGoto in t.sons[0].sym.flags: + if t[0].kind == nkSym and sfGoto in t[0].sym.flags: genGotoForCase(p, t) else: genOrdinalCase(p, t, d) proc genRestoreFrameAfterException(p: BProc) = if optStackTrace in p.module.config.options: - if not p.hasCurFramePointer: - p.hasCurFramePointer = true + if hasCurFramePointer notin p.flags: + p.flags.incl hasCurFramePointer p.procSec(cpsLocals).add(ropecg(p.module, "\tTFrame* _nimCurFrame;$n", [])) p.procSec(cpsInit).add(ropecg(p.module, "\t_nimCurFrame = #getFrame();$n", [])) - linefmt(p, cpsStmts, "#setFrame(_nimCurFrame);$n") + linefmt(p, cpsStmts, "#setFrame(_nimCurFrame);$n", []) proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = # code to generate: @@ -845,7 +964,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = getTemp(p, t.typ, d) genLineDir(p, t) discard cgsym(p.module, "popCurrentExceptionEx") - add(p.nestedTryStmts, (t, false)) + let fin = if t[^1].kind == nkFinally: t[^1] else: nil + p.nestedTryStmts.add((fin, false, 0.Natural)) startBlock(p, "try {$n") expr(p, t[0], d) endBlock(p) @@ -866,7 +986,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = genExceptBranchBody(t[i][0]) endBlock(p) else: - for j in 0..t[i].len-2: + for j in 0.. 1 and t[^1].kind == nkFinally: + genStmts(p, t[^1][0]) + return + + let fin = if t[^1].kind == nkFinally: t[^1] else: nil + inc p.labels, 2 + let lab = p.labels-1 + p.nestedTryStmts.add((fin, false, Natural lab)) + + p.flags.incl nimErrorFlagAccessed + linefmt(p, cpsStmts, "NI oldNimErr$1_ = *nimErr_; *nimErr_ = 0;;$n", [lab]) + + expr(p, t[0], d) + + if 1 < t.len and t[1].kind == nkExceptBranch: + startBlock(p, "if (NIM_UNLIKELY(*nimErr_)) {$n") + else: + startBlock(p) + # pretend we did handle the error for the safe execution of the sections: + linefmt(p, cpsStmts, "LA$1_: oldNimErr$1_ = *nimErr_; *nimErr_ = 0;$n", [lab]) + + p.nestedTryStmts[^1].inExcept = true + var i = 1 + while (i < t.len) and (t[i].kind == nkExceptBranch): + + inc p.labels + let nextExcept = p.labels + p.nestedTryStmts[^1].label = nextExcept + + # bug #4230: avoid false sharing between branches: + if d.k == locTemp and isEmptyType(t.typ): d.k = locNone + if t[i].len == 1: + # general except section: + if i > 1: lineF(p, cpsStmts, "else", []) + startBlock(p) + # we handled the exception, remember this: + linefmt(p, cpsStmts, "--oldNimErr$1_;$n", [lab]) + expr(p, t[i][0], d) + else: + var orExpr: Rope = nil + for j in 0..$1, $2)", [memberName, checkFor]) + + if i > 1: line(p, cpsStmts, "else ") + startBlock(p, "if ($1) {$n", [orExpr]) + # we handled the exception, remember this: + linefmt(p, cpsStmts, "--oldNimErr$1_;$n", [lab]) + expr(p, t[i][^1], d) + + linefmt(p, cpsStmts, "#popCurrentException();$n", []) + linefmt(p, cpsStmts, "LA$1_:;$n", [nextExcept]) + endBlock(p) + + inc(i) + discard pop(p.nestedTryStmts) + endBlock(p) + + #linefmt(p, cpsStmts, "LA$1_:;$n", [lab+1]) + if i < t.len and t[i].kind == nkFinally: + startBlock(p) + if not bodyCanRaise(t[i][0]): + # this is an important optimization; most destroy blocks are detected not to raise an + # exception and so we help the C optimizer by not mutating nimErr_ pointlessly: + genStmts(p, t[i][0]) + else: + # pretend we did handle the error for the safe execution of the 'finally' section: + linefmt(p, cpsStmts, "NI oldNimErrFin$1_ = *nimErr_; *nimErr_ = 0;$n", [lab]) + genStmts(p, t[i][0]) + # this is correct for all these cases: + # 1. finally is run during ordinary control flow + # 2. finally is run after 'except' block handling: these however set the + # error back to nil. + # 3. finally is run for exception handling code without any 'except' + # handler present or only handlers that did not match. + linefmt(p, cpsStmts, "*nimErr_ += oldNimErr$1_ + (*nimErr_ - oldNimErrFin$1_); oldNimErr$1_ = 0;$n", [lab]) + raiseExit(p) + endBlock(p) + # restore the real error value: + linefmt(p, cpsStmts, "*nimErr_ += oldNimErr$1_;$n", [lab]) + +proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = # code to generate: # # XXX: There should be a standard dispatch algorithm @@ -920,79 +1149,90 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = # if not isEmptyType(t.typ) and d.k == locNone: getTemp(p, t.typ, d) - let quirkyExceptions = isDefined(p.config, "nimQuirky") + let quirkyExceptions = p.config.exc == excQuirky or + (t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags) if not quirkyExceptions: p.module.includeHeader("") + else: + p.flags.incl noSafePoints genLineDir(p, t) discard cgsym(p.module, "Exception") var safePoint: Rope if not quirkyExceptions: safePoint = getTempName(p.module) - linefmt(p, cpsLocals, "#TSafePoint $1;$n", safePoint) - linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", safePoint) + linefmt(p, cpsLocals, "#TSafePoint $1;$n", [safePoint]) + linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", [safePoint]) if isDefined(p.config, "nimStdSetjmp"): - linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint) + 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) + linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", [safePoint]) elif isDefined(p.config, "nimRawSetjmp"): - 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) + linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) startBlock(p, "if ($1.status == 0) {$n", [safePoint]) - var length = sonsLen(t) - add(p.nestedTryStmts, (t, false)) - expr(p, t.sons[0], d) + let fin = if t[^1].kind == nkFinally: t[^1] else: nil + p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural)) + expr(p, t[0], d) if not quirkyExceptions: - linefmt(p, cpsStmts, "#popSafePoint();$n") + linefmt(p, cpsStmts, "#popSafePoint();$n", []) endBlock(p) startBlock(p, "else {$n") - linefmt(p, cpsStmts, "#popSafePoint();$n") + linefmt(p, cpsStmts, "#popSafePoint();$n", []) genRestoreFrameAfterException(p) - elif 1 < length and t.sons[1].kind == nkExceptBranch: - startBlock(p, "if (#getCurrentException()) {$n") + elif 1 < t.len and t[1].kind == nkExceptBranch: + startBlock(p, "if (#nimBorrowCurrentException()) {$n") else: startBlock(p) p.nestedTryStmts[^1].inExcept = true var i = 1 - while (i < length) and (t.sons[i].kind == nkExceptBranch): + while (i < t.len) and (t[i].kind == nkExceptBranch): # bug #4230: avoid false sharing between branches: if d.k == locTemp and isEmptyType(t.typ): d.k = locNone - var blen = sonsLen(t.sons[i]) - if blen == 1: + if t[i].len == 1: # general except section: if i > 1: lineF(p, cpsStmts, "else", []) startBlock(p) if not quirkyExceptions: - linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) - expr(p, t.sons[i].sons[0], d) - linefmt(p, cpsStmts, "#popCurrentException();$n") + linefmt(p, cpsStmts, "$1.status = 0;$n", [safePoint]) + expr(p, t[i][0], d) + linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlock(p) else: var orExpr: Rope = nil - for j in countup(0, blen - 2): - assert(t.sons[i].sons[j].kind == nkType) - if orExpr != nil: add(orExpr, "||") - let isObjFormat = if not p.module.compileToCpp: - "#isObj(#getCurrentException()->Sup.m_type, $1)" - else: "#isObj(#getCurrentException()->m_type, $1)" - appcg(p.module, orExpr, isObjFormat, - [genTypeInfo(p.module, t[i][j].typ, t[i][j].info)]) + for j in 0..$1, $2)", [memberName, checkFor]) + if i > 1: line(p, cpsStmts, "else ") startBlock(p, "if ($1) {$n", [orExpr]) if not quirkyExceptions: - linefmt(p, cpsStmts, "$1.status = 0;$n", safePoint) - expr(p, t.sons[i].sons[blen-1], d) - linefmt(p, cpsStmts, "#popCurrentException();$n") + linefmt(p, cpsStmts, "$1.status = 0;$n", [safePoint]) + expr(p, t[i][^1], d) + linefmt(p, cpsStmts, "#popCurrentException();$n", []) endBlock(p) inc(i) discard pop(p.nestedTryStmts) endBlock(p) # end of else block - if i < length and t.sons[i].kind == nkFinally: + if i < t.len and t[i].kind == nkFinally: p.finallySafePoints.add(safePoint) - genSimpleBlock(p, t.sons[i].sons[0]) + startBlock(p) + genStmts(p, t[i][0]) + # pretend we handled the exception in a 'finally' so that we don't + # re-raise the unhandled one but instead keep the old one (it was + # not popped either): + if not quirkyExceptions and getCompilerProc(p.module.g.graph, "nimLeaveFinally") != nil: + linefmt(p, cpsStmts, "if ($1.status != 0) #nimLeaveFinally();$n", [safePoint]) + endBlock(p) discard pop(p.finallySafePoints) if not quirkyExceptions: - linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", safePoint) + linefmt(p, cpsStmts, "if ($1.status != 0) #reraiseException();$n", [safePoint]) proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = var res = "" @@ -1033,12 +1273,12 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope = if x[j] in {'"', ':'}: # don't modify the line if already in quotes or # some clobber register list: - add(result, x); add(result, "\L") + result.add(x); result.add("\L") else: # ignore empty lines - add(result, "\"") - add(result, x) - add(result, "\\n\"\n") + result.add("\"") + result.add(x) + result.add("\\n\"\n") else: res.add("\L") result = res.rope @@ -1052,74 +1292,48 @@ proc genAsmStmt(p: BProc, t: PNode) = # work: if p.prc == nil: # top level asm statement? - addf(p.module.s[cfsProcHeaders], CC[p.config.cCompiler].asmStmtFrmt, [s]) + p.module.s[cfsProcHeaders].add runtimeFormat(CC[p.config.cCompiler].asmStmtFrmt, [s]) else: - lineF(p, cpsStmts, 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.sons[0].kind in {nkStrLit..nkTripleStrLit}: - let sec = n.sons[0].strVal + if n.len >= 1 and n[0].kind in {nkStrLit..nkTripleStrLit}: + let sec = n[0].strVal 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 = genAsmOrEmitStmt(p, t.sons[1]) + var s = genAsmOrEmitStmt(p, t[1]) if p.prc == nil: # top level emit pragma? let section = determineSection(t[1]) genCLineDir(p.module.s[section], t.info, p.config) - add(p.module.s[section], s) + p.module.s[section].add(s) else: genLineDir(p, t) line(p, cpsStmts, s) -proc genBreakPoint(p: BProc, t: PNode) = - var name: string - if optEndb in p.options: - if t.kind == nkExprColonExpr: - assert(t.sons[1].kind in {nkStrLit..nkTripleStrLit}) - name = normalize(t.sons[1].strVal) - else: - inc(p.module.g.breakPointId) - name = "bp" & $p.module.g.breakPointId - genLineDir(p, t) # BUGFIX - appcg(p.module, p.module.g.breakpoints, - "#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [ - rope(toLinenumber(t.info)), makeCString(toFilename(p.config, t.info)), - makeCString(name)]) - -proc genWatchpoint(p: BProc, n: PNode) = - if optEndb notin p.options: return - var a: TLoc - initLocExpr(p, n.sons[1], a) - let typ = skipTypes(n.sons[1].typ, abstractVarRange) - lineCg(p, cpsStmts, "#dbgRegisterWatchpoint($1, (NCSTRING)$2, $3);$n", - [addrLoc(p.config, a), makeCString(renderTree(n.sons[1])), - genTypeInfo(p.module, typ, n.info)]) - proc genPragma(p: BProc, n: PNode) = for it in n.sons: case whichPragma(it) of wEmit: genEmit(p, it) - of wBreakpoint: genBreakPoint(p, it) - of wWatchPoint: genWatchpoint(p, it) of wInjectStmt: var p = newProc(nil, p.module) p.options = p.options - {optLineTrace, optStackTrace} - genStmts(p, it.sons[1]) + genStmts(p, it[1]) p.module.injectStmt = p.s(cpsStmts) else: discard proc fieldDiscriminantCheckNeeded(p: BProc, asgn: PNode): bool = if optFieldCheck in p.options: - var le = asgn.sons[0] + var le = asgn[0] if le.kind == nkCheckedFieldExpr: - var field = le.sons[0].sons[1].sym + var field = le[0][1].sym result = sfDiscriminant in field.flags elif le.kind == nkDotExpr: - var field = le.sons[1].sym + var field = le[1].sym result = sfDiscriminant in field.flags proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType, @@ -1127,70 +1341,71 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType, var t = skipTypes(objtype, abstractVar) assert t.kind == tyObject discard genTypeInfo(p.module, t, a.lode.info) - var L = lengthOrd(p.config, field.typ) if not containsOrIncl(p.module.declaredThings, field.id): appcg(p.module, cfsVars, "extern $1", - discriminatorTableDecl(p.module, t, field)) + [discriminatorTableDecl(p.module, t, field)]) lineCg(p, cpsStmts, "#FieldDiscriminantCheck((NI)(NU)($1), (NI)(NU)($2), $3, $4);$n", [rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field), - intLiteral(L+1)]) + intLiteral(toInt64(lengthOrd(p.config, field.typ))+1)]) + +proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) = + const ObjDiscMappingProcSlot = -5 + var theProc: PSym = nil + for idx, p in items(t.methods): + if idx == ObjDiscMappingProcSlot: + theProc = p + break + if theProc == nil: + theProc = genCaseObjDiscMapping(t, field, e.info, p.module.g.graph) + t.methods.add((ObjDiscMappingProcSlot, theProc)) + var call = newNodeIT(nkCall, e.info, getSysType(p.module.g.graph, e.info, tyUInt8)) + call.add newSymNode(theProc) + call.add e + expr(p, call, d) proc asgnFieldDiscriminant(p: BProc, e: PNode) = var a, tmp: TLoc - var dotExpr = e.sons[0] - if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr.sons[0] - initLocExpr(p, e.sons[0], a) + var dotExpr = e[0] + if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr[0] + initLocExpr(p, e[0], a) getTemp(p, a.t, tmp) - expr(p, e.sons[1], tmp) - genDiscriminantCheck(p, a, tmp, dotExpr.sons[0].typ, dotExpr.sons[1].sym) - genAssignment(p, a, tmp, {}) - -proc patchAsgnStmtListExpr(father, orig, n: PNode) = - case n.kind - of nkDerefExpr, nkHiddenDeref: - let asgn = copyNode(orig) - asgn.add orig[0] - asgn.add n - father.add asgn - of nkStmtList, nkStmtListExpr: - for x in n: - patchAsgnStmtListExpr(father, orig, x) + expr(p, e[1], tmp) + let field = dotExpr[1].sym + if optTinyRtti in p.config.globalOptions: + let t = dotExpr[0].typ.skipTypes(abstractInst) + var oldVal, newVal: TLoc + genCaseObjDiscMapping(p, e[0], t, field, oldVal) + genCaseObjDiscMapping(p, e[1], t, field, newVal) + lineCg(p, cpsStmts, + "#nimFieldDiscriminantCheckV2($1, $2);$n", + [rdLoc(oldVal), rdLoc(newVal)]) else: - father.add n + genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field) + genAssignment(p, a, tmp, {}) proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = - if e.sons[0].kind == nkSym and sfGoto in e.sons[0].sym.flags: + if e[0].kind == nkSym and sfGoto in e[0].sym.flags: genLineDir(p, e) - genGotoVar(p, e.sons[1]) + genGotoVar(p, e[1]) elif not fieldDiscriminantCheckNeeded(p, e): - # this fixes bug #6422 but we really need to change the representation of - # arrays in the backend... let le = e[0] let ri = e[1] - var needsRepair = false - var it = ri - while it.kind in {nkStmtList, nkStmtListExpr}: - it = it.lastSon - needsRepair = true - if it.kind in {nkDerefExpr, nkHiddenDeref} and needsRepair: - var patchedTree = newNodeI(nkStmtList, e.info) - patchAsgnStmtListExpr(patchedTree, e, ri) - genStmts(p, patchedTree) - return var a: TLoc discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs)) - if le.kind in {nkDerefExpr, nkHiddenDeref}: - genDeref(p, le, a, enforceDeref=true) - else: - initLocExpr(p, le, a) + initLoc(a, locNone, le, OnUnknown) + a.flags.incl(lfEnforceDeref) + a.flags.incl(lfPrepareForMutation) + expr(p, le, a) + a.flags.excl(lfPrepareForMutation) if fastAsgn: incl(a.flags, lfNoDeepCopy) assert(a.t != nil) genLineDir(p, ri) - loadInto(p, e.sons[0], ri, a) + loadInto(p, le, ri, a) else: genLineDir(p, e) asgnFieldDiscriminant(p, e) + message(p.config, e.info, warnCaseTransition) proc genStmts(p: BProc, t: PNode) = var a: TLoc diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index 3e8a870410e4..cc72e3d37e43 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -16,12 +16,12 @@ proc emulatedThreadVars(conf: ConfigRef): bool = result = {optThreads, optTlsEmulation} <= conf.globalOptions proc accessThreadLocalVar(p: BProc, s: PSym) = - if emulatedThreadVars(p.config) and not p.threadVarAccessed: - p.threadVarAccessed = true + if emulatedThreadVars(p.config) and threadVarAccessed notin p.flags: + p.flags.incl threadVarAccessed incl p.module.flags, usesThreadVars - addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV_;$n", []) - add(p.procSec(cpsInit), - ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n")) + p.procSec(cpsLocals).addf("\tNimThreadVars* NimTV_;$n", []) + p.procSec(cpsInit).add( + ropecg(p.module, "\tNimTV_ = (NimThreadVars*) #GetThreadLocalVars();$n", [])) proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = if emulatedThreadVars(m.config): @@ -30,23 +30,23 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = # allocator for it :-( if not containsOrIncl(m.g.nimtvDeclared, s.id): m.g.nimtvDeps.add(s.loc.t) - addf(m.g.nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r]) + m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r]) else: - if isExtern: add(m.s[cfsVars], "extern ") - if optThreads in m.config.globalOptions: add(m.s[cfsVars], "NIM_THREADVAR ") - add(m.s[cfsVars], getTypeDesc(m, s.loc.t)) - addf(m.s[cfsVars], " $1;$n", [s.loc.r]) + if isExtern: m.s[cfsVars].add("extern ") + if optThreads in m.config.globalOptions: m.s[cfsVars].add("NIM_THREADVAR ") + m.s[cfsVars].add(getTypeDesc(m, s.loc.t)) + m.s[cfsVars].addf(" $1;$n", [s.loc.r]) proc generateThreadLocalStorage(m: BModule) = 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) - addf(m.s[cfsSeqTypes], "typedef struct {$1} NimThreadVars;$n", [m.g.nimtv]) + m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv]) proc generateThreadVarsSize(m: BModule) = if m.g.nimtv != nil: let externc = if m.config.cmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags: "extern \"C\" " else: "" - addf(m.s[cfsProcs], + m.s[cfsProcs].addf( "$#NI NimThreadVarsSize(){return (NI)sizeof(NimThreadVars);}$n", [externc.rope]) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index c69bb2c805cb..63b1cb88a6df 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -16,6 +16,9 @@ type p: BProc visitorFrmt: string +const + visitorFrmt = "#nimGCvisit((void*)$1, $2);$n" + proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) proc genCaseRange(p: BProc, branch: PNode) proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) @@ -25,18 +28,18 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode; if n == nil: return case n.kind of nkRecList: - for i in countup(0, sonsLen(n) - 1): - genTraverseProc(c, accessor, n.sons[i], typ) + for i in 0.. proxy => reloadable + # we call performCodeReload() in proxy to reload only changes in reloadable + # but there is a new import which introduces an importc symbol `socket` + # and a function called in main or proxy uses `socket` as a parameter name. + # That would lead to either needing to reload `proxy` or to overwrite the + # executable file for the main module, which is running (or both!) -> error. + if m.hcrOn or isKeyword(s.name) or m.g.config.cppDefines.contains(res): res.add "_0" result = res.rope s.loc.r = result @@ -94,7 +110,8 @@ proc scopeMangledParam(p: BProc; param: PSym) = const irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation, - tyDistinct, tyRange, tyStatic, tyAlias, tySink, tyInferred} + tyDistinct, tyRange, tyStatic, tyAlias, tySink, + tyInferred, tyOwned} proc typeName(typ: PType): Rope = let typ = typ.skipTypes(irrelevantForBackend) @@ -114,7 +131,7 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope = t = t.lastSon else: break - let typ = if typ.kind in {tyAlias, tySink}: typ.lastSon else: typ + let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.lastSon else: typ if typ.loc.r == nil: typ.loc.r = typ.typeName & $sig else: @@ -135,9 +152,10 @@ proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind = proc mapType(conf: ConfigRef; typ: PType): TCTypeKind = ## Maps a Nim type to a C type case typ.kind - of tyNone, tyStmt: result = ctVoid + of tyNone, tyTyped: result = ctVoid of tyBool: result = ctBool of tyChar: result = ctChar + of tyNil: result = ctPtr of tySet: result = mapSetType(conf, typ) of tyOpenArray, tyArray, tyVarargs, tyUncheckedArray: result = ctArray of tyObject, tyTuple: result = ctStruct @@ -145,7 +163,7 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind = doAssert typ.isResolvedUserTypeClass return mapType(conf, typ.lastSon) of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal, - tyTypeDesc, tyAlias, tySink, tyInferred: + tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned: result = mapType(conf, lastSon(typ)) of tyEnum: if firstOrd(conf, typ) < 0: @@ -157,8 +175,8 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind = of 4: result = ctInt32 of 8: result = ctInt64 else: result = ctInt32 - of tyRange: result = mapType(conf, typ.sons[0]) - of tyPtr, tyVar, tyLent, tyRef, tyOptAsRef: + of tyRange: result = mapType(conf, typ[0]) + of tyPtr, tyVar, tyLent, tyRef: var base = skipTypes(typ.lastSon, typedescInst) case base.kind of tyOpenArray, tyArray, tyVarargs, tyUncheckedArray: result = ctPtrToArray @@ -195,7 +213,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope proc isObjLackingTypeField(typ: PType): bool {.inline.} = result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and - (typ.sons[0] == nil) or isPureObject(typ)) + (typ[0] == nil) or isPureObject(typ)) proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool = # Arrays and sets cannot be returned by a C procedure, because C is @@ -229,12 +247,13 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope = result = tab.getOrDefault(sig) proc addAbiCheck(m: BModule, t: PType, name: Rope) = - if isDefined(m.config, "checkabi"): - addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(m.config, t))]) + if isDefined(m.config, "checkabi") and (let size = getSize(m.config, t); size != szUnknownSize): + m.s[cfsTypeInfo].addf("NIM_CHECK_SIZE($1, $2);$n", [name, rope(size)]) -proc ccgIntroducedPtr(conf: ConfigRef; s: PSym): bool = +proc ccgIntroducedPtr(conf: ConfigRef; s: PSym, retType: PType): bool = var pt = skipTypes(s.typ, typedescInst) assert skResult != s.kind + if tfByRef in pt.flags: return true elif tfByCopy in pt.flags: return false case pt.kind @@ -244,13 +263,18 @@ proc ccgIntroducedPtr(conf: ConfigRef; s: PSym): bool = result = true elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3): result = true # requested anyway - elif (tfFinal in pt.flags) and (pt.sons[0] == nil): + elif retType != nil and retType.kind == tyLent: + result = true + elif (tfFinal in pt.flags) and (pt[0] == nil): result = false # no need, because no subtyping possible else: result = true # ordinary objects are always passed by reference, # otherwise casting doesn't work of tyTuple: - result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options) + if retType != nil and retType.kind == tyLent: + result = true + else: + result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options) else: result = false proc fillResult(conf: ConfigRef; param: PNode) = @@ -263,6 +287,7 @@ proc fillResult(conf: ConfigRef; param: PNode) = proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope = if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone: + useHeader(m, t.sym) result = t.sym.loc.r else: result = rope(literal) @@ -279,6 +304,7 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyString: case detectStrVersion(m) of 2: + discard cgsym(m, "NimStrPayload") discard cgsym(m, "NimStringV2") result = typeNameOrLiteral(m, typ, "NimStringV2") else: @@ -290,11 +316,11 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyNil: result = typeNameOrLiteral(m, typ, "void*") of tyInt..tyUInt64: result = typeNameOrLiteral(m, typ, NumericalTypeToStr[typ.kind]) - of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.sons[0]) + of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ[0]) of tyStatic: if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ) else: internalError(m.config, "tyStatic for getSimpleTypeDesc") - of tyGenericInst, tyAlias, tySink: + of tyGenericInst, tyAlias, tySink, tyOwned: result = getSimpleTypeDesc(m, lastSon typ) else: result = nil @@ -305,7 +331,10 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = addAbiCheck(m, typ, result) proc pushType(m: BModule, typ: PType) = - add(m.typeStack, typ) + for i in 0..high(m.typeStack): + # pointer equality is good enough here: + if m.typeStack[i] == typ: return + m.typeStack.add(typ) proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope = if typ == nil: result = rope("void") @@ -314,15 +343,20 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope = if result == nil: result = cacheGetType(m.typeCache, sig) proc structOrUnion(t: PType): Rope = + let cachedUnion {.global.} = rope("union") + let cachedStruct {.global.} = rope("struct") let t = t.skipTypes({tyAlias, tySink}) - (if tfUnion in t.flags: rope("union") else: rope("struct")) + if tfUnion in t.flags: cachedUnion + else: cachedStruct -proc getForwardStructFormat(m: BModule): string = - if m.compileToCpp: result = "$1 $2;$n" - else: result = "typedef $1 $2 $2;$n" +proc addForwardStructFormat(m: BModule, structOrUnion: Rope, typename: Rope) = + if m.compileToCpp: + m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename] + else: + m.s[cfsForwardTypes].addf "typedef $1 $2 $2;$n", [structOrUnion, typename] proc seqStar(m: BModule): string = - if m.config.selectedGC == gcDestructors: result = "" + if optSeqDestructors in m.config.globalOptions: result = "" else: result = "*" proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = @@ -336,8 +370,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = result = getTypeName(m, typ, sig) m.forwTypeCache[sig] = result if not isImportedType(concrete): - addf(m.s[cfsForwardTypes], getForwardStructFormat(m), - [structOrUnion(typ), result]) + addForwardStructFormat(m, structOrUnion(typ), result) else: pushType(m, concrete) doAssert m.forwTypeCache[sig] == result @@ -356,14 +389,53 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = result = getTypeForward(m, t, hashType(t)) pushType(m, t) of tySequence: - if m.config.selectedGC == gcDestructors: - result = getTypeDescAux(m, t, check) + let sig = hashType(t) + if optSeqDestructors in m.config.globalOptions: + if skipTypes(etB[0], typedescInst).kind == tyEmpty: + internalError(m.config, "cannot map the empty seq type to a C type") + + result = cacheGetType(m.forwTypeCache, sig) + if result == nil: + result = getTypeName(m, t, sig) + if not isImportedType(t): + m.forwTypeCache[sig] = result + addForwardStructFormat(m, rope"struct", result) + let payload = result & "_Content" + addForwardStructFormat(m, rope"struct", payload) + + if cacheGetType(m.typeCache, sig) == nil: + m.typeCache[sig] = result + #echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s + appcg(m, m.s[cfsTypes], + "struct $1 {$N" & + " NI len; $1_Content* p;$N" & + "};$N", [result]) else: - result = getTypeForward(m, t, hashType(t)) & seqStar(m) - pushType(m, t) + result = getTypeForward(m, t, sig) & seqStar(m) + pushType(m, t) else: result = getTypeDescAux(m, t, check) +proc getSeqPayloadType(m: BModule; t: PType): Rope = + var check = initIntSet() + result = getTypeDescWeak(m, t, check) & "_Content" + #result = getTypeForward(m, t, hashType(t)) & "_Content" + +proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) = + let sig = hashType(t) + let result = cacheGetType(m.typeCache, sig) + if result == nil: + discard getTypeDescAux(m, t, check) + else: + # little hack for now to prevent multiple definitions of the same + # Seq_Content: + appcg(m, m.s[cfsTypes], """$N +$3ifndef $2_Content_PP +$3define $2_Content_PP +struct $2_Content { NI cap;#AllocatorObj* allocator;$1 data[SEQ_DECL_SIZE];}; +$3endif$N + """, [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check), result, rope"#"]) + proc paramStorageLoc(param: PSym): TStorageLoc = if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin { tyArray, tyOpenArray, tyVarargs}: @@ -375,56 +447,56 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, check: var IntSet, declareEnvironment=true; weakDep=false) = params = nil - if t.sons[0] == nil or isInvalidReturnType(m.config, t.sons[0]): + if t[0] == nil or isInvalidReturnType(m.config, t[0]): rettype = ~"void" else: - rettype = getTypeDescAux(m, t.sons[0], check) - for i in countup(1, sonsLen(t.n) - 1): - if t.n.sons[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams") - var param = t.n.sons[i].sym + rettype = getTypeDescAux(m, t[0], check) + for i in 1..