Merge pull request #2 from nim-lang/devel

Update
This commit is contained in:
Daniil Yarancev 2018-06-05 21:25:45 +03:00 • committed by GitHub
commit 6426413598
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
598 changed files with 32385 additions and 27063 deletions

View file

@ -8,7 +8,7 @@ stages:
.linux_set_path: &linux_set_path_def
before_script:
- export PATH=$(pwd)/bin:$PATH
- export PATH=$(pwd)/bin${PATH:+:$PATH}
tags:
- linux

View file

@ -30,7 +30,7 @@ before_script:
- sh build.sh
- cd ..
- sed -i -e 's,cc = gcc,cc = clang,' config/nim.cfg
- export PATH=$(pwd)/bin:$PATH
- export PATH=$(pwd)/bin${PATH:+:$PATH}
script:
- nim c koch
- ./koch boot

View file

@ -24,6 +24,7 @@ environment:
# platform: x86
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

View file

@ -1,192 +1,154 @@
## v0.18.0 - dd/mm/yyyy
## v0.19.X - XX/XX/2018
### Changes affecting backwards compatibility
- The stdlib module ``future`` has been renamed to ``sugar``.
- ``macros.callsite`` is now deprecated. Since the introduction of ``varargs``
parameters this became unnecessary.
- Anonymous tuples with a single element can now be written as ``(1,)`` with a
trailing comma. The underlying AST is ``nnkTupleConst(newLit 1)`` for this
example. ``nnkTupleConstr`` is a new node kind your macros need to be able
to deal with!
- Indexing into a ``cstring`` for the JS target is now mapped
to ``charCodeAt``.
- Assignments that would "slice" an object into its supertype are now prevented
at runtime. Use ``ref object`` with inheritance rather than ``object`` with
inheritance to prevent this issue.
- The ``not nil`` type annotation now has to be enabled explicitly
via ``{.experimental: "notnil"}`` as we are still not pleased with how this
feature works with Nim's containers.
- The parser now warns about inconsistent spacing around binary operators as
these can easily be confused with unary operators. This warning will likely
become an error in the future.
- Arrays of char cannot be converted to ``cstring`` anymore, pointers to
arrays of char can! This means ``$`` for arrays can finally exist
in ``system.nim`` and do the right thing.
- ``echo`` now works with strings that contain ``\0`` (the binary zero is not
shown) and ``nil`` strings are equal to empty strings.
- JSON: Deprecated `getBVal`, `getFNum`, and `getNum` in favour to
`getBool`, `getFloat`, `getBiggestInt`. Also `getInt` procedure was added.
- `reExtended` is no longer default for the `re` constructor in the `re`
module.
- The overloading rules changed slightly so that constrained generics are
preferred over unconstrained generics. (Bug #6526)
- It is now possible to forward declare object types so that mutually
recursive types can be created across module boundaries. See
[package level objects](https://nim-lang.org/docs/manual.html#package-level-objects)
for more information.
- The **unary** ``<`` is now deprecated, for ``.. <`` use ``..<`` for other usages
use the ``pred`` proc.
- 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
generic parameters which can be ``BackwardsIndex`` indices. ``BackwardsIndex`` is
produced by ``system.^``.
This means if you overload ``[]`` or ``[]=`` you need to ensure they also work
with ``system.BackwardsIndex`` (if applicable for the accessors).
- ``mod`` and bitwise ``and`` do not produce ``range`` subtypes anymore. This
turned out to be more harmful than helpful and the language is simpler
without this special typing rule.
- Added ``algorithm.rotateLeft``.
- ``rationals.toRational`` now uses an algorithm based on continued fractions.
This means its results are more precise and it can't run into an infinite loop
anymore.
- Added ``typetraits.$`` as an alias for ``typetraits.name``.
- ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv``
what to return if the environment variable does not exist.
- Bodies of ``for`` loops now get their own scope:
```nim
# now compiles:
for i in 0..4:
let i = i + 1
echo i
```
#### Breaking changes in the standard library
- The parsing rules of ``if`` expressions were changed so that multiple
statements are allowed in the branches. We found few code examples that
now fail because of this change, but here is one:
- ``re.split`` for empty regular expressions now yields every character in
the string which is what other programming languages chose to do.
- The returned tuple of ``system.instantiationInfo`` now has a third field
containing the column of the instantiation.
```nim
t[ti] = if exp_negative: '-' else: '+'; inc(ti)
```
- ``cookies.setCookie` no longer assumes UTC for the expiration date.
- ``strutils.formatEng`` does not distinguish between ``nil`` and ``""``
strings anymore for its ``unit`` parameter. Instead the space is controlled
by a new parameter ``useUnitSpace``.
This now needs to be written as:
- ``proc `-`*(a, b: Time): int64`` in the ``times`` module has changed return type
to ``times.Duration`` in order to support higher time resolutions.
The proc is no longer deprecated.
- ``posix.Timeval.tv_sec`` has changed type to ``posix.Time``.
```nim
t[ti] = (if exp_negative: '-' else: '+'); inc(ti)
```
- ``math.`mod` `` for floats now behaves the same as ``mod`` for integers
(previously it used floor division like Python). Use ``math.floorMod`` for the old behavior.
- To make Nim even more robust the system iterators ``..`` and ``countup``
now only accept a single generic type ``T``. This means the following code
doesn't die with an "out of range" error anymore:
#### Breaking changes in the compiler
```nim
var b = 5.Natural
var a = -5
for i in a..b:
echo i
```
- The undocumented ``#? braces`` parsing mode was removed.
- The undocumented PHP backend was removed.
- ``formatFloat``/``formatBiggestFloat`` now support formatting floats with zero
precision digits. The previous ``precision = 0`` behavior (default formatting)
is now available via ``precision = -1``.
- The ``nim doc`` command is now an alias for ``nim doc2``, the second version of
the documentation generator. The old version 1 can still be accessed
via the new ``nim doc0`` command.
- Added ``system.getStackTraceEntries`` that allows you to access the stack
trace in a structured manner without string parsing.
- Added ``sequtils.mapLiterals`` for easier construction of array and tuple
literals.
- Added ``parseutils.parseSaturatedNatural``.
- ``atomic`` and ``generic`` are no longer keywords in Nim. ``generic`` used to be
an alias for ``concept``, ``atomic`` was not used for anything.
- Moved from stdlib into Nimble packages:
- [``basic2d``](https://github.com/nim-lang/basic2d)
_deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
- [``basic3d``](https://github.com/nim-lang/basic3d)
_deprecated: use ``glm``, ``arraymancer``, ``neo``, or another package instead_
- [``gentabs``](https://github.com/lcrees/gentabs)
- [``libuv``](https://github.com/lcrees/libuv)
- [``numeric``](https://github.com/lcrees/polynumeric)
- [``poly``](https://github.com/lcrees/polynumeric)
- [``pdcurses``](https://github.com/lcrees/pdcurses)
- [``romans``](https://github.com/lcrees/romans)
### Library additions
- Added ``system.runnableExamples`` to make examples in Nim's documentation easier
to write and test. The examples are tested as the last step of
``nim doc``.
- Nim's ``rst2html`` command now supports the testing of code snippets via an RST
extension that we called ``:test:``::
- ``re.split`` now also supports the ``maxsplit`` parameter for consistency
with ``strutils.split``.
- Added ``system.toOpenArray`` in order to support zero-copy slicing
operations. This is currently not yet available for the JavaScript target.
- Added ``getCurrentDir``, ``findExe``, ``cpDir`` and ``mvDir`` procs to
``nimscript``.
- The ``times`` module now supports up to nanosecond time resolution when available.
- Added the type ``times.Duration`` for representing fixed durations of time.
- Added the proc ``times.convert`` for converting between different time units,
e.g days to seconds.
- Added the proc ``algorithm.binarySearch[T, K]`` with the ```cmp``` parameter.
- Added the proc ``algorithm.upperBound``.
- Added inverse hyperbolic functions, ``math.arcsinh``, ``math.arccosh`` and ``math.arctanh`` procs.
- Added cotangent, secant and cosecant procs ``math.cot``, ``math.sec`` and ``math.csc``; and their hyperbolic, inverse and inverse hyperbolic functions, ``math.coth``, ``math.sech``, ``math.csch``, ``math.arccot``, ``math.arcsec``, ``math.arccsc``, ``math.arccoth``, ``math.arcsech`` and ``math.arccsch`` procs.
- Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division.
- Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals.
- Added the proc ``math.prod`` for product of elements in openArray.
```rst
.. code-block:: nim
:test:
# shows how the 'if' statement works
if true: echo "yes"
```
- The ``[]`` proc for strings now raises an ``IndexError`` exception when
the specified slice is out of bounds. See issue
[#6223](https://github.com/nim-lang/Nim/issues/6223) for more details.
You can use ``substr(str, start, finish)`` to get the old behaviour back,
see [this commit](https://github.com/nim-lang/nimbot/commit/98cc031a27ea89947daa7f0bb536bcf86462941f) for an example.
- ``strutils.split`` and ``strutils.rsplit`` with an empty string and a
separator now returns that empty string.
See issue [#4377](https://github.com/nim-lang/Nim/issues/4377).
- The experimental overloading of the dot ``.`` operators now take
an ``untyped``` parameter as the field name, it used to be
a ``static[string]``. You can use ``when defined(nimNewDot)`` to make
your code work with both old and new Nim versions.
See [special-operators](https://nim-lang.org/docs/manual.html#special-operators)
for more information.
- Added ``macros.unpackVarargs``.
- The memory manager now uses a variant of the TLSF algorithm that has much
better memory fragmentation behaviour. According
to [http://www.gii.upv.es/tlsf/](http://www.gii.upv.es/tlsf/) the maximum
fragmentation measured is lower than 25%. As a nice bonus ``alloc`` and
``dealloc`` became O(1) operations.
- The behavior of ``$`` has been changed for all standard library collections. The
collection-to-string implementations now perform proper quoting and escaping of
strings and chars.
- The ``random`` procs in ``random.nim`` have all been deprecated. Instead use
the new ``rand`` procs. The module now exports the state of the random
number generator as type ``Rand`` so multiple threads can easily use their
own random number generators that do not require locking. For more information
about this rename see issue [#6934](https://github.com/nim-lang/Nim/issues/6934)
- The compiler is now more consistent in its treatment of ambiguous symbols:
Types that shadow procs and vice versa are marked as ambiguous (bug #6693).
- ``yield`` (or ``await`` which is mapped to ``yield``) never worked reliably
in an array, seq or object constructor and is now prevented at compile-time.
- For string formatting / interpolation a new module
called [strformat](https://nim-lang.org/docs/strformat.html) has been added
to the stdlib.
- codegenDecl pragma now works for the JavaScript backend. It returns an empty string for
function return type placeholders.
- Asynchronous programming for the JavaScript backend using the `asyncjs` module.
- Extra semantic checks for procs with noreturn pragma: return type is not allowed,
statements after call to noreturn procs are no longer allowed.
- Noreturn proc calls and raising exceptions branches are now skipped during common type
deduction in if and case expressions. The following code snippets now compile:
```nim
import strutils
let str = "Y"
let a = case str:
of "Y": true
of "N": false
else: raise newException(ValueError, "Invalid boolean")
let b = case str:
of nil, "": raise newException(ValueError, "Invalid boolean")
elif str.startsWith("Y"): true
elif str.startsWith("N"): false
else: false
let c = if str == "Y": true
elif str == "N": false
else:
echo "invalid bool"
quit("this is the end")
```
- Proc [toCountTable](https://nim-lang.org/docs/tables.html#toCountTable,openArray[A]) now produces a `CountTable` with values correspoding to the number of occurrences of the key in the input. It used to produce a table with all values set to `1`.
### Library changes
Counting occurrences in a sequence used to be:
- ``macros.astGenRepr``, ``macros.lispRepr`` and ``macros.treeRepr``
now escapes the content of string literals consistently.
- ``macros.NimSym`` and ``macros.NimIdent`` is now deprecated in favor
of the more general ``NimNode``.
- ``macros.getImpl`` now includes the pragmas of types, instead of omitting them.
- ``macros.hasCustomPragma`` and ``macros.getCustomPragmaVal`` now
also support ``ref`` and ``ptr`` types, pragmas on types and variant
fields.
- ``system.SomeReal`` is now called ``SomeFloat`` for consistency and
correctness.
- ``algorithm.smartBinarySearch`` and ``algorithm.binarySearch`` is
now joined in ``binarySearch``. ``smartbinarySearch`` is now
deprecated.
- The `terminal` module now exports additional procs for generating ANSI color
codes as strings.
- Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc.
- An exception raised from ``test`` block of ``unittest`` now shows its type in
the error message
- The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated.
```nim
let mySeq = @[1, 2, 1, 3, 1, 4]
var myCounter = initCountTable[int]()
### Language additions
for item in mySeq:
myCounter.inc item
```
- Dot calls combined with explicit generic instantiations can now be written
as ``x.y[:z]`` which is transformed into ``y[z](x)`` by the parser.
- ``func`` is now an alias for ``proc {.noSideEffect.}``.
- In order to make ``for`` loops and iterators more flexible to use Nim now
supports so called "for-loop macros". See
the `manual <manual.html#macros-for-loop-macros>`_ for more details.
Now, you can simply do:
### Language changes
```nim
let
mySeq = @[1, 2, 1, 3, 1, 4]
myCounter = mySeq.toCountTable()
```
- The `importcpp` pragma now allows importing the listed fields of generic
C++ types. Support for numeric parameters have also been added through
the use of `static[T]` types.
(#6415)
- Added support for casting between integers of same bitsize in VM (compile time and nimscript).
This allow to among other things to reinterpret signed integers as unsigned.
- Native C++ exceptions can now be imported with `importcpp` pragma.
Imported exceptions can be raised and caught just like Nim exceptions.
More details in language manual.
- ``nil`` for strings/seqs is finally gone. Instead the default value for
these is ``"" / @[]``.
- Accessing the binary zero terminator in Nim's native strings
is now invalid. Internally a Nim string still has the trailing zero for
zero-copy interoperability with ``cstring``. Compile your code with the
new switch ``--laxStrings:on`` if you need a transition period.
- The command syntax now supports keyword arguments after the first comma.
- Thread-local variables can now be declared inside procs. This implies all
the effects of the `global` pragma.
- Nim now supports `except` clause in the export statement.
### Tool changes
- ``jsondoc2`` has been renamed ``jsondoc``, similar to how ``doc2`` was renamed
``doc``. The old ``jsondoc`` can still be invoked with ``jsondoc0``.
### Compiler changes
- The VM's instruction count limit was raised to 1 billion instructions in
order to support more complex computations at compile-time.
- Support for hot code reloading has been implemented for the JavaScript
target. To use it, compile your code with `--hotCodeReloading:on` and use a
helper library such as LiveReload or BrowserSync.
- A new compiler option `--cppCompileToNamespace` puts the generated C++ code
into the namespace "Nim" in order to avoid naming conflicts with existing
C++ code. This is done for all Nim code - internal and exported.
- Added ``macros.getProjectPath`` and ``ospaths.putEnv`` procs to Nim's virtual
machine.
- The ``deadCodeElim`` option is now always turned on and the switch has no
effect anymore, but is recognized for backwards compatibility.
- ``experimental`` is now a pragma / command line switch that can enable specific
language extensions, it is not an all-or-nothing switch anymore.
### Bugfixes

View file

@ -6,7 +6,7 @@ cd csources
sh build.sh
cd ..
# Add Nim to the PATH
export PATH=$(pwd)/bin:$PATH
export PATH=$(pwd)/bin${PATH:+:$PATH}
# Bootstrap.
nim -v
nim c koch

View file

@ -7,7 +7,7 @@ apt-get install -y -qq build-essential git libcurl4-openssl-dev libsdl1.2-dev li
gcc -v
export PATH=$(pwd)/bin:$PATH
export PATH=$(pwd)/bin${PATH:+:$PATH}
# Nimble deps
nim e install_nimble.nims

View file

@ -34,10 +34,10 @@ proc isPartOfAux(n: PNode, b: PType, marker: var IntSet): TAnalysisResult =
of nkOfBranch, nkElse:
result = isPartOfAux(lastSon(n.sons[i]), b, marker)
if result == arYes: return
else: internalError("isPartOfAux(record case branch)")
else: discard "isPartOfAux(record case branch)"
of nkSym:
result = isPartOfAux(n.sym.typ, b, marker)
else: internalError(n.info, "isPartOfAux()")
else: discard
proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
result = arNo
@ -49,7 +49,7 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
if a.sons[0] != nil:
result = isPartOfAux(a.sons[0].skipTypes(skipPtrs), b, marker)
if result == arNo: result = isPartOfAux(a.n, b, marker)
of tyGenericInst, tyDistinct, tyAlias:
of tyGenericInst, tyDistinct, tyAlias, tySink:
result = isPartOfAux(lastSon(a), b, marker)
of tyArray, tySet, tyTuple:
for i in countup(0, sonsLen(a) - 1):

View file

@ -10,7 +10,7 @@
# abstract syntax tree + symbol table
import
msgs, hashes, nversion, options, strutils, securehash, ropes, idents,
msgs, hashes, nversion, options, strutils, std / sha1, ropes, idents,
intsets, idgen
type
@ -221,7 +221,8 @@ type
nkGotoState, # used for the state machine (for iterators)
nkState, # give a label to a code section (for iterators)
nkBreakState, # special break statement for easier code generation
nkFuncDef # a func
nkFuncDef, # a func
nkTupleConstr # a tuple constructor
TNodeKinds* = set[TNodeKind]
@ -261,7 +262,8 @@ type
# variable is a thread variable
sfCompileTime, # proc can be evaluated at compile time
sfConstructor, # proc is a C++ constructor
sfDeadCodeElim, # dead code elimination for the module is turned on
sfDispatcher, # copied method symbol is the dispatcher
# deprecated and unused, except for the con
sfBorrow, # proc is borrowed
sfInfixCall, # symbol needs infix call syntax in target language;
# for interfacing with C++, JS
@ -274,10 +276,9 @@ type
TSymFlags* = set[TSymFlag]
const
sfDispatcher* = sfDeadCodeElim # copied method symbol is the dispatcher
sfNoInit* = sfMainModule # don't generate code to init the variable
sfImmediate* = sfDeadCodeElim
sfImmediate* = sfDispatcher
# macro or template is immediately expanded
# without considering any possible overloads
sfAllUntyped* = sfVolatile # macro or template is immediately expanded \
@ -305,6 +306,7 @@ const
sfEscapes* = sfProcvar # param escapes
sfBase* = sfDiscriminant
sfIsSelf* = sfOverriden # param is 'self'
sfCustomPragma* = sfRegister # symbol is custom pragma template
const
# getting ready for the future expr/stmt merge
@ -354,7 +356,7 @@ type
tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
tyFloat, tyFloat32, tyFloat64, tyFloat128,
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
tyOptAsRef, tyUnused1, tyUnused2,
tyOptAsRef, tySink, tyLent,
tyVarargs,
tyUnused,
tyProxy # used as errornous type (for idetools)
@ -631,16 +633,17 @@ type
mCpuEndian, mHostOS, mHostCPU, mBuildOS, mBuildCPU, mAppType,
mNaN, mInf, mNegInf,
mCompileOption, mCompileOptionArg,
mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind,
mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel,
mNKind, mNSymKind
mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr,
mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent,
mNBindSym, mLocals, mNCallSite,
mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl,
mNHint, mNWarning, mNError,
mInstantiationInfo, mGetTypeInfo, mNGenSym,
mNimvm, mIntDefine, mStrDefine, mRunnableExamples,
mException
mException, mBuiltinType
# things that we can evaluate safely at compile time, even if not asked for it:
const
@ -731,7 +734,7 @@ type
locOther # location is something other
TLocFlag* = enum
lfIndirect, # backend introduced a pointer
lfFullExternalName, # only used when 'gCmd == cmdPretty': Indicates
lfFullExternalName, # only used when 'conf.cmd == cmdPretty': Indicates
# that the symbol has been imported via 'importc: "fullname"' and
# no format string.
lfNoDeepCopy, # no need for a deep copy
@ -846,6 +849,8 @@ type
constraint*: PNode # additional constraints like 'lit|result'; also
# misused for the codegenDecl pragma in the hope
# it won't cause problems
# for skModule the string literal to output for
# deprecated modules.
when defined(nimsuggest):
allUsages*: seq[TLineInfo]
@ -940,13 +945,13 @@ const
tyGenericParam}
StructuralEquivTypes*: TTypeKinds = {tyNil, tyTuple, tyArray,
tySet, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc, tyOpenArray,
tySet, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc, tyOpenArray,
tyVarargs}
ConcreteTypes*: TTypeKinds = { # types of the expr that may occur in::
# var x = expr
tyBool, tyChar, tyEnum, tyArray, tyObject,
tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tySequence, tyProc,
tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc,
tyPointer,
tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
tyUInt..tyUInt64}
@ -978,7 +983,9 @@ const
nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
nkClosedSymChoice}
nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
nkLiterals* = {nkCharLit..nkTripleStrLit}
nkFloatLiterals* = {nkFloatLit..nkFloat128Lit}
nkLambdaKinds* = {nkLambda, nkDo}
declarativeDefs* = {nkProcDef, nkFuncDef, nkMethodDef, nkIteratorDef, nkConverterDef}
procDefs* = nkLambdaKinds + declarativeDefs
@ -1035,9 +1042,9 @@ proc newNode*(kind: TNodeKind): PNode =
new(result)
result.kind = kind
#result.info = UnknownLineInfo() inlined:
result.info.fileIndex = int32(-1)
result.info.fileIndex = InvalidFileIdx
result.info.col = int16(-1)
result.info.line = int16(-1)
result.info.line = uint16(0)
when defined(useNodeIds):
result.id = gNodeId
if result.id == nodeIdToDebug:
@ -1071,14 +1078,14 @@ template previouslyInferred*(t: PType): PType =
if t.sons.len > 1: t.lastSon else: nil
proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
info: TLineInfo): 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 = gOptions
result.options = options
result.owner = owner
result.offset = -1
result.id = getID()
@ -1088,7 +1095,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym,
# writeStacktrace()
# MessageOut(name.s & " has id: " & toString(result.id))
var emptyNode* = newNode(nkEmpty)
var emptyNode* = newNode(nkEmpty) # XXX global variable here!
# There is a single empty node that is shared! Do not overwrite it!
proc isMetaType*(t: PType): bool =
@ -1109,13 +1116,13 @@ proc linkTo*(s: PSym, t: PType): PSym {.discardable.} =
s.typ = t
result = s
template fileIdx*(c: PSym): int32 =
template fileIdx*(c: PSym): FileIndex =
# XXX: this should be used only on module symbols
c.position.int32
c.position.FileIndex
template filename*(c: PSym): string =
# XXX: this should be used only on module symbols
c.position.int32.toFilename
c.position.FileIndex.toFilename
proc appendToModule*(m: PSym, n: PNode) =
## The compiler will use this internally to add nodes that will be
@ -1318,7 +1325,7 @@ proc copyType*(t: PType, owner: PSym, keepId: bool): PType =
proc exactReplica*(t: PType): PType = copyType(t, t.owner, true)
proc copySym*(s: PSym, keepId: bool = false): PSym =
result = newSym(s.kind, s.name, s.owner, s.info)
result = newSym(s.kind, s.name, s.owner, s.info, s.options)
#result.ast = nil # BUGFIX; was: s.ast which made problems
result.typ = s.typ
if keepId:
@ -1337,8 +1344,9 @@ proc copySym*(s: PSym, keepId: bool = false): PSym =
if result.kind in {skVar, skLet, skField}:
result.guard = s.guard
proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo): PSym =
result = newSym(s.kind, newIdent, s.owner, info)
proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo;
options: TOptions): PSym =
result = newSym(s.kind, newIdent, s.owner, info, options)
# keep ID!
result.ast = s.ast
result.id = s.id
@ -1427,7 +1435,7 @@ proc propagateToOwner*(owner, elem: PType) =
owner.flags.incl tfHasMeta
if tfHasAsgn in elem.flags:
let o2 = owner.skipTypes({tyGenericInst, tyAlias})
let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink})
if o2.kind in {tyTuple, tyObject, tyArray,
tySequence, tyOpt, tySet, tyDistinct}:
o2.flags.incl tfHasAsgn
@ -1435,7 +1443,7 @@ proc propagateToOwner*(owner, elem: PType) =
if owner.kind notin {tyProc, tyGenericInst, tyGenericBody,
tyGenericInvocation, tyPtr}:
let elemB = elem.skipTypes({tyGenericInst, tyAlias})
let elemB = elem.skipTypes({tyGenericInst, tyAlias, tySink})
if elemB.isGCedMem or tfHasGCedMem in elemB.flags:
# for simplicity, we propagate this flag even to generics. We then
# ensure this doesn't bite us in sempass2.
@ -1474,7 +1482,7 @@ proc copyNode*(src: PNode): PNode =
echo "COMES FROM ", src.id
case src.kind
of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal
of nkFloatLiterals: result.floatVal = src.floatVal
of nkSym: result.sym = src.sym
of nkIdent: result.ident = src.ident
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
@ -1493,7 +1501,7 @@ proc shallowCopy*(src: PNode): PNode =
echo "COMES FROM ", src.id
case src.kind
of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal
of nkFloatLiterals: result.floatVal = src.floatVal
of nkSym: result.sym = src.sym
of nkIdent: result.ident = src.ident
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
@ -1513,7 +1521,7 @@ proc copyTree*(src: PNode): PNode =
echo "COMES FROM ", src.id
case src.kind
of nkCharLit..nkUInt64Lit: result.intVal = src.intVal
of nkFloatLit..nkFloat128Lit: result.floatVal = src.floatVal
of nkFloatLiterals: result.floatVal = src.floatVal
of nkSym: result.sym = src.sym
of nkIdent: result.ident = src.ident
of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
@ -1557,15 +1565,17 @@ proc getInt*(a: PNode): BiggestInt =
case a.kind
of nkCharLit..nkUInt64Lit: result = a.intVal
else:
internalError(a.info, "getInt")
result = 0
#internalError(a.info, "getInt")
doAssert false, "getInt"
#result = 0
proc getFloat*(a: PNode): BiggestFloat =
case a.kind
of nkFloatLit..nkFloat128Lit: result = a.floatVal
of nkFloatLiterals: result = a.floatVal
else:
internalError(a.info, "getFloat")
result = 0.0
doAssert false, "getFloat"
#internalError(a.info, "getFloat")
#result = 0.0
proc getStr*(a: PNode): string =
case a.kind
@ -1574,16 +1584,18 @@ proc getStr*(a: PNode): string =
# let's hope this fixes more problems than it creates:
result = nil
else:
internalError(a.info, "getStr")
result = ""
doAssert false, "getStr"
#internalError(a.info, "getStr")
#result = ""
proc getStrOrChar*(a: PNode): string =
case a.kind
of nkStrLit..nkTripleStrLit: result = a.strVal
of nkCharLit..nkUInt64Lit: result = $chr(int(a.intVal))
else:
internalError(a.info, "getStrOrChar")
result = ""
doAssert false, "getStrOrChar"
#internalError(a.info, "getStrOrChar")
#result = ""
proc isGenericRoutine*(s: PSym): bool =
case s.kind
@ -1608,6 +1620,19 @@ proc originatingModule*(s: PSym): PSym =
proc isRoutine*(s: PSym): bool {.inline.} =
result = s.kind in skProcKinds
proc isCompileTimeProc*(s: PSym): bool {.inline.} =
result = s.kind == skMacro or
s.kind == skProc and sfCompileTime in s.flags
proc requiredParams*(s: PSym): int =
# Returns the number of required params (without default values)
# XXX: Perhaps we can store this in the `offset` field of the
# symbol instead?
for i in 1 ..< s.typ.len:
if s.typ.n[i].sym.ast != nil:
return i - 1
return s.typ.len - 1
proc hasPattern*(s: PSym): bool {.inline.} =
result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty
@ -1615,7 +1640,7 @@ iterator items*(n: PNode): PNode =
for i in 0..<n.safeLen: yield n.sons[i]
iterator pairs*(n: PNode): tuple[i: int, n: PNode] =
for i in 0..<n.len: yield (i, n.sons[i])
for i in 0..<n.safeLen: yield (i, n.sons[i])
proc isAtom*(n: PNode): bool {.inline.} =
result = n.kind >= nkNone and n.kind <= nkNilLit
@ -1655,6 +1680,33 @@ proc toObject*(typ: PType): PType =
if result.kind == tyRef:
result = result.lastSon
proc isException*(t: PType): bool =
# check if `y` is object type and it inherits from Exception
assert(t != nil)
if t.kind != tyObject:
return false
var base = t
while base != nil:
if base.sym != nil and base.sym.magic == mException:
return true
base = base.lastSon
return false
proc isImportedException*(t: PType; conf: ConfigRef): bool =
assert(t != nil)
if optNoCppExceptions in conf.globalOptions:
return false
let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
if base.sym != nil and sfCompileToCpp in base.sym.flags:
result = true
proc isInfixAs*(n: PNode): bool =
return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == getIdent("as").id
proc findUnresolvedStatic*(n: PNode): PNode =
if n.kind == nkSym and n.typ.kind == tyStatic and n.typ.n == nil:
return n

View file

@ -69,22 +69,22 @@ proc debug*(n: PNode) {.deprecated.}
template mdbg*: bool {.dirty.} =
when compiles(c.module):
c.module.fileIdx == gProjectMainIdx
c.module.fileIdx.int32 == c.config.projectMainIdx
elif compiles(c.c.module):
c.c.module.fileIdx == gProjectMainIdx
c.c.module.fileIdx.int32 == c.c.config.projectMainIdx
elif compiles(m.c.module):
m.c.module.fileIdx == gProjectMainIdx
m.c.module.fileIdx.int32 == m.c.config.projectMainIdx
elif compiles(cl.c.module):
cl.c.module.fileIdx == gProjectMainIdx
cl.c.module.fileIdx.int32 == cl.c.config.projectMainIdx
elif compiles(p):
when compiles(p.lex):
p.lex.fileIdx == gProjectMainIdx
p.lex.fileIdx.int32 == p.lex.config.projectMainIdx
else:
p.module.module.fileIdx == gProjectMainIdx
p.module.module.fileIdx.int32 == p.config.projectMainIdx
elif compiles(m.module.fileIdx):
m.module.fileIdx == gProjectMainIdx
m.module.fileIdx.int32 == m.config.projectMainIdx
elif compiles(L.fileIdx):
L.fileIdx == gProjectMainIdx
L.fileIdx.int32 == L.config.projectMainIdx
else:
error()
@ -180,7 +180,7 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym =
result = lookupInRecord(n.sons[i], field)
if result != nil: return
of nkRecCase:
if (n.sons[0].kind != nkSym): internalError(n.info, "lookupInRecord")
if (n.sons[0].kind != nkSym): return nil
result = lookupInRecord(n.sons[0], field)
if result != nil: return
for i in countup(1, sonsLen(n) - 1):
@ -188,10 +188,10 @@ proc lookupInRecord(n: PNode, field: PIdent): PSym =
of nkOfBranch, nkElse:
result = lookupInRecord(lastSon(n.sons[i]), field)
if result != nil: return
else: internalError(n.info, "lookupInRecord(record case branch)")
else: return nil
of nkSym:
if n.sym.name.id == field.id: result = n.sym
else: internalError(n.info, "lookupInRecord()")
else: return nil
proc getModule(s: PSym): PSym =
result = s
@ -203,7 +203,7 @@ proc getSymFromList(list: PNode, ident: PIdent, start: int = 0): PSym =
if list.sons[i].kind == nkSym:
result = list.sons[i].sym
if result.name.id == ident.id: return
else: internalError(list.info, "getSymFromList")
else: return nil
result = nil
proc hashNode(p: RootRef): Hash =

View file

@ -28,6 +28,7 @@ proc bitSetExcl*(x: var TBitSet, elem: BiggestInt)
proc bitSetIn*(x: TBitSet, e: BiggestInt): bool
proc bitSetEquals*(x, y: TBitSet): bool
proc bitSetContains*(x, y: TBitSet): bool
proc bitSetCard*(x: TBitSet): BiggestInt
# implementation
proc bitSetIn(x: TBitSet, e: BiggestInt): bool =
@ -69,3 +70,28 @@ proc bitSetContains(x, y: TBitSet): bool =
if (x[i] and not y[i]) != int8(0):
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]
proc countSetBits(x: int8): int8 =
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)
arr
proc bitSetCard(x: TBitSet): BiggestInt =
for it in x:
result.inc populationCount[it]

View file

@ -32,7 +32,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
if d.k == locNone: getTemp(p, typ.sons[0], d, needsInit=true)
elif d.k notin {locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var:
resetLoc(p, d)
discard "resetLoc(p, d)"
add(pl, addrLoc(d))
add(pl, ~");$n")
line(p, cpsStmts, pl)
@ -71,7 +71,7 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool =
if n.sym.kind in {skVar, skResult, skTemp, skLet} and p.prc != nil:
result = p.prc.id == n.sym.owner.id
of nkDotExpr, nkBracketExpr:
if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyPtr,tyRef}:
if skipTypes(n.sons[0].typ, abstractInst).kind notin {tyVar,tyLent,tyPtr,tyRef}:
result = isInCurrentFrame(p, n.sons[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
result = isInCurrentFrame(p, n.sons[1])
@ -83,6 +83,8 @@ proc isInCurrentFrame(p: BProc, n: PNode): bool =
result = isInCurrentFrame(p, n.sons[0])
else: discard
proc genIndexCheck(p: BProc; arr, idx: TLoc)
proc openArrayLoc(p: BProc, n: PNode): Rope =
var a: TLoc
@ -93,18 +95,28 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
initLocExpr(p, q[1], a)
initLocExpr(p, q[2], b)
initLocExpr(p, q[3], c)
let fmt =
case skipTypes(a.t, abstractVar+{tyPtr}).kind
of tyOpenArray, tyVarargs, tyArray:
"($1)+($2), ($3)-($2)+1"
of tyString, tySequence:
if skipTypes(n.typ, abstractInst).kind == tyVar and
not compileToCpp(p.module):
"(*$1)->data+($2), ($3)-($2)+1"
else:
"$1->data+($2), ($3)-($2)+1"
else: (internalError("openArrayLoc: " & typeToString(a.t)); "")
result = fmt % [rdLoc(a), rdLoc(b), rdLoc(c)]
# but first produce the required index checks:
if optBoundsCheck in p.options:
genIndexCheck(p, a, b)
genIndexCheck(p, a, c)
let ty = skipTypes(a.t, abstractVar+{tyPtr})
case ty.kind
of tyArray:
let first = firstOrd(ty)
if first == 0:
result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)]
else:
result = "($1)+(($2)-($4)), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), intLiteral(first)]
of tyOpenArray, tyVarargs:
result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)]
of tyString, tySequence:
if skipTypes(n.typ, abstractInst).kind == tyVar and
not compileToCpp(p.module):
result = "(*$1)->data+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)]
else:
result = "$1->data+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)]
else:
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
else:
initLocExpr(p, n, a)
case skipTypes(a.t, abstractVar).kind
@ -113,25 +125,25 @@ proc openArrayLoc(p: BProc, n: PNode): Rope =
of tyString, tySequence:
if skipTypes(n.typ, abstractInst).kind == tyVar and
not compileToCpp(p.module):
result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
else:
result = "$1->data, $1->$2" % [a.rdLoc, lenField(p)]
result = "$1->data, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p)]
of tyArray:
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(a.t))]
of tyPtr, tyRef:
case lastSon(a.t).kind
of tyString, tySequence:
result = "(*$1)->data, (*$1)->$2" % [a.rdLoc, lenField(p)]
result = "(*$1)->data, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p)]
of tyArray:
result = "$1, $2" % [rdLoc(a), rope(lengthOrd(lastSon(a.t)))]
else:
internalError("openArrayLoc: " & typeToString(a.t))
else: internalError("openArrayLoc: " & typeToString(a.t))
internalError(p.config, "openArrayLoc: " & typeToString(a.t))
else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} =
var a: TLoc
initLocExpr(p, n.sons[0], a)
result = "$1->data" % [a.rdLoc]
result = "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc]
proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope =
var a: TLoc
@ -228,7 +240,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
getTemp(p, typ.sons[0], d, needsInit=true)
elif d.k notin {locTemp} and not hasNoInit(ri):
# reset before pass as 'result' var:
resetLoc(p, d)
discard "resetLoc(p, d)"
add(pl, addrLoc(d))
genCallPattern()
else:
@ -261,7 +273,7 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
else:
if tfVarargs notin typ.flags:
localError(ri.info, "wrong argument count")
localError(p.config, ri.info, "wrong argument count")
result = nil
else:
result = genArgNoParam(p, ri.sons[i])
@ -325,13 +337,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 i < sonsLen(typ)
internalAssert p.config, i < sonsLen(typ)
assert(typ.n.sons[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})
let t = typ.sons[i].skipTypes({tyGenericInst, tyAlias, tySink})
if t.kind == tyVar:
let x = if ri.kind == nkHiddenAddr: ri[0] else: ri
if x.typ.kind == tyPtr:
@ -382,7 +394,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType): Rope =
result.add genOtherArg(p, ri, k, typ)
result.add(~")")
else:
localError(ri.info, "call expression expected for C++ pattern")
localError(p.config, ri.info, "call expression expected for C++ pattern")
inc i
elif pat[i+1] == '.':
result.add genThisArg(p, ri, j, typ)
@ -420,7 +432,7 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
assert(sonsLen(typ) == sonsLen(typ.n))
# don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data
internalAssert pat != nil
internalAssert p.config, pat != nil
if pat.contains({'#', '(', '@', '\''}):
var pl = genPatternCall(p, ri, pat, typ)
# simpler version of 'fixupCall' that works with the pl+params combination:
@ -469,7 +481,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
# don't call '$' here for efficiency:
let pat = ri.sons[0].sym.loc.r.data
internalAssert pat != nil
internalAssert p.config, pat != nil
var start = 3
if ' ' in pat:
start = 1
@ -489,7 +501,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
for i in countup(start, length-1):
assert(sonsLen(typ) == sonsLen(typ.n))
if i >= sonsLen(typ):
internalError(ri.info, "varargs for objective C method?")
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, ~" ")
@ -527,7 +539,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
line(p, cpsStmts, pl)
proc genCall(p: BProc, e: PNode, d: var TLoc) =
if e.sons[0].typ.skipTypes({tyGenericInst, tyAlias}).callConv == ccClosure:
if e.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}).callConv == ccClosure:
genClosureCall(p, nil, e, d)
elif e.sons[0].kind == nkSym and sfInfixCall in e.sons[0].sym.flags:
genInfixCall(p, nil, e, d)
@ -538,7 +550,7 @@ proc genCall(p: BProc, e: PNode, d: var TLoc) =
postStmtActions(p)
proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) =
if ri.sons[0].typ.skipTypes({tyGenericInst, tyAlias}).callConv == ccClosure:
if ri.sons[0].typ.skipTypes({tyGenericInst, tyAlias, tySink}).callConv == ccClosure:
genClosureCall(p, le, ri, d)
elif ri.sons[0].kind == nkSym and sfInfixCall in ri.sons[0].sym.flags:
genInfixCall(p, le, ri, d)

File diff suppressed because it is too large Load diff

91
compiler/ccgliterals.nim Normal file
View file

@ -0,0 +1,91 @@
#
#
# The Nim Compiler
# (c) Copyright 2018 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This include file contains the logic to produce constant string
## and seq literals. The code here is responsible that
## ``const x = ["a", "b"]`` works without hidden runtime creation code.
## The price is that seqs and strings are not purely a library
## implementation.
template detectVersion(field, corename) =
if m.g.field == 0:
let core = getCompilerProc(m.g.graph, corename)
if core == nil or core.kind != skConst:
m.g.field = 1
else:
m.g.field = int ast.getInt(core.ast)
result = m.g.field
proc detectStrVersion(m: BModule): int =
detectVersion(strVersion, "nimStrVersion")
proc detectSeqVersion(m: BModule): int =
detectVersion(seqVersion, "nimSeqVersion")
# ----- Version 1: GC'ed strings and seqs --------------------------------
proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope =
discard cgsym(m, "TGenericSeq")
result = getTempName(m)
addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n",
[result, makeCString(s), rope(len(s))])
proc genStringLiteralV1(m: BModule; n: PNode): Rope =
if s.isNil:
result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", [])
else:
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
if id == m.labels:
# string literal not found in the cache:
result = ropecg(m, "((#NimStringDesc*) &$1)",
[genStringLiteralDataOnlyV1(m, n.strVal)])
else:
result = ropecg(m, "((#NimStringDesc*) &$1$2)",
[m.tmpBase, rope(id)])
# ------ Version 2: destructor based strings and seqs -----------------------
proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope =
result = getTempName(m)
addf(m.s[cfsData], " static const NIM_CHAR $1[$2] = $3;$n",
[result, rope(len(s)+1), makeCString(s)])
proc genStringLiteralV2(m: BModule; n: PNode): Rope =
let id = nodeTableTestOrSet(m.dataCache, n, m.labels)
if id == m.labels:
# 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, $2, $3};$n",
[result, rope(len(n.strVal)+1), pureLit])
else:
result = m.tmpBase & rope(id)
# ------ Version selector ---------------------------------------------------
proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope =
case detectStrVersion(m)
of 0, 1: result = genStringLiteralDataOnlyV1(m, s)
of 2: result = genStringLiteralDataOnlyV2(m, s)
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)
else:
localError(m.config, n.info, "cannot determine how to produce code for string literal")

View file

@ -45,28 +45,28 @@ const
]
NimMergeEndMark = "/*\tNIM_merge_END:*/"
proc genSectionStart*(fs: TCFileSection): Rope =
if compilationCachePresent:
proc genSectionStart*(fs: TCFileSection; conf: ConfigRef): Rope =
if compilationCachePresent(conf):
result = rope(tnl)
add(result, "/*\t")
add(result, CFileSectionNames[fs])
add(result, ":*/")
add(result, tnl)
proc genSectionEnd*(fs: TCFileSection): Rope =
if compilationCachePresent:
proc genSectionEnd*(fs: TCFileSection; conf: ConfigRef): Rope =
if compilationCachePresent(conf):
result = rope(NimMergeEndMark & tnl)
proc genSectionStart*(ps: TCProcSection): Rope =
if compilationCachePresent:
proc genSectionStart*(ps: TCProcSection; conf: ConfigRef): Rope =
if compilationCachePresent(conf):
result = rope(tnl)
add(result, "/*\t")
add(result, CProcSectionNames[ps])
add(result, ":*/")
add(result, tnl)
proc genSectionEnd*(ps: TCProcSection): Rope =
if compilationCachePresent:
proc genSectionEnd*(ps: TCProcSection; conf: ConfigRef): Rope =
if compilationCachePresent(conf):
result = rope(NimMergeEndMark & tnl)
proc writeTypeCache(a: TypeCache, s: var string) =
@ -96,7 +96,7 @@ proc writeIntSet(a: IntSet, s: var string) =
s.add('}')
proc genMergeInfo*(m: BModule): Rope =
if not compilationCachePresent: return nil
if not compilationCachePresent(m.config): return nil
var s = "/*\tNIM_merge_INFO:"
s.add(tnl)
s.add("typeCache:{")
@ -161,7 +161,7 @@ proc readVerbatimSection(L: var TBaseLexer): Rope =
buf = L.buf
r.add(tnl)
of '\0':
internalError("ccgmerge: expected: " & NimMergeEndMark)
doAssert(false, "ccgmerge: expected: " & NimMergeEndMark)
break
else:
if atEndMark(buf, pos):
@ -179,7 +179,7 @@ proc readKey(L: var TBaseLexer, result: var string) =
while buf[pos] in IdentChars:
result.add(buf[pos])
inc pos
if buf[pos] != ':': internalError("ccgmerge: ':' expected")
if buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected")
L.bufpos = pos + 1 # skip ':'
proc newFakeType(id: int): PType =
@ -187,12 +187,12 @@ proc newFakeType(id: int): PType =
result.id = id
proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
inc L.bufpos
while ^L.bufpos != '}':
skipWhite(L)
var key = decodeStr(L.buf, L.bufpos)
if ^L.bufpos != ':': internalError("ccgmerge: ':' expected")
if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected")
inc L.bufpos
var value = decodeStr(L.buf, L.bufpos)
# XXX implement me
@ -201,7 +201,7 @@ proc readTypeCache(L: var TBaseLexer, result: var TypeCache) =
inc L.bufpos
proc readIntSet(L: var TBaseLexer, result: var IntSet) =
if ^L.bufpos != '{': internalError("ccgmerge: '{' expected")
if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected")
inc L.bufpos
while ^L.bufpos != '}':
skipWhite(L)
@ -225,7 +225,7 @@ proc processMergeInfo(L: var TBaseLexer, m: BModule) =
of "labels": m.labels = decodeVInt(L.buf, L.bufpos)
of "flags":
m.flags = cast[set[CodegenFlag]](decodeVInt(L.buf, L.bufpos) != 0)
else: internalError("ccgmerge: unknown key: " & k)
else: doAssert(false, "ccgmerge: unknown key: " & k)
when not defined(nimhygiene):
{.pragma: inject.}
@ -275,9 +275,9 @@ proc readMergeSections(cfilename: string, m: var TMergeSections) =
if sectionB >= 0 and sectionB <= high(TCProcSection).int:
m.p[TCProcSection(sectionB)] = verbatim
else:
internalError("ccgmerge: unknown section: " & k)
doAssert(false, "ccgmerge: unknown section: " & k)
else:
internalError("ccgmerge: '*/' expected")
doAssert(false, "ccgmerge: '*/' expected")
proc mergeRequired*(m: BModule): bool =
for i in cfsHeaders..cfsProcs:

View file

@ -16,13 +16,17 @@ const
# above X strings a hash-switch for strings is generated
proc registerGcRoot(p: BProc, v: PSym) =
if gSelectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} and
if p.config.selectedGC in {gcMarkAndSweep, gcGenerational, gcV2, gcRefc} 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)
appcg(p.module, p.module.initProc.procSec(cpsInit),
"#nimRegisterGlobalMarker($1);$n", [prc])
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])
proc isAssignedImmediately(n: PNode): bool {.inline.} =
if n.kind == nkEmpty: return false
@ -33,15 +37,19 @@ proc isAssignedImmediately(n: PNode): bool {.inline.} =
return false
result = true
proc inExceptBlockLen(p: BProc): int =
for x in p.nestedTryStmts:
if x.inExcept: result.inc
proc genVarTuple(p: BProc, n: PNode) =
var tup, field: TLoc
if n.kind != nkVarTuple: internalError(n.info, "genVarTuple")
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):
if n[i].kind != nkSym:
genStmts(p, lowerTupleUnpacking(n, p.prc))
genStmts(p, lowerTupleUnpacking(p.module.g.graph, n, p.prc))
return
genLineDir(p, n)
@ -62,7 +70,7 @@ proc genVarTuple(p: BProc, n: PNode) =
if t.kind == tyTuple:
field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
else:
if t.n.sons[i].kind != nkSym: internalError(n.info, "genVarTuple")
if t.n.sons[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple")
field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n.sons[i].sym, t)]
putLocIntoDest(p, v.loc, field)
@ -92,7 +100,7 @@ proc startBlock(p: BProc, start: FormatStr = "{$n",
setLen(p.blocks, result + 1)
p.blocks[result].id = p.labels
p.blocks[result].nestedTryStmts = p.nestedTryStmts.len.int16
p.blocks[result].nestedExceptStmts = p.inExceptBlock.int16
p.blocks[result].nestedExceptStmts = p.inExceptBlockLen.int16
proc assignLabel(b: var TBlock): Rope {.inline.} =
b.label = "LA" & b.id.rope
@ -117,7 +125,7 @@ proc endBlock(p: BProc, blockEnd: Rope) =
proc endBlock(p: BProc) =
let topBlock = p.blocks.len - 1
var blockEnd = if p.blocks[topBlock].label != nil:
rfmt(nil, "} $1: ;$n", p.blocks[topBlock].label)
ropecg(p.module, "} $1: ;$n", p.blocks[topBlock].label)
else:
~"}$n"
let frameLen = p.blocks[topBlock].frameLen
@ -141,7 +149,7 @@ template preserveBreakIdx(body: untyped): untyped =
p.breakIdx = oldBreakIdx
proc genState(p: BProc, n: PNode) =
internalAssert n.len == 1
internalAssert p.config, n.len == 1
let n0 = n[0]
if n0.kind == nkIntLit:
let idx = n.sons[0].intVal
@ -149,6 +157,39 @@ proc genState(p: BProc, n: PNode) =
elif n0.kind == nkStrLit:
linefmt(p, cpsStmts, "$1: ;$n", n0.strVal.rope)
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
# Called by return and break stmts.
# Deals with issues faced when jumping out of try/except/finally stmts,
var stack = newSeq[tuple[n: PNode, inExcept: bool]](0)
for i in countup(1, howManyTrys):
let tryStmt = p.nestedTryStmts.pop
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
# Pop safe points generated by try
if not tryStmt.inExcept:
linefmt(p, cpsStmts, "#popSafePoint();$n")
# Pop this try-stmt of the list of nested trys
# so we don't infinite recurse on it in the next step.
stack.add(tryStmt)
# Find finally-stmt for this try-stmt
# and generate a copy of its sons
var finallyStmt = lastSon(tryStmt.n)
if finallyStmt.kind == nkFinally:
genStmts(p, finallyStmt.sons[0])
# push old elements again:
for i in countdown(howManyTrys-1, 0):
p.nestedTryStmts.add(stack[i])
if not p.module.compileToCpp or optNoCppExceptions in p.config.globalOptions:
# Pop exceptions that was handled by the
# except-blocks we are in
for i in countdown(howManyExcepts-1, 0):
linefmt(p, cpsStmts, "#popCurrentException();$n")
proc genGotoState(p: BProc, n: PNode) =
# we resist the temptation to translate it into duff's device as it later
# will be translated into computed gotos anyway for GCC at least:
@ -159,7 +200,11 @@ proc genGotoState(p: BProc, n: PNode) =
initLocExpr(p, n.sons[0], a)
lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
p.beforeRetNeeded = true
lineF(p, cpsStmts, "case -1: goto BeforeRet_;$n", [])
lineF(p, cpsStmts, "case -1:$n", [])
blockLeaveActions(p,
howManyTrys = p.nestedTryStmts.len,
howManyExcepts = p.inExceptBlockLen)
lineF(p, cpsStmts, " goto BeforeRet_;$n", [])
var statesCounter = lastOrd(n.sons[0].typ)
if n.len >= 2 and n[1].kind == nkIntLit:
statesCounter = n[1].intVal
@ -169,23 +214,21 @@ proc genGotoState(p: BProc, n: PNode) =
lineF(p, cpsStmts, "case $2: goto $1$2;$n", [prefix, rope(i)])
lineF(p, cpsStmts, "}$n", [])
proc genBreakState(p: BProc, n: PNode) =
proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
var a: TLoc
initLoc(d, locExpr, n, OnUnknown)
if n.sons[0].kind == nkClosure:
# XXX this produces quite inefficient code!
initLocExpr(p, n.sons[0].sons[1], a)
lineF(p, cpsStmts, "if (((NI*) $1)[1] < 0) break;$n", [rdLoc(a)])
d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
else:
initLocExpr(p, n.sons[0], a)
# the environment is guaranteed to contain the 'state' field at offset 1:
lineF(p, cpsStmts, "if ((((NI*) $1.ClE_0)[1]) < 0) break;$n", [rdLoc(a)])
# lineF(p, cpsStmts, "if (($1) < 0) break;$n", [rdLoc(a)])
proc genVarPrototypeAux(m: BModule, n: PNode)
d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
proc genGotoVar(p: BProc; value: PNode) =
if value.kind notin {nkCharLit..nkUInt64Lit}:
localError(value.info, "'goto' target must be a literal value")
localError(p.config, value.info, "'goto' target must be a literal value")
else:
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
@ -217,7 +260,7 @@ proc genSingleVar(p: BProc, a: PNode) =
# 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:
genVarPrototypeAux(p.module.g.generatedHeader, vn)
genVarPrototype(p.module.g.generatedHeader, vn)
registerGcRoot(p, v)
else:
let value = a.sons[2]
@ -239,7 +282,10 @@ proc genSingleVar(p: BProc, a: PNode) =
if params != nil: params.add(~", ")
assert(sonsLen(typ) == sonsLen(typ.n))
add(params, genOtherArg(p, value, i, typ))
lineF(p, cpsStmts, "$#($#);$n", [decl, params])
if params == nil:
lineF(p, cpsStmts, "$#;$n", [decl])
else:
lineF(p, cpsStmts, "$#($#);$n", [decl, params])
else:
initLocExprSingleUse(p, value, tmp)
lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc])
@ -260,28 +306,16 @@ proc genClosureVar(p: BProc, a: PNode) =
loadInto(p, a.sons[0], a.sons[2], v)
proc genVarStmt(p: BProc, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.kind == nkIdentDefs:
# can be a lifted var nowadays ...
if a.sons[0].kind == nkSym:
genSingleVar(p, a)
else:
genClosureVar(p, a)
else:
genVarTuple(p, a)
proc genConstStmt(p: BProc, t: PNode) =
for i in countup(0, sonsLen(t) - 1):
var it = t.sons[i]
for it in n.sons:
if it.kind == nkCommentStmt: continue
if it.kind != nkConstDef: internalError(t.info, "genConstStmt")
var c = it.sons[0].sym
if c.typ.containsCompileTimeOnly: continue
elif c.typ.kind in ConstantDataTypes and lfNoDecl notin c.loc.flags and
c.ast.len != 0:
if not emitLazily(c): requestConstImpl(p, c)
if it.kind == nkIdentDefs:
# can be a lifted var nowadays ...
if it.sons[0].kind == nkSym:
genSingleVar(p, it)
else:
genClosureVar(p, it)
else:
genVarTuple(p, it)
proc genIf(p: BProc, n: PNode, d: var TLoc) =
#
@ -302,10 +336,9 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
getTemp(p, n.typ, d)
genLineDir(p, n)
let lend = getLabel(p)
for i in countup(0, sonsLen(n) - 1):
for it in n.sons:
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(n.typ): d.k = locNone
let it = n.sons[i]
if it.len == 2:
when newScopeForIf: startBlock(p)
initLocExprSingleUse(p, it.sons[0], a)
@ -329,47 +362,9 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
startBlock(p)
expr(p, it.sons[0], d)
endBlock(p)
else: internalError(n.info, "genIf()")
else: internalError(p.config, n.info, "genIf()")
if sonsLen(n) > 1: fixLabel(p, lend)
proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
# Called by return and break stmts.
# Deals with issues faced when jumping out of try/except/finally stmts,
var stack: seq[PNode]
newSeq(stack, 0)
var alreadyPoppedCnt = p.inExceptBlock
for i in countup(1, howManyTrys):
if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
# Pop safe points generated by try
if alreadyPoppedCnt > 0:
dec alreadyPoppedCnt
else:
linefmt(p, cpsStmts, "#popSafePoint();$n")
# Pop this try-stmt of the list of nested trys
# so we don't infinite recurse on it in the next step.
var tryStmt = p.nestedTryStmts.pop
stack.add(tryStmt)
# Find finally-stmt for this try-stmt
# and generate a copy of its sons
var finallyStmt = lastSon(tryStmt)
if finallyStmt.kind == nkFinally:
genStmts(p, finallyStmt.sons[0])
# push old elements again:
for i in countdown(howManyTrys-1, 0):
p.nestedTryStmts.add(stack[i])
if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
# Pop exceptions that was handled by the
# except-blocks we are in
for i in countdown(howManyExcepts-1, 0):
linefmt(p, cpsStmts, "#popCurrentException();$n")
proc genReturnStmt(p: BProc, t: PNode) =
if nfPreventCg in t.flags: return
p.beforeRetNeeded = true
@ -377,7 +372,7 @@ proc genReturnStmt(p: BProc, t: PNode) =
if (t.sons[0].kind != nkEmpty): genStmts(p, t.sons[0])
blockLeaveActions(p,
howManyTrys = p.nestedTryStmts.len,
howManyExcepts = p.inExceptBlock)
howManyExcepts = p.inExceptBlockLen)
if (p.finallySafePoints.len > 0):
# If we're in a finally block, and we came here by exception
# consume it before we return.
@ -391,7 +386,7 @@ proc genGotoForCase(p: BProc; caseStmt: PNode) =
let it = caseStmt.sons[i]
for j in 0 .. it.len-2:
if it.sons[j].kind == nkRange:
localError(it.info, "range notation not available for computed goto")
localError(p.config, it.info, "range notation not available for computed goto")
return
let val = getOrdValue(it.sons[j])
lineF(p, cpsStmts, "NIMSTATE_$#:$n", [val.rope])
@ -406,19 +401,19 @@ proc genComputedGoto(p: BProc; n: PNode) =
let it = n.sons[i]
if it.kind == nkCaseStmt:
if lastSon(it).kind != nkOfBranch:
localError(it.info,
localError(p.config, it.info,
"case statement must be exhaustive for computed goto"); return
casePos = i
let aSize = lengthOrd(it.sons[0].typ)
if aSize > 10_000:
localError(it.info,
localError(p.config, it.info,
"case statement has too many cases for computed goto"); return
arraySize = aSize.int
if firstOrd(it.sons[0].typ) != 0:
localError(it.info,
localError(p.config, it.info,
"case statement has to start at 0 for computed goto"); return
if casePos < 0:
localError(n.info, "no case statement found for computed goto"); return
localError(p.config, n.info, "no case statement found for computed goto"); return
var id = p.labels+1
inc p.labels, arraySize+1
let tmp = "TMP$1_" % [id.rope]
@ -452,7 +447,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
let it = caseStmt.sons[i]
for j in 0 .. it.len-2:
if it.sons[j].kind == nkRange:
localError(it.info, "range notation not available for computed goto")
localError(p.config, it.info, "range notation not available for computed goto")
return
let val = getOrdValue(it.sons[j])
lineF(p, cpsStmts, "TMP$#_:$n", [intLiteral(val+id+1)])
@ -556,33 +551,38 @@ proc genBreakStmt(p: BProc, t: PNode) =
# an unnamed 'break' can only break a loop after 'transf' pass:
while idx >= 0 and not p.blocks[idx].isLoop: dec idx
if idx < 0 or not p.blocks[idx].isLoop:
internalError(t.info, "no loop to break")
internalError(p.config, t.info, "no loop to break")
let label = assignLabel(p.blocks[idx])
blockLeaveActions(p,
p.nestedTryStmts.len - p.blocks[idx].nestedTryStmts,
p.inExceptBlock - p.blocks[idx].nestedExceptStmts)
p.inExceptBlockLen - p.blocks[idx].nestedExceptStmts)
genLineDir(p, t)
lineF(p, cpsStmts, "goto $1;$n", [label])
proc genRaiseStmt(p: BProc, t: PNode) =
if p.inExceptBlock > 0:
if p.module.compileToCpp:
discard cgsym(p.module, "popCurrentExceptionEx")
if 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[p.nestedTryStmts.len - 1].lastSon
var finallyBlock = p.nestedTryStmts[^1].n[^1]
if finallyBlock.kind == nkFinally:
genSimpleBlock(p, finallyBlock.sons[0])
if t.sons[0].kind != nkEmpty:
genSimpleBlock(p, finallyBlock[0])
if t[0].kind != nkEmpty:
var a: TLoc
initLocExpr(p, t.sons[0], a)
initLocExprSingleUse(p, t[0], a)
var e = rdLoc(a)
var typ = skipTypes(t.sons[0].typ, abstractPtrs)
var typ = skipTypes(t[0].typ, abstractPtrs)
genLineDir(p, t)
lineCg(p, cpsStmts, "#raiseException((#Exception*)$1, $2);$n",
[e, makeCString(typ.sym.name.s)])
if isImportedException(typ, p.config):
lineF(p, cpsStmts, "throw $1;$n", [e])
else:
lineCg(p, cpsStmts, "#raiseException((#Exception*)$1, $2);$n",
[e, makeCString(typ.sym.name.s)])
else:
genLineDir(p, t)
# reraise the last exception:
if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions:
if p.module.compileToCpp and optNoCppExceptions notin p.config.globalOptions:
line(p, cpsStmts, ~"throw;$n")
else:
linefmt(p, cpsStmts, "#reraiseException();$n")
@ -775,91 +775,75 @@ proc genCase(p: BProc, t: PNode, d: var TLoc) =
else:
genOrdinalCase(p, t, d)
proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
# code to generate:
#
# XXX: There should be a standard dispatch algorithm
# that's used both here and with multi-methods
#
# try
# {
# myDiv(4, 9);
# } catch (NimException& exp) {
# if (isObj(exp, EIO) {
# ...
# } else if (isObj(exp, ESystem) {
# ...
# finallyPart()
# raise;
# } else {
# // general handler
# }
# }
# finallyPart();
# } catch (NimExceptionType1&) {
# body
# } catch (NimExceptionType2&) {
# finallyPart()
# raise;
# }
# catch(...) {
# general_handler_body
# }
# finallyPart();
template genExceptBranchBody(body: PNode) {.dirty.} =
if optStackTrace in p.options:
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
expr(p, body, d)
if not isEmptyType(t.typ) and d.k == locNone:
getTemp(p, t.typ, d)
genLineDir(p, t)
let exc = getTempName(p.module)
if getCompilerProc("Exception") != nil:
discard cgsym(p.module, "Exception")
else:
discard cgsym(p.module, "E_Base")
add(p.nestedTryStmts, t)
discard cgsym(p.module, "popCurrentExceptionEx")
add(p.nestedTryStmts, (t, false))
startBlock(p, "try {$n")
expr(p, t.sons[0], d)
let length = sonsLen(t)
endBlock(p, ropecg(p.module, "} catch (NimException& $1) {$n", [exc]))
if optStackTrace in p.options:
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
inc p.inExceptBlock
var i = 1
expr(p, t[0], d)
endBlock(p)
var catchAllPresent = false
while (i < length) and (t.sons[i].kind == nkExceptBranch):
p.nestedTryStmts[^1].inExcept = true
for i in 1..<t.len:
if t[i].kind != nkExceptBranch: break
# bug #4230: avoid false sharing between branches:
if d.k == locTemp and isEmptyType(t.typ): d.k = locNone
let blen = sonsLen(t.sons[i])
if i > 1: addf(p.s(cpsStmts), "else ", [])
if blen == 1:
if t[i].len == 1:
# general except section:
catchAllPresent = true
startBlock(p)
expr(p, t.sons[i].sons[0], d)
linefmt(p, cpsStmts, "#popCurrentException();$n")
startBlock(p, "catch (...) {$n")
genExceptBranchBody(t[i][0])
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, "||")
appcg(p.module, orExpr,
"#isObj($1.exp->m_type, $2)",
[exc, genTypeInfo(p.module, t[i][j].typ, t[i][j].info)])
lineF(p, cpsStmts, "if ($1) ", [orExpr])
startBlock(p)
expr(p, t.sons[i].sons[blen-1], d)
linefmt(p, cpsStmts, "#popCurrentException();$n")
endBlock(p)
inc(i)
for j in 0..t[i].len-2:
if t[i][j].isInfixAs():
let exvar = t[i][j][2] # ex1 in `except ExceptType as ex1:`
fillLoc(exvar.sym.loc, locTemp, exvar, mangleLocalName(p, exvar.sym), OnUnknown)
startBlock(p, "catch ($1& $2) {$n", getTypeDesc(p.module, t[i][j][1].typ), rdLoc(exvar.sym.loc))
else:
startBlock(p, "catch ($1&) {$n", getTypeDesc(p.module, t[i][j].typ))
genExceptBranchBody(t[i][^1]) # exception handler body will duplicated for every type
endBlock(p)
# reraise the exception if there was no catch all
# and none of the handlers matched
if not catchAllPresent:
if i > 1: lineF(p, cpsStmts, "else ", [])
startBlock(p)
var finallyBlock = t.lastSon
if finallyBlock.kind == nkFinally:
#expr(p, finallyBlock.sons[0], d)
genStmts(p, finallyBlock.sons[0])
discard pop(p.nestedTryStmts)
if not catchAllPresent and t[^1].kind == nkFinally:
# finally requires catch all presence
startBlock(p, "catch (...) {$n")
genSimpleBlock(p, t[^1][0])
line(p, cpsStmts, ~"throw;$n")
endBlock(p)
lineF(p, cpsStmts, "}$n", []) # end of catch block
dec p.inExceptBlock
discard pop(p.nestedTryStmts)
if (i < length) and (t.sons[i].kind == nkFinally):
genSimpleBlock(p, t.sons[i].sons[0])
if t[^1].kind == nkFinally:
genSimpleBlock(p, t[^1][0])
proc genTry(p: BProc, t: PNode, d: var TLoc) =
# code to generate:
@ -895,23 +879,20 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
p.module.includeHeader("<setjmp.h>")
genLineDir(p, t)
var safePoint = getTempName(p.module)
if getCompilerProc("Exception") != nil:
discard cgsym(p.module, "Exception")
else:
discard cgsym(p.module, "E_Base")
discard cgsym(p.module, "Exception")
linefmt(p, cpsLocals, "#TSafePoint $1;$n", safePoint)
linefmt(p, cpsStmts, "#pushSafePoint(&$1);$n", safePoint)
if isDefined("nimStdSetjmp"):
if isDefined(p.config, "nimStdSetjmp"):
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", safePoint)
elif isDefined("nimSigSetjmp"):
elif isDefined(p.config, "nimSigSetjmp"):
linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", safePoint)
elif isDefined("nimRawSetjmp"):
elif isDefined(p.config, "nimRawSetjmp"):
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", safePoint)
else:
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)
add(p.nestedTryStmts, (t, false))
expr(p, t.sons[0], d)
linefmt(p, cpsStmts, "#popSafePoint();$n")
endBlock(p)
@ -919,7 +900,7 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
linefmt(p, cpsStmts, "#popSafePoint();$n")
if optStackTrace in p.options:
linefmt(p, cpsStmts, "#setFrame((TFrame*)&FR_);$n")
inc p.inExceptBlock
p.nestedTryStmts[^1].inExcept = true
var i = 1
while (i < length) and (t.sons[i].kind == nkExceptBranch):
# bug #4230: avoid false sharing between branches:
@ -950,7 +931,6 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
linefmt(p, cpsStmts, "#popCurrentException();$n")
endBlock(p)
inc(i)
dec p.inExceptBlock
discard pop(p.nestedTryStmts)
endBlock(p) # end of else block
if i < length and t.sons[i].kind == nkFinally:
@ -961,19 +941,20 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) =
proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
var res = ""
for i in countup(0, sonsLen(t) - 1):
case t.sons[i].kind
for it in t.sons:
case it.kind
of nkStrLit..nkTripleStrLit:
res.add(t.sons[i].strVal)
res.add(it.strVal)
of nkSym:
var sym = t.sons[i].sym
var sym = it.sym
if sym.kind in {skProc, skFunc, skIterator, skMethod}:
var a: TLoc
initLocExpr(p, t.sons[i], a)
initLocExpr(p, it, a)
res.add($rdLoc(a))
elif sym.kind == skType:
res.add($getTypeDesc(p.module, sym.typ))
else:
discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
var r = sym.loc.r
if r == nil:
# if no name has already been given,
@ -982,12 +963,12 @@ proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false): Rope =
sym.loc.r = r # but be consequent!
res.add($r)
of nkTypeOfExpr:
res.add($getTypeDesc(p.module, t.sons[i].typ))
res.add($getTypeDesc(p.module, it.typ))
else:
discard getTypeDesc(p.module, skipTypes(it.typ, abstractPtrs))
var a: TLoc
initLocExpr(p, t.sons[i], a)
initLocExpr(p, it, a)
res.add($a.rdLoc)
#internalError(t.sons[i].info, "genAsmOrEmitStmt()")
if isAsmStmt and hasGnuAsm in CC[cCompiler].props:
for x in splitLines(res):
@ -1032,7 +1013,7 @@ proc genEmit(p: BProc, t: PNode) =
if p.prc == nil:
# top level emit pragma?
let section = determineSection(t[1])
genCLineDir(p.module.s[section], t.info)
genCLineDir(p.module.s[section], t.info, p.config)
add(p.module.s[section], s)
else:
genLineDir(p, t)
@ -1063,8 +1044,7 @@ proc genWatchpoint(p: BProc, n: PNode) =
genTypeInfo(p.module, typ, n.info)])
proc genPragma(p: BProc, n: PNode) =
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
for it in n.sons:
case whichPragma(it)
of wEmit: genEmit(p, it)
of wBreakpoint: genBreakPoint(p, it)
@ -1159,4 +1139,4 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
proc genStmts(p: BProc, t: PNode) =
var a: TLoc
expr(p, t, a)
internalAssert a.k in {locNone, locTemp, locLocalVar}
internalAssert p.config, a.k in {locNone, locTemp, locLocalVar}

View file

@ -12,11 +12,11 @@
# included from cgen.nim
proc emulatedThreadVars(): bool =
result = {optThreads, optTlsEmulation} <= gGlobalOptions
proc emulatedThreadVars(conf: ConfigRef): bool =
result = {optThreads, optTlsEmulation} <= conf.globalOptions
proc accessThreadLocalVar(p: BProc, s: PSym) =
if emulatedThreadVars() and not p.threadVarAccessed:
if emulatedThreadVars(p.config) and not p.threadVarAccessed:
p.threadVarAccessed = true
incl p.module.flags, usesThreadVars
addf(p.procSec(cpsLocals), "\tNimThreadVars* NimTV_;$n", [])
@ -37,7 +37,7 @@ var
# made to be one.
proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
if emulatedThreadVars():
if emulatedThreadVars(m.config):
# we gather all thread locals var into a struct; we need to allocate
# storage for that somehow, can't use the thread local storage
# allocator for it :-(
@ -46,7 +46,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
addf(nimtv, "$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
else:
if isExtern: add(m.s[cfsVars], "extern ")
if optThreads in gGlobalOptions: add(m.s[cfsVars], "NIM_THREADVAR ")
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])
@ -57,7 +57,7 @@ proc generateThreadLocalStorage(m: BModule) =
proc generateThreadVarsSize(m: BModule) =
if nimtv != nil:
let externc = if gCmd == cmdCompileToCpp or
let externc = if m.config.cmd == cmdCompileToCpp or
sfCompileToCpp in m.module.flags: "extern \"C\" "
else: ""
addf(m.s[cfsProcs],

View file

@ -17,11 +17,11 @@ type
p: BProc
visitorFrmt: string
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType)
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
proc genCaseRange(p: BProc, branch: PNode)
proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false)
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, n: PNode;
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
typ: PType) =
if n == nil: return
case n.kind
@ -29,12 +29,12 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, n: PNode;
for i in countup(0, sonsLen(n) - 1):
genTraverseProc(c, accessor, n.sons[i], typ)
of nkRecCase:
if (n.sons[0].kind != nkSym): internalError(n.info, "genTraverseProc")
if (n.sons[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
var p = c.p
let disc = n.sons[0].sym
if disc.loc.r == nil: fillObjectFields(c.p.module, typ)
if disc.loc.t == nil:
internalError(n.info, "genTraverseProc()")
internalError(c.p.config, n.info, "genTraverseProc()")
lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
for i in countup(1, sonsLen(n) - 1):
let branch = n.sons[i]
@ -51,9 +51,9 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, n: PNode;
if field.typ.kind == tyVoid: return
if field.loc.r == nil: fillObjectFields(c.p.module, typ)
if field.loc.t == nil:
internalError(n.info, "genTraverseProc()")
internalError(c.p.config, n.info, "genTraverseProc()")
genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
else: internalError(n.info, "genTraverseProc()")
else: internalError(c.p.config, n.info, "genTraverseProc()")
proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
if not m.compileToCpp:
@ -61,22 +61,23 @@ proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
else:
result = accessor
proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) =
proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
if typ == nil: return
var p = c.p
case typ.kind
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred:
of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
tySink:
genTraverseProc(c, accessor, lastSon(typ))
of tyArray:
let arraySize = lengthOrd(typ.sons[0])
var i: TLoc
getTemp(p, getSysType(tyInt), i)
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
let oldCode = p.s(cpsStmts)
linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
i.r, arraySize.rope)
let oldLen = p.s(cpsStmts).len
genTraverseProc(c, rfmt(nil, "$1[$2]", accessor, i.r), typ.sons[1])
genTraverseProc(c, ropecg(c.p.module, "$1[$2]", accessor, i.r), typ.sons[1])
if p.s(cpsStmts).len == oldLen:
# do not emit dummy long loops for faster debug builds:
p.s(cpsStmts) = oldCode
@ -91,20 +92,20 @@ proc genTraverseProc(c: var TTraversalClosure, accessor: Rope, typ: PType) =
of tyTuple:
let typ = getUniqueType(typ)
for i in countup(0, sonsLen(typ) - 1):
genTraverseProc(c, rfmt(nil, "$1.Field$2", accessor, i.rope), typ.sons[i])
genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", accessor, i.rope), typ.sons[i])
of tyRef, tyString, tySequence:
lineCg(p, cpsStmts, c.visitorFrmt, accessor)
of tyProc:
if typ.callConv == ccClosure:
lineCg(p, cpsStmts, c.visitorFrmt, rfmt(nil, "$1.ClE_0", accessor))
lineCg(p, cpsStmts, c.visitorFrmt, ropecg(c.p.module, "$1.ClE_0", accessor))
else:
discard
proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) =
proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
var p = c.p
assert typ.kind == tySequence
var i: TLoc
getTemp(p, getSysType(tyInt), i)
getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i)
let oldCode = p.s(cpsStmts)
lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n",
[i.r, accessor, rope(if c.p.module.compileToCpp: "len" else: "Sup.len")])
@ -116,18 +117,13 @@ proc genTraverseProcSeq(c: var TTraversalClosure, accessor: Rope, typ: PType) =
else:
lineF(p, cpsStmts, "}$n", [])
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash;
reason: TTypeInfoReason): Rope =
proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
var c: TTraversalClosure
var p = newProc(nil, m)
result = "Marker_" & getTypeName(m, origTyp, sig)
var typ = origTyp.skipTypes(abstractInst)
if typ.kind == tyOpt: typ = optLowering(typ)
case reason
of tiNew: c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
else: assert false
let header = "static N_NIMCALL(void, $1)(void* p, NI op)" % [result]
let t = getTypeDesc(m, typ)
@ -135,6 +131,8 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash;
lineF(p, cpsInit, "a = ($1)p;$n", [t])
c.p = p
c.visitorFrmt = "#nimGCvisit((void*)$1, op);$n"
assert typ.kind != tyTypeDesc
if typ.kind == tySequence:
genTraverseProcSeq(c, "a".rope, typ)
@ -159,7 +157,7 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
var sLoc = s.loc.r
result = getTempName(m)
if sfThread in s.flags and emulatedThreadVars():
if sfThread in s.flags and emulatedThreadVars(m.config):
accessThreadLocalVar(p, s)
sLoc = "NimTV_->" & sLoc

View file

@ -14,6 +14,8 @@
import sighashes
from lowerings import createObj
proc genProcHeader(m: BModule, prc: PSym): Rope
proc isKeyword(w: PIdent): bool =
# Nim and C++ share some keywords
# it's more efficient to test the whole Nim keywords range
@ -42,38 +44,11 @@ when false:
assert p.kind == skPackage
result = gDebugInfo.register(p.name.s, m.name.s)
proc idOrSig(m: BModule; s: PSym): Rope =
if s.kind in routineKinds and s.typ != nil:
# signatures for exported routines are reliable enough to
# produce a unique name and this means produced C++ is more stable wrt
# Nim changes:
let sig = hashProc(s)
result = rope($sig)
#let m = if s.typ.callConv != ccInline: findPendingModule(m, s) else: m
let counter = m.sigConflicts.getOrDefault(sig)
#if sigs == "_jckmNePK3i2MFnWwZlp6Lg" and s.name.s == "contains":
# echo "counter ", counter, " ", s.id
if counter != 0:
result.add "_" & rope(counter+1)
# this minor hack is necessary to make tests/collections/thashes compile.
# The inlined hash function's original module is ambiguous so we end up
# generating duplicate names otherwise:
if s.typ.callConv == ccInline:
result.add rope(m.module.name.s)
m.sigConflicts.inc(sig)
else:
let sig = hashNonProc(s)
result = rope($sig)
let counter = m.sigConflicts.getOrDefault(sig)
if counter != 0:
result.add "_" & rope(counter+1)
m.sigConflicts.inc(sig)
proc mangleName(m: BModule; s: PSym): Rope =
result = s.loc.r
if result == nil:
result = s.name.s.mangle.rope
add(result, m.idOrSig(s))
add(result, idOrSig(s, m.module.name.s, m.sigConflicts))
s.loc.r = result
writeMangledName(m.ndi, s)
@ -119,7 +94,7 @@ proc scopeMangledParam(p: BProc; param: PSym) =
const
irrelevantForBackend = {tyGenericBody, tyGenericInst, tyGenericInvocation,
tyDistinct, tyRange, tyStatic, tyAlias, tyInferred}
tyDistinct, tyRange, tyStatic, tyAlias, tySink, tyInferred}
proc typeName(typ: PType): Rope =
let typ = typ.skipTypes(irrelevantForBackend)
@ -139,7 +114,7 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
t = t.lastSon
else:
break
let typ = if typ.kind == tyAlias: typ.lastSon else: typ
let typ = if typ.kind in {tyAlias, tySink}: typ.lastSon else: typ
if typ.loc.r == nil:
typ.loc.r = typ.typeName & $sig
else:
@ -147,7 +122,7 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
# check consistency:
assert($typ.loc.r == $(typ.typeName & $sig))
result = typ.loc.r
if result == nil: internalError("getTypeName: " & $typ.kind)
if result == nil: internalError(m.config, "getTypeName: " & $typ.kind)
proc mapSetType(typ: PType): TCTypeKind =
case int(getSize(typ))
@ -167,10 +142,10 @@ proc mapType(typ: PType): TCTypeKind =
of tyOpenArray, tyArray, tyVarargs: result = ctArray
of tyObject, tyTuple: result = ctStruct
of tyUserTypeClasses:
internalAssert typ.isResolvedUserTypeClass
doAssert typ.isResolvedUserTypeClass
return mapType(typ.lastSon)
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
tyTypeDesc, tyAlias, tyInferred:
tyTypeDesc, tyAlias, tySink, tyInferred:
result = mapType(lastSon(typ))
of tyEnum:
if firstOrd(typ) < 0:
@ -181,9 +156,9 @@ proc mapType(typ: PType): TCTypeKind =
of 2: result = ctUInt16
of 4: result = ctInt32
of 8: result = ctInt64
else: internalError("mapType")
else: result = ctInt32
of tyRange: result = mapType(typ.sons[0])
of tyPtr, tyVar, tyRef, tyOptAsRef:
of tyPtr, tyVar, tyLent, tyRef, tyOptAsRef:
var base = skipTypes(typ.lastSon, typedescInst)
case base.kind
of tyOpenArray, tyArray, tyVarargs: result = ctPtrToArray
@ -208,8 +183,8 @@ proc mapType(typ: PType): TCTypeKind =
result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
of tyStatic:
if typ.n != nil: result = mapType(lastSon typ)
else: internalError("mapType")
else: internalError("mapType")
else: doAssert(false, "mapType")
else: doAssert(false, "mapType")
proc mapReturnType(typ: PType): TCTypeKind =
#if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
@ -242,7 +217,7 @@ proc isInvalidReturnType(rettype: PType): bool =
case mapType(rettype)
of ctArray:
result = not (skipTypes(rettype, typedescInst).kind in
{tyVar, tyRef, tyPtr})
{tyVar, tyLent, tyRef, tyPtr})
of ctStruct:
let t = skipTypes(rettype, typedescInst)
if rettype.isImportedCppType or t.isImportedCppType: return false
@ -264,13 +239,9 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
result = tab.getOrDefault(sig)
proc addAbiCheck(m: BModule, t: PType, name: Rope) =
if isDefined("checkabi"):
if isDefined(m.config, "checkabi"):
addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(t))])
proc getTempName(m: BModule): Rope =
result = m.tmpBase & rope(m.labels)
inc m.labels
proc ccgIntroducedPtr(s: PSym): bool =
var pt = skipTypes(s.typ, typedescInst)
assert skResult != s.kind
@ -316,8 +287,13 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
of tyPointer:
result = typeNameOrLiteral(m, typ, "void*")
of tyString:
discard cgsym(m, "NimStringDesc")
result = typeNameOrLiteral(m, typ, "NimStringDesc*")
case detectStrVersion(m)
of 2:
discard cgsym(m, "string")
result = typeNameOrLiteral(m, typ, "NimStringV2")
else:
discard cgsym(m, "NimStringDesc")
result = typeNameOrLiteral(m, typ, "NimStringDesc*")
of tyCString: result = typeNameOrLiteral(m, typ, "NCSTRING")
of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL")
of tyChar: result = typeNameOrLiteral(m, typ, "NIM_CHAR")
@ -327,8 +303,8 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.sons[0])
of tyStatic:
if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ)
else: internalError("tyStatic for getSimpleTypeDesc")
of tyGenericInst, tyAlias:
else: internalError(m.config, "tyStatic for getSimpleTypeDesc")
of tyGenericInst, tyAlias, tySink:
result = getSimpleTypeDesc(m, lastSon typ)
else: result = nil
@ -348,7 +324,7 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
if result == nil: result = cacheGetType(m.typeCache, sig)
proc structOrUnion(t: PType): Rope =
let t = t.skipTypes({tyAlias})
let t = t.skipTypes({tyAlias, tySink})
(if tfUnion in t.flags: rope("union") else: rope("struct"))
proc getForwardStructFormat(m: BModule): string =
@ -368,8 +344,10 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
if not isImportedType(concrete):
addf(m.s[cfsForwardTypes], getForwardStructFormat(m),
[structOrUnion(typ), result])
else:
pushType(m, concrete)
doAssert m.forwTypeCache[sig] == result
else: internalError("getTypeForward(" & $typ.kind & ')')
else: internalError(m.config, "getTypeForward(" & $typ.kind & ')')
proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
## like getTypeDescAux but creates only a *weak* dependency. In other words
@ -396,7 +374,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope =
result = getTypeDescAux(m, t, check)
proc paramStorageLoc(param: PSym): TStorageLoc =
if param.typ.skipTypes({tyVar, tyTypeDesc}).kind notin {
if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin {
tyArray, tyOpenArray, tyVarargs}:
result = OnStack
else:
@ -411,7 +389,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
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(t.n.info, "genProcParams")
if t.n.sons[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
var param = t.n.sons[i].sym
if isCompileTimeOnly(param.typ): continue
if params != nil: add(params, ~", ")
@ -430,11 +408,11 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
add(params, param.loc.r)
# declare the len field for open arrays:
var arr = param.typ
if arr.kind == tyVar: arr = arr.sons[0]
if arr.kind in {tyVar, tyLent}: arr = arr.lastSon
var j = 0
while arr.kind in {tyOpenArray, tyVarargs}:
# this fixes the 'sort' bug:
if param.typ.kind == tyVar: param.loc.storage = OnUnknown
if param.typ.kind in {tyVar, tyLent}: param.loc.storage = OnUnknown
# need to pass hidden parameter:
addf(params, ", NI $1Len_$2", [param.loc.r, j.rope])
inc(j)
@ -464,7 +442,7 @@ proc mangleRecFieldName(m: BModule; field: PSym, rectype: PType): Rope =
result = field.loc.r
else:
result = rope(mangleField(m, field.name))
if result == nil: internalError(field.info, "mangleRecFieldName")
if result == nil: internalError(m.config, field.info, "mangleRecFieldName")
proc genRecordFieldsAux(m: BModule, n: PNode,
accessExpr: Rope, rectype: PType,
@ -475,7 +453,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
for i in countup(0, sonsLen(n) - 1):
add(result, genRecordFieldsAux(m, n.sons[i], accessExpr, rectype, check))
of nkRecCase:
if n.sons[0].kind != nkSym: internalError(n.info, "genRecordFieldsAux")
if n.sons[0].kind != nkSym: internalError(m.config, n.info, "genRecordFieldsAux")
add(result, genRecordFieldsAux(m, n.sons[0], accessExpr, rectype, check))
let uname = rope(mangle(n.sons[0].sym.name.s) & 'U')
let ae = if accessExpr != nil: "$1.$2" % [accessExpr, uname]
@ -496,14 +474,14 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
if hasAttribute in CC[cCompiler].props:
add(unionBody, "struct __attribute__((__packed__)){" )
else:
addf(unionBody, "#pragma pack(1)$nstruct{", [])
addf(unionBody, "#pragma pack(push, 1)$nstruct{", [])
add(unionBody, a)
addf(unionBody, "} $1;$n", [sname])
if tfPacked in rectype.flags and hasAttribute notin CC[cCompiler].props:
addf(unionBody, "#pragma pack(pop)$n", [])
else:
add(unionBody, genRecordFieldsAux(m, k, ae, rectype, check))
else: internalError("genRecordFieldsAux(record case branch)")
else: internalError(m.config, "genRecordFieldsAux(record case branch)")
if unionBody != nil:
addf(result, "union{$n$1} $2;$n", [unionBody, uname])
of nkSym:
@ -531,7 +509,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
# don't use fieldType here because we need the
# tyGenericInst for C++ template support
addf(result, "$1 $2;$n", [getTypeDescAux(m, field.loc.t, check), sname])
else: internalError(n.info, "genRecordFieldsAux()")
else: internalError(m.config, n.info, "genRecordFieldsAux()")
proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
result = genRecordFieldsAux(m, typ.n, nil, typ, check)
@ -551,7 +529,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
if hasAttribute in CC[cCompiler].props:
result = structOrUnion(typ) & " __attribute__((__packed__))"
else:
result = "#pragma pack(1)" & tnl & structOrUnion(typ)
result = "#pragma pack(push, 1)" & tnl & structOrUnion(typ)
else:
result = structOrUnion(typ)
@ -569,6 +547,16 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope,
elif m.compileToCpp:
appcg(m, result, " : public $1 {$n",
[getTypeDescAux(m, typ.sons[0].skipTypes(skipPtrs), check)])
if typ.isException:
appcg(m, result, "virtual void raise() {throw *this;}$n") # required for polymorphic exceptions
if typ.sym.magic == mException:
# Add cleanup destructor to Exception base class
appcg(m, result, "~$1() {if(this->raise_id) popCurrentExceptionEx(this->raise_id);}$n", [name])
# hack: forward declare popCurrentExceptionEx() on top of type description,
# proper request to generate popCurrentExceptionEx not possible for 2 reasons:
# generated function will be below declared Exception type and circular dependency
# between Exception and popCurrentExceptionEx function
result = genProcHeader(m, magicsys.getCompilerProc(m.g.graph, "popCurrentExceptionEx")) & ";" & rnl & result
hasField = true
else:
appcg(m, result, " {$n $1 Sup;$n",
@ -615,8 +603,10 @@ proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
return false
proc resolveStarsInCppType(typ: PType, idx, stars: int): PType =
# XXX: we should catch this earlier and report it as a semantic error
if idx >= typ.len: internalError "invalid apostrophe type parameter index"
# Make sure the index refers to one of the generic params of the type.
# XXX: we should catch this earlier and report it as a semantic error.
if idx >= typ.len:
doAssert false, "invalid apostrophe type parameter index"
result = typ.sons[idx]
for i in 1..stars:
@ -629,7 +619,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
var t = origTyp.skipTypes(irrelevantForBackend)
if containsOrIncl(check, t.id):
if not (isImportedCppType(origTyp) or isImportedCppType(t)):
internalError("cannot generate C type for: " & typeToString(origTyp))
internalError(m.config, "cannot generate C type for: " & typeToString(origTyp))
# XXX: this BUG is hard to fix -> we need to introduce helper structs,
# but determining when this needs to be done is hard. We should split
# C type generation into an analysis and a code generation phase somehow.
@ -641,7 +631,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
excl(check, t.id)
return
case t.kind
of tyRef, tyOptAsRef, tyPtr, tyVar:
of tyRef, tyOptAsRef, tyPtr, tyVar, tyLent:
var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and
compileToCpp(m): "&" else: "*"
var et = origTyp.skipTypes(abstractInst).lastSon
@ -661,7 +651,6 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
let name = getTypeForward(m, et, hashType et)
result = name & star
m.typeCache[sig] = result
pushType(m, et)
of tySequence:
# no restriction! We have a forward declaration for structs
let name = getTypeForward(m, et, hashType et)
@ -708,7 +697,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
of 2: addf(m.s[cfsTypes], "typedef NU16 $1;$n", [result])
of 4: addf(m.s[cfsTypes], "typedef NI32 $1;$n", [result])
of 8: addf(m.s[cfsTypes], "typedef NI64 $1;$n", [result])
else: internalError(t.sym.info, "getTypeDescAux: enum")
else: internalError(m.config, t.sym.info, "getTypeDescAux: enum")
when false:
let owner = hashOwner(t.sym)
if not gDebugInfo.hasEnum(t.sym.name.s, t.sym.info.line, owner):
@ -818,6 +807,9 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
let typeInSlot = resolveStarsInCppType(origTyp, idx + 1, stars)
if typeInSlot == nil or typeInSlot.kind == tyVoid:
result.add(~"void")
elif typeInSlot.kind == tyStatic:
internalAssert m.config, typeInSlot.n != nil
result.add typeInSlot.n.renderTree
else:
result.add getTypeDescAux(m, typeInSlot, check)
else:
@ -834,6 +826,13 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
# always call for sideeffects:
assert t.kind != tyTuple
discard getRecordDesc(m, t, result, check)
# The resulting type will include commas and these won't play well
# with the C macros for defining procs such as N_NIMCALL. We must
# create a typedef for the type and use it in the proc signature:
let typedefName = ~"TY" & $sig
addf(m.s[cfsTypes], "typedef $1 $2;$n", [result, typedefName])
m.typeCache[sig] = typedefName
result = typedefName
else:
when false:
if t.sym != nil and t.sym.name.s == "KeyValuePair":
@ -872,11 +871,11 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope =
of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)])
else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n",
[result, rope(getSize(t))])
of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias,
of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink,
tyUserTypeClass, tyUserTypeClassInst, tyInferred:
result = getTypeDescAux(m, lastSon(t), check)
else:
internalError("getTypeDescAux(" & $t.kind & ')')
internalError(m.config, "getTypeDescAux(" & $t.kind & ')')
result = nil
# fixes bug #145:
excl(check, t.id)
@ -916,7 +915,7 @@ template cgDeclFrmt*(s: PSym): string = s.constraint.strVal
proc genProcHeader(m: BModule, prc: PSym): Rope =
var
rettype, params: Rope
genCLineDir(result, prc.info)
genCLineDir(result, prc.info, m.config)
# using static is needed for inline procs
if lfExportLib in prc.loc.flags:
if isHeaderFile in m.flags:
@ -969,8 +968,9 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
if flags != 0:
addf(m.s[cfsTypeInit3], "$1.flags = $2;$n", [name, rope(flags)])
discard cgsym(m, "TNimType")
if isDefined("nimTypeNames"):
var typename = typeToString(origType, preferName)
if isDefined(m.config, "nimTypeNames"):
var typename = typeToString(if origType.typeInst != nil: origType.typeInst
else: origType, preferName)
if typename == "ref object" and origType.skipTypes(skipPtrs).sym != nil:
typename = "anon ref object from " & $origType.skipTypes(skipPtrs).sym.info
addf(m.s[cfsTypeInit3], "$1.name = $2;$n",
@ -1000,7 +1000,7 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
while lookupInRecord(objtype.n, d.name) == nil:
objtype = objtype.sons[0]
if objtype.sym == nil:
internalError(d.info, "anonymous obj with discriminator")
internalError(m.config, d.info, "anonymous obj with discriminator")
result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)]
proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope =
@ -1034,7 +1034,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
assert L > 0
if field.loc.r == nil: fillObjectFields(m, typ)
if field.loc.t == nil:
internalError(n.info, "genObjectFields")
internalError(m.config, n.info, "genObjectFields")
addf(m.s[cfsTypeInit3], "$1.kind = 3;$n" &
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
"$1.name = $5;$n" & "$1.sons = &$6[0];$n" &
@ -1050,7 +1050,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
case b.kind
of nkOfBranch:
if sonsLen(b) < 2:
internalError(b.info, "genObjectFields; nkOfBranch broken")
internalError(m.config, b.info, "genObjectFields; nkOfBranch broken")
for j in countup(0, sonsLen(b) - 2):
if b.sons[j].kind == nkRange:
var x = int(getOrdValue(b.sons[j].sons[0]))
@ -1064,23 +1064,23 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
of nkElse:
addf(m.s[cfsTypeInit3], "$1[$2] = &$3;$n",
[tmp, rope(L), tmp2])
else: internalError(n.info, "genObjectFields(nkRecCase)")
else: internalError(m.config, n.info, "genObjectFields(nkRecCase)")
of nkSym:
var field = n.sym
if field.bitsize == 0:
if field.loc.r == nil: fillObjectFields(m, typ)
if field.loc.t == nil:
internalError(n.info, "genObjectFields")
internalError(m.config, n.info, "genObjectFields")
addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" &
"$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
"$1.name = $5;$n", [expr, getTypeDesc(m, origType),
field.loc.r, genTypeInfo(m, field.typ, info), makeCString(field.name.s)])
else: internalError(n.info, "genObjectFields")
else: internalError(m.config, n.info, "genObjectFields")
proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) =
if typ.kind == tyObject:
if incompleteType(typ):
localError(info, "request for RTTI generation for incomplete object: " &
localError(m.config, info, "request for RTTI generation for incomplete object: " &
typeToString(typ))
genTypeInfoAux(m, typ, origType, name, info)
else:
@ -1171,19 +1171,15 @@ proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ.sons[1], info), info)
proc fakeClosureType(owner: PSym): PType =
proc fakeClosureType(m: BModule; owner: PSym): PType =
# we generate the same RTTI as for a tuple[pointer, ref tuple[]]
result = newType(tyTuple, owner)
result.rawAddSon(newType(tyPointer, owner))
var r = newType(tyRef, owner)
let obj = createObj(owner, owner.info, final=false)
let obj = createObj(m.g.graph, owner, owner.info, final=false)
r.rawAddSon(obj)
result.rawAddSon(r)
type
TTypeInfoReason = enum ## for what do we need the type info?
tiNew, ## for 'new'
include ccgtrav
proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
@ -1227,24 +1223,24 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
m.g.typeInfoMarker[sig] = result
case t.kind
of tyEmpty, tyVoid: result = rope"0"
of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar:
of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar, tyLent:
genTypeInfoAuxBase(m, t, t, result, rope"0", info)
of tyStatic:
if t.n != nil: result = genTypeInfo(m, lastSon t, info)
else: internalError("genTypeInfo(" & $t.kind & ')')
else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
of tyUserTypeClasses:
internalAssert t.isResolvedUserTypeClass
internalAssert m.config, t.isResolvedUserTypeClass
return genTypeInfo(m, t.lastSon, info)
of tyProc:
if t.callConv != ccClosure:
genTypeInfoAuxBase(m, t, t, result, rope"0", info)
else:
let x = fakeClosureType(t.owner)
let x = fakeClosureType(m, t.owner)
genTupleInfo(m, x, x, result, info)
of tySequence, tyRef, tyOptAsRef:
genTypeInfoAux(m, t, t, result, info)
if gSelectedGC >= gcMarkAndSweep:
let markerProc = genTraverseProc(m, origType, sig, tiNew)
if m.config.selectedGC >= gcMarkAndSweep:
let markerProc = genTraverseProc(m, origType, sig)
addf(m.s[cfsTypeInit3], "$1.marker = $2;$n", [result, markerProc])
of tyPtr, tyRange: genTypeInfoAux(m, t, t, result, info)
of tyArray: genArrayInfo(m, t, result, info)
@ -1257,7 +1253,7 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope =
# BUGFIX: use consistently RTTI without proper field names; otherwise
# results are not deterministic!
genTupleInfo(m, t, origType, result, info)
else: internalError("genTypeInfo(" & $t.kind & ')')
else: internalError(m.config, "genTypeInfo(" & $t.kind & ')')
if t.deepCopy != nil:
genDeepCopyProc(m, t.deepCopy, result)
elif origType.deepCopy != nil:

View file

@ -53,7 +53,7 @@ proc hashString*(s: string): BiggestInt =
result = a
var
gTypeTable: array[TTypeKind, TIdTable]
gTypeTable: array[TTypeKind, TIdTable] # XXX globals here
gCanonicalTypes: array[TTypeKind, PType]
proc initTypeTables() =
@ -110,13 +110,13 @@ proc getUniqueType*(key: PType): PType =
of tyDistinct:
if key.deepCopy != nil: result = key
else: result = getUniqueType(lastSon(key))
of tyGenericInst, tyOrdinal, tyStatic, tyAlias, tyInferred:
of tyGenericInst, tyOrdinal, tyStatic, tyAlias, tySink, tyInferred:
result = getUniqueType(lastSon(key))
#let obj = lastSon(key)
#if obj.sym != nil and obj.sym.name.s == "TOption":
# echo "for ", typeToString(key), " I returned "
# debug result
of tyPtr, tyRef, tyVar:
of tyPtr, tyRef, tyVar, tyLent:
let elemType = lastSon(key)
if elemType.kind in {tyBool, tyChar, tyInt..tyUInt64}:
# no canonicalization for integral types, so that e.g. ``ptr pid_t`` is
@ -211,8 +211,4 @@ proc mangle*(name: string): string =
if requiresUnderscore:
result.add "_"
proc emitLazily*(s: PSym): bool {.inline.} =
result = optDeadCodeElim in gGlobalOptions or
sfDeadCodeElim in getModule(s).flags
initTypeTables()

View file

@ -11,7 +11,7 @@
import
ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
nversion, nimsets, msgs, securehash, bitsets, idents, types,
nversion, nimsets, msgs, std / sha1, bitsets, idents, types,
ccgutils, os, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
lowerings, semparallel, tables, sets, ndi
@ -19,6 +19,7 @@ import
import strutils except `%` # collides with ropes.`%`
from modulegraphs import ModuleGraph
from configuration import warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated
import dynlib
when not declared(dynlib.libCandidates):
@ -92,6 +93,7 @@ proc useHeader(m: BModule, sym: PSym) =
proc cgsym(m: BModule, name: string): Rope
proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
assert m != nil
var i = 0
var length = len(frmt)
result = nil
@ -115,15 +117,15 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
if i >= length or not (frmt[i] in {'0'..'9'}): break
num = j
if j > high(args) + 1:
internalError("ropes: invalid format string $" & $j)
internalError(m.config, "ropes: invalid format string $" & $j)
add(result, args[j-1])
of 'n':
if optLineDir notin gOptions: add(result, rnl)
if optLineDir notin m.config.options: add(result, rnl)
inc(i)
of 'N':
add(result, rnl)
inc(i)
else: internalError("ropes: invalid format string $" & frmt[i])
else: internalError(m.config, "ropes: invalid format string $" & frmt[i])
elif frmt[i] == '#' and frmt[i+1] in IdentStartChars:
inc(i)
var j = i
@ -145,15 +147,10 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
if i - 1 >= start:
add(result, substr(frmt, start, i - 1))
template rfmt(m: BModule, fmt: string, args: varargs[Rope]): untyped =
ropecg(m, fmt, args)
var indent = "\t".rope
proc indentLine(p: BProc, r: Rope): Rope =
result = r
for i in countup(0, p.blocks.len-1):
prepend(result, indent)
prepend(result, "\t".rope)
proc appcg(m: BModule, c: var Rope, frmt: FormatStr,
args: varargs[Rope]) =
@ -189,14 +186,14 @@ proc safeLineNm(info: TLineInfo): int =
result = toLinenumber(info)
if result < 0: result = 0 # negative numbers are not allowed in #line
proc genCLineDir(r: var Rope, filename: string, line: int) =
proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
assert line >= 0
if optLineDir in gOptions:
if optLineDir in conf.options:
addf(r, "$N#line $2 $1$N",
[rope(makeSingleLineCString(filename)), rope(line)])
proc genCLineDir(r: var Rope, info: TLineInfo) =
genCLineDir(r, info.toFullPath, info.safeLineNm)
proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
genCLineDir(r, info.toFullPath, info.safeLineNm, conf)
proc freshLineInfo(p: BProc; info: TLineInfo): bool =
if p.lastLineInfo.line != info.line or
@ -213,9 +210,9 @@ proc genLineDir(p: BProc, t: PNode) =
tt = tt.sons[1]
let line = tt.info.safeLineNm
if optEmbedOrigSrc in gGlobalOptions:
add(p.s(cpsStmts), ~"//" & tt.info.sourceLine & rnl)
genCLineDir(p.s(cpsStmts), tt.info.toFullPath, line)
if optEmbedOrigSrc in p.config.globalOptions:
add(p.s(cpsStmts), ~"//" & sourceLine(p.config, tt.info) & rnl)
genCLineDir(p.s(cpsStmts), tt.info.toFullPath, line, p.config)
if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and
(p.prc == nil or sfPure notin p.prc.flags):
if freshLineInfo(p, tt.info):
@ -223,22 +220,27 @@ proc genLineDir(p: BProc, t: PNode) =
line.rope, makeCString(toFilename(tt.info)))
elif ({optLineTrace, optStackTrace} * p.options ==
{optLineTrace, optStackTrace}) and
(p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex >= 0:
(p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX:
if freshLineInfo(p, tt.info):
linefmt(p, cpsStmts, "nimln_($1, $2);$n",
line.rope, tt.info.quotedFilename)
line.rope, quotedFilename(p.config, tt.info))
proc postStmtActions(p: BProc) {.inline.} =
add(p.s(cpsStmts), p.module.injectStmt)
proc accessThreadLocalVar(p: BProc, s: PSym)
proc emulatedThreadVars(): bool {.inline.}
proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
proc genProc(m: BModule, prc: PSym)
template compileToCpp(m: BModule): untyped =
gCmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags
m.config.cmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags
include "ccgtypes.nim"
proc getTempName(m: BModule): Rope =
result = m.tmpBase & rope(m.labels)
inc m.labels
include ccgliterals
include ccgtypes
# ------------------------------ Manager of temporaries ------------------
@ -260,6 +262,11 @@ proc rdCharLoc(a: TLoc): Rope =
proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: TLoc,
takeAddr: bool) =
if p.module.compileToCpp and t.isException and not isDefined(p.config, "noCppExceptions"):
# init vtable in Exception object for polymorphic exceptions
includeHeader(p.module, "<new>")
linefmt(p, section, "new ($1) $2;$n", rdLoc(a), getTypeDesc(p.module, t))
case analyseObjectWithTypeField(t)
of frNone:
discard
@ -312,8 +319,10 @@ proc resetLoc(p: BProc, loc: var TLoc) =
genObjectInit(p, cpsStmts, loc.t, loc, true)
else:
useStringh(p.module)
# array passed as argument decayed into pointer, bug #7332
# so we use getTypeDesc here rather than rdLoc(loc)
linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
addrLoc(loc), rdLoc(loc))
addrLoc(loc), getTypeDesc(p.module, loc.t))
# XXX: We can be extra clever here and call memset only
# on the bytes following the m_type field?
genObjectInit(p, cpsStmts, loc.t, loc, true)
@ -330,7 +339,7 @@ proc constructLoc(p: BProc, loc: TLoc, isTemp = false) =
if not isImportedCppType(typ):
useStringh(p.module)
linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n",
addrLoc(loc), rdLoc(loc))
addrLoc(loc), getTypeDesc(p.module, typ))
genObjectInit(p, cpsStmts, loc.t, loc, true)
proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
@ -361,7 +370,7 @@ proc getIntTemp(p: BProc, result: var TLoc) =
linefmt(p, cpsLocals, "NI $1;$n", result.r)
result.k = locTemp
result.storage = OnStack
result.lode = lodeTyp getSysType(tyInt)
result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo(), tyInt)
result.flags = {}
proc initGCFrame(p: BProc): Rope =
@ -405,7 +414,7 @@ proc assignLocalVar(p: BProc, n: PNode) =
#assert(s.loc.k == locNone) # not yet assigned
# this need not be fulfilled for inline procs; they are regenerated
# for each module that uses them!
let nl = if optLineDir in gOptions: "" else: tnl
let nl = if optLineDir in p.config.options: "" else: tnl
let decl = localVarDecl(p, n) & ";" & nl
line(p, cpsLocals, decl)
localDebugInfo(p, n.sym)
@ -499,24 +508,24 @@ proc initFrame(p: BProc, procname, filename: Rope): Rope =
discard cgsym(p.module, "nimFrame")
if p.maxFrameLen > 0:
discard cgsym(p.module, "VarSlot")
result = rfmt(nil, "\tnimfrs_($1, $2, $3, $4);$n",
result = ropecg(p.module, "\tnimfrs_($1, $2, $3, $4);$n",
procname, filename, p.maxFrameLen.rope,
p.blocks[0].frameLen.rope)
else:
result = rfmt(nil, "\tnimfr_($1, $2);$n", procname, filename)
result = ropecg(p.module, "\tnimfr_($1, $2);$n", procname, filename)
proc initFrameNoDebug(p: BProc; frame, procname, filename: Rope; line: int): Rope =
discard cgsym(p.module, "nimFrame")
addf(p.blocks[0].sections[cpsLocals], "TFrame $1;$n", [frame])
result = rfmt(nil, "\t$1.procname = $2; $1.filename = $3; " &
result = ropecg(p.module, "\t$1.procname = $2; $1.filename = $3; " &
" $1.line = $4; $1.len = -1; nimFrame(&$1);$n",
frame, procname, filename, rope(line))
proc deinitFrameNoDebug(p: BProc; frame: Rope): Rope =
result = rfmt(p.module, "\t#popFrameOfAddr(&$1);$n", frame)
result = ropecg(p.module, "\t#popFrameOfAddr(&$1);$n", frame)
proc deinitFrame(p: BProc): Rope =
result = rfmt(p.module, "\t#popFrame();$n")
result = ropecg(p.module, "\t#popFrame();$n")
include ccgexprs
@ -539,16 +548,18 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
if lib.path.kind in {nkStrLit..nkTripleStrLit}:
var s: TStringSeq = @[]
libCandidates(lib.path.strVal, s)
rawMessage(hintDependency, lib.path.strVal)
rawMessage(m.config, hintDependency, lib.path.strVal)
var loadlib: Rope = nil
for i in countup(0, high(s)):
inc(m.labels)
if i > 0: add(loadlib, "||")
appcg(m, loadlib, "($1 = #nimLoadLibrary((#NimStringDesc*) &$2))$n",
[tmp, getStrLit(m, s[i])])
let n = newStrNode(nkStrLit, s[i])
n.info = lib.path.info
appcg(m, loadlib, "($1 = #nimLoadLibrary($2))$n",
[tmp, genStringLiteral(m, n)])
appcg(m, m.s[cfsDynLibInit],
"if (!($1)) #nimLoadLibraryError((#NimStringDesc*) &$2);$n",
[loadlib, getStrLit(m, lib.path.strVal)])
"if (!($1)) #nimLoadLibraryError($2);$n",
[loadlib, genStringLiteral(m, lib.path)])
else:
var p = newProc(nil, m)
p.options = p.options - {optStackTrace, optEndb}
@ -561,7 +572,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
"if (!($1 = #nimLoadLibrary($2))) #nimLoadLibraryError($2);$n",
[tmp, rdLoc(dest)])
if lib.name == nil: internalError("loadDynamicLib")
if lib.name == nil: internalError(m.config, "loadDynamicLib")
proc mangleDynLibProc(sym: PSym): Rope =
if sfCompilerProc in sym.flags:
@ -592,14 +603,14 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
[tmp, getTypeDesc(m, sym.typ), params, makeCString($extname)]
var last = lastSon(n)
if last.kind == nkHiddenStdConv: last = last.sons[1]
internalAssert(last.kind == nkStrLit)
internalAssert(m.config, last.kind == nkStrLit)
let idx = last.strVal
if idx.len == 0:
add(m.initProc.s(cpsStmts), load)
elif idx.len == 1 and idx[0] in {'0'..'9'}:
add(m.extensionLoaders[idx[0]], load)
else:
internalError(sym.info, "wrong index: " & idx)
internalError(m.config, sym.info, "wrong index: " & idx)
else:
appcg(m, m.s[cfsDynLibInit],
"\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
@ -625,18 +636,18 @@ proc symInDynamicLibPartial(m: BModule, sym: PSym) =
sym.typ.sym = nil # generate a new name
proc cgsym(m: BModule, name: string): Rope =
let sym = magicsys.getCompilerProc(name)
let sym = magicsys.getCompilerProc(m.g.graph, name)
if sym != nil:
case sym.kind
of skProc, skFunc, skMethod, skConverter, skIterator: genProc(m, sym)
of skVar, skResult, skLet: genVarPrototype(m, newSymNode sym)
of skType: discard getTypeDesc(m, sym.typ)
else: internalError("cgsym: " & name & ": " & $sym.kind)
else: internalError(m.config, "cgsym: " & name & ": " & $sym.kind)
else:
# we used to exclude the system module from this check, but for DLL
# generation support this sloppyness leads to hard to detect bugs, so
# we're picky here for the system module too:
rawMessage(errSystemNeeds, name)
rawMessage(m.config, errGenerated, "system module needs: " & name)
result = sym.loc.r
proc generateHeaders(m: BModule) =
@ -662,12 +673,18 @@ proc generateHeaders(m: BModule) =
add(m.s[cfsHeaders], "#undef powerpc" & tnl)
add(m.s[cfsHeaders], "#undef unix" & tnl)
proc openNamespaceNim(): Rope =
result.add("namespace Nim {" & tnl)
proc closeNamespaceNim(): Rope =
result.add("}" & tnl)
proc closureSetup(p: BProc, prc: PSym) =
if tfCapturesEnv notin prc.typ.flags: return
# prc.ast[paramsPos].last contains the type we're after:
var ls = lastSon(prc.ast[paramsPos])
if ls.kind != nkSym:
internalError(prc.info, "closure generation failed")
internalError(p.config, prc.info, "closure generation failed")
var env = ls.sym
#echo "created environment: ", env.id, " for ", prc.name.s
assignLocalVar(p, ls)
@ -707,7 +724,7 @@ proc genProcAux(m: BModule, prc: PSym) =
assert(prc.ast != nil)
if sfPure notin prc.flags and prc.typ.sons[0] != nil:
if resultPos >= prc.ast.len:
internalError(prc.info, "proc has no result symbol")
internalError(m.config, prc.info, "proc has no result symbol")
let resNode = prc.ast.sons[resultPos]
let res = resNode.sym # get result symbol
if not isInvalidReturnType(prc.typ.sons[0]):
@ -722,10 +739,11 @@ proc genProcAux(m: BModule, prc: PSym) =
assignLocalVar(p, resNode)
assert(res.loc.r != nil)
initLocalVar(p, res, immediateAsgn=false)
returnStmt = rfmt(nil, "\treturn $1;$n", rdLoc(res.loc))
returnStmt = ropecg(p.module, "\treturn $1;$n", rdLoc(res.loc))
else:
fillResult(resNode)
assignParam(p, res)
if sfNoInit notin prc.flags: resetLoc(p, res.loc)
if skipTypes(res.typ, abstractInst).kind == tyArray:
#incl(res.loc.flags, lfIndirect)
res.loc.storage = OnUnknown
@ -743,15 +761,15 @@ proc genProcAux(m: BModule, prc: PSym) =
if sfPure in prc.flags:
if hasDeclspec in extccomp.CC[extccomp.cCompiler].props:
header = "__declspec(naked) " & header
generatedProc = rfmt(nil, "$N$1 {$n$2$3$4}$N$N",
generatedProc = ropecg(p.module, "$N$1 {$n$2$3$4}$N$N",
header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts))
else:
generatedProc = rfmt(nil, "$N$1 {$N", header)
generatedProc = ropecg(p.module, "$N$1 {$N", header)
add(generatedProc, initGCFrame(p))
if optStackTrace in prc.options:
add(generatedProc, p.s(cpsLocals))
var procname = makeCString(prc.name.s)
add(generatedProc, initFrame(p, procname, prc.info.quotedFilename))
add(generatedProc, initFrame(p, procname, quotedFilename(p.config, prc.info)))
else:
add(generatedProc, p.s(cpsLocals))
if optProfiler in prc.options:
@ -770,10 +788,10 @@ proc genProcAux(m: BModule, prc: PSym) =
proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
result = (sfCompileToCpp in m.module.flags and
sfCompileToCpp notin sym.getModule().flags and
gCmd != cmdCompileToCpp) or (
m.config.cmd != cmdCompileToCpp) or (
sym.flags * {sfImportc, sfInfixCall, sfCompilerProc} == {sfImportc} and
sym.magic == mNone and
gCmd == cmdCompileToCpp)
m.config.cmd == cmdCompileToCpp)
proc genProcPrototype(m: BModule, sym: PSym) =
useHeader(m, sym)
@ -781,7 +799,7 @@ proc genProcPrototype(m: BModule, sym: PSym) =
if lfDynamicLib in sym.loc.flags:
if getModule(sym).id != m.module.id and
not containsOrIncl(m.declaredThings, sym.id):
add(m.s[cfsVars], rfmt(nil, "extern $1 $2;$n",
add(m.s[cfsVars], ropecg(m, "extern $1 $2;$n",
getTypeDesc(m, sym.loc.t), mangleDynLibProc(sym)))
elif not containsOrIncl(m.declaredProtos, sym.id):
var header = genProcHeader(m, sym)
@ -793,7 +811,7 @@ proc genProcPrototype(m: BModule, sym: PSym) =
header.add(" __attribute__((naked))")
if sfNoReturn in sym.flags and hasAttribute in CC[cCompiler].props:
header.add(" __attribute__((noreturn))")
add(m.s[cfsProcHeaders], rfmt(nil, "$1;$n", header))
add(m.s[cfsProcHeaders], ropecg(m, "$1;$n", header))
proc genProcNoForward(m: BModule, prc: PSym) =
if lfImportCompilerProc in prc.loc.flags:
@ -879,7 +897,7 @@ proc genProc(m: BModule, prc: PSym) =
if not containsOrIncl(m.g.generatedHeader.declaredThings, prc.id):
genProcAux(m.g.generatedHeader, prc)
proc genVarPrototypeAux(m: BModule, n: PNode) =
proc genVarPrototype(m: BModule, n: PNode) =
#assert(sfGlobal in sym.flags)
let sym = n.sym
useHeader(m, sym)
@ -899,17 +917,14 @@ proc genVarPrototypeAux(m: BModule, n: PNode) =
if sfVolatile in sym.flags: add(m.s[cfsVars], " volatile")
addf(m.s[cfsVars], " $1;$n", [sym.loc.r])
proc genVarPrototype(m: BModule, n: PNode) =
genVarPrototypeAux(m, n)
proc addIntTypes(result: var Rope) {.inline.} =
proc addIntTypes(result: var Rope; conf: ConfigRef) {.inline.} =
addf(result, "#define NIM_NEW_MANGLING_RULES" & tnl &
"#define NIM_INTBITS $1" & tnl, [
platform.CPU[targetCPU].intSize.rope])
if optUseNimNamespace in conf.globalOptions: result.add("#define USE_NIM_NAMESPACE" & tnl)
proc getCopyright(cfile: Cfile): Rope =
const copyrightYear = "2017"
if optCompileOnly in gGlobalOptions:
proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope =
if optCompileOnly in conf.globalOptions:
result = ("/* Generated by Nim Compiler v$1 */$N" &
"/* (c) " & copyrightYear & " Andreas Rumpf */$N" &
"/* The generated code is subject to the original license. */$N") %
@ -924,11 +939,11 @@ proc getCopyright(cfile: Cfile): Rope =
rope(platform.OS[targetOS].name),
rope(platform.CPU[targetCPU].name),
rope(extccomp.CC[extccomp.cCompiler].name),
rope(getCompileCFileCmd(cfile))]
rope(getCompileCFileCmd(conf, cfile))]
proc getFileHeader(cfile: Cfile): Rope =
result = getCopyright(cfile)
addIntTypes(result)
proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
result = getCopyright(conf, cfile)
addIntTypes(result, conf)
proc genFilenames(m: BModule): Rope =
discard cgsym(m, "dbgRegisterFilename")
@ -1033,8 +1048,8 @@ proc genMainProc(m: BModule) =
var nimMain, otherMain: FormatStr
if platform.targetOS == osWindows and
gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}:
if optGenGuiApp in gGlobalOptions:
m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
if optGenGuiApp in m.config.globalOptions:
nimMain = WinNimMain
otherMain = WinCMain
else:
@ -1044,7 +1059,7 @@ proc genMainProc(m: BModule) =
elif platform.targetOS == osGenode:
nimMain = GenodeNimMain
otherMain = ComponentConstruct
elif optGenDynLib in gGlobalOptions:
elif optGenDynLib in m.config.globalOptions:
nimMain = PosixNimDllMain
otherMain = PosixCDllMain
elif platform.targetOS == osStandalone:
@ -1054,16 +1069,16 @@ proc genMainProc(m: BModule) =
nimMain = PosixNimMain
otherMain = PosixCMain
if m.g.breakpoints != nil: discard cgsym(m, "dbgRegisterBreakpoint")
if optEndb in gOptions:
if optEndb in m.config.options:
m.g.breakpoints.add(m.genFilenames)
let initStackBottomCall =
if platform.targetOS == osStandalone or gSelectedGC == gcNone: "".rope
if platform.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope
else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
inc(m.labels)
appcg(m, m.s[cfsProcs], PreMainBody, [
m.g.mainDatInit, m.g.breakpoints, m.g.otherModsInit,
if emulatedThreadVars() and platform.targetOS != osStandalone:
if emulatedThreadVars(m.config) and platform.targetOS != osStandalone:
ropecg(m, "\t#initThreadVarsEmulation();$N")
else:
"".rope,
@ -1071,8 +1086,12 @@ proc genMainProc(m: BModule) =
appcg(m, m.s[cfsProcs], nimMain,
[m.g.mainModInit, initStackBottomCall, rope(m.labels)])
if optNoMain notin gGlobalOptions:
if optNoMain notin m.config.globalOptions:
if optUseNimNamespace in m.config.globalOptions:
m.s[cfsProcs].add closeNamespaceNim() & "using namespace Nim;" & tnl
appcg(m, m.s[cfsProcs], otherMain, [])
if optUseNimNamespace in m.config.globalOptions: m.s[cfsProcs].add openNamespaceNim()
proc getSomeInitName(m: PSym, suffix: string): Rope =
assert m.kind == skModule
@ -1080,7 +1099,7 @@ proc getSomeInitName(m: PSym, suffix: string): Rope =
if {sfSystemModule, sfMainModule} * m.flags == {}:
result = m.owner.name.s.mangle.rope
result.add "_"
result.add m.name.s
result.add m.name.s.mangle
result.add suffix
proc getInitName(m: PSym): Rope =
@ -1096,8 +1115,8 @@ proc registerModuleToMain(g: BModuleList; m: PSym) =
var
init = m.getInitName
datInit = m.getDatInitName
addf(g.mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [init])
addf(g.mainModProcs, "NIM_EXTERNC N_NOINLINE(void, $1)(void);$N", [datInit])
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init])
addf(g.mainModProcs, "N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit])
if sfSystemModule notin m.flags:
addf(g.mainDatInit, "\t$1();$N", [datInit])
let initCall = "\t$1();$N" % [init]
@ -1108,7 +1127,7 @@ proc registerModuleToMain(g: BModuleList; m: PSym) =
proc genInitCode(m: BModule) =
var initname = getInitName(m.module)
var prc = "NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N" % [initname]
var prc = "N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N" % [initname]
if m.typeNodes > 0:
appcg(m, m.s[cfsTypeInit1], "static #TNimNode $1[$2];$n",
[m.typeNodesName, rope(m.typeNodes)])
@ -1118,11 +1137,11 @@ proc genInitCode(m: BModule) =
add(prc, initGCFrame(m.initProc))
add(prc, genSectionStart(cpsLocals))
add(prc, genSectionStart(cpsLocals, m.config))
add(prc, m.preInitProc.s(cpsLocals))
add(prc, m.initProc.s(cpsLocals))
add(prc, m.postInitProc.s(cpsLocals))
add(prc, genSectionEnd(cpsLocals))
add(prc, genSectionEnd(cpsLocals, m.config))
if optStackTrace in m.initProc.options and frameDeclared notin m.flags:
# BUT: the generated init code might depend on a current frame, so
@ -1130,33 +1149,33 @@ proc genInitCode(m: BModule) =
incl m.flags, frameDeclared
if preventStackTrace notin m.flags:
var procname = makeCString(m.module.name.s)
add(prc, initFrame(m.initProc, procname, m.module.info.quotedFilename))
add(prc, initFrame(m.initProc, procname, quotedFilename(m.config, m.module.info)))
else:
add(prc, ~"\tTFrame FR_; FR_.len = 0;$N")
add(prc, genSectionStart(cpsInit))
add(prc, genSectionStart(cpsInit, m.config))
add(prc, m.preInitProc.s(cpsInit))
add(prc, m.initProc.s(cpsInit))
add(prc, m.postInitProc.s(cpsInit))
add(prc, genSectionEnd(cpsInit))
add(prc, genSectionEnd(cpsInit, m.config))
add(prc, genSectionStart(cpsStmts))
add(prc, genSectionStart(cpsStmts, m.config))
add(prc, m.preInitProc.s(cpsStmts))
add(prc, m.initProc.s(cpsStmts))
add(prc, m.postInitProc.s(cpsStmts))
add(prc, genSectionEnd(cpsStmts))
add(prc, genSectionEnd(cpsStmts, m.config))
if optStackTrace in m.initProc.options and preventStackTrace notin m.flags:
add(prc, deinitFrame(m.initProc))
add(prc, deinitGCFrame(m.initProc))
addf(prc, "}$N$N", [])
prc.addf("NIM_EXTERNC N_NOINLINE(void, $1)(void) {$N",
prc.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void) {$N",
[getDatInitName(m.module)])
for i in cfsTypeInit1..cfsDynLibInit:
add(prc, genSectionStart(i))
add(prc, genSectionStart(i, m.config))
add(prc, m.s[i])
add(prc, genSectionEnd(i))
add(prc, genSectionEnd(i, m.config))
addf(prc, "}$N$N", [])
# we cannot simply add the init proc to ``m.s[cfsProcs]`` anymore because
@ -1171,16 +1190,19 @@ proc genInitCode(m: BModule) =
add(m.s[cfsInitProc], ex)
proc genModule(m: BModule, cfile: Cfile): Rope =
result = getFileHeader(cfile)
result = getFileHeader(m.config, cfile)
result.add(genMergeInfo(m))
generateThreadLocalStorage(m)
generateHeaders(m)
for i in countup(cfsHeaders, cfsProcs):
add(result, genSectionStart(i))
add(result, genSectionStart(i, m.config))
add(result, m.s[i])
add(result, genSectionEnd(i))
add(result, genSectionEnd(i, m.config))
if optUseNimNamespace in m.config.globalOptions and i == cfsHeaders:
result.add openNamespaceNim()
add(result, m.s[cfsInitProc])
if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim()
proc newPreInitProc(m: BModule): BProc =
result = newProc(nil, m)
@ -1193,10 +1215,12 @@ proc newPostInitProc(m: BModule): BProc =
result.labels = 200_000
proc initProcOptions(m: BModule): TOptions =
if sfSystemModule in m.module.flags: gOptions-{optStackTrace} else: gOptions
let opts = m.config.options
if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
new(result)
result.g = g
result.tmpBase = rope("TM" & $hashOwner(module) & "_")
result.headerFiles = @[]
result.declaredThings = initIntSet()
@ -1217,14 +1241,13 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: string): BModule =
result.forwardedProcs = @[]
result.typeNodesName = getTempName(result)
result.nimTypesName = getTempName(result)
result.g = g
# no line tracing for the init sections of the system module so that we
# don't generate a TFrame which can confuse the stack botton initialization:
if sfSystemModule in module.flags:
incl result.flags, preventStackTrace
excl(result.preInitProc.options, optStackTrace)
excl(result.postInitProc.options, optStackTrace)
let ndiName = if optCDebug in gGlobalOptions: changeFileExt(completeCFilePath(filename), "ndi")
let ndiName = if optCDebug in g.config.globalOptions: changeFileExt(completeCFilePath(g.config, filename), "ndi")
else: ""
open(result.ndi, ndiName)
@ -1277,7 +1300,7 @@ proc resetCgenModules*(g: BModuleList) =
for m in cgenModules(g): resetModule(m)
proc rawNewModule(g: BModuleList; module: PSym): BModule =
result = rawNewModule(g, module, module.position.int32.toFullPath)
result = rawNewModule(g, module, module.position.FileIndex.toFullPath)
proc newModule(g: BModuleList; module: PSym): BModule =
# we should create only one cgen module for each module sym
@ -1285,22 +1308,19 @@ proc newModule(g: BModuleList; module: PSym): BModule =
growCache g.modules, module.position
g.modules[module.position] = result
if (optDeadCodeElim in gGlobalOptions):
if (sfDeadCodeElim in module.flags):
internalError("added pending module twice: " & module.filename)
template injectG(config) {.dirty.} =
template injectG() {.dirty.} =
if graph.backend == nil:
graph.backend = newModuleList(config)
graph.backend = newModuleList(graph)
let g = BModuleList(graph.backend)
proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
injectG(graph.config)
injectG()
result = newModule(g, module)
if optGenIndex in gGlobalOptions and g.generatedHeader == nil:
let f = if graph.config.headerFile.len > 0: graph.config.headerFile else: gProjectFull
if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil:
let f = if graph.config.headerFile.len > 0: graph.config.headerFile
else: graph.config.projectFull
g.generatedHeader = rawNewModule(g, module,
changeFileExt(completeCFilePath(f), hExt))
changeFileExt(completeCFilePath(graph.config, f), hExt))
incl g.generatedHeader.flags, isHeaderFile
proc writeHeader(m: BModule) =
@ -1311,41 +1331,44 @@ proc writeHeader(m: BModule) =
var guard = "__$1__" % [m.filename.splitFile.name.rope]
result.addf("#ifndef $1$n#define $1$n", [guard])
addIntTypes(result)
addIntTypes(result, m.config)
generateHeaders(m)
generateThreadLocalStorage(m)
for i in countup(cfsHeaders, cfsProcs):
add(result, genSectionStart(i))
add(result, genSectionStart(i, m.config))
add(result, m.s[i])
add(result, genSectionEnd(i))
add(result, genSectionEnd(i, m.config))
if optUseNimNamespace in m.config.globalOptions and i == cfsHeaders: result.add openNamespaceNim()
add(result, m.s[cfsInitProc])
if optGenDynLib in gGlobalOptions:
if optGenDynLib in m.config.globalOptions:
result.add("N_LIB_IMPORT ")
result.addf("N_CDECL(void, NimMain)(void);$n", [])
if optUseNimNamespace in m.config.globalOptions: result.add closeNamespaceNim()
result.addf("#endif /* $1 */$n", [guard])
writeRope(result, m.filename)
proc getCFile(m: BModule): string =
let ext =
if m.compileToCpp: ".cpp"
elif gCmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m"
elif m.config.cmd == cmdCompileToOC or sfCompileToObjC in m.module.flags: ".m"
else: ".c"
result = changeFileExt(completeCFilePath(m.cfilename.withPackageName), ext)
result = changeFileExt(completeCFilePath(m.config, withPackageName(m.config, m.cfilename)), ext)
proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext =
injectG(graph.config)
injectG()
var m = newModule(g, module)
readMergeInfo(getCFile(m), m)
result = m
proc myProcess(b: PPassContext, n: PNode): PNode =
result = n
if b == nil or passes.skipCodegen(n): return
if b == nil: return
var m = BModule(b)
if passes.skipCodegen(m.config, n): return
m.initProc.options = initProcOptions(m)
softRnl = if optLineDir in gOptions: noRnl else: rnl
softRnl = if optLineDir in m.config.options: noRnl else: rnl
genStmts(m.initProc, n)
proc finishModule(m: BModule) =
@ -1355,18 +1378,18 @@ proc finishModule(m: BModule) =
# a ``for`` loop here
var prc = m.forwardedProcs[i]
if sfForward in prc.flags:
internalError(prc.info, "still forwarded: " & prc.name.s)
internalError(m.config, prc.info, "still forwarded: " & prc.name.s)
genProcNoForward(m, prc)
inc(i)
assert(m.g.forwardedProcsCounter >= i)
dec(m.g.forwardedProcsCounter, i)
setLen(m.forwardedProcs, 0)
proc shouldRecompile(code: Rope, cfile: Cfile): bool =
proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
result = true
if optForceFullMake notin gGlobalOptions:
if optForceFullMake notin m.config.globalOptions:
if not equalsFile(code, cfile.cname):
if isDefined("nimdiff"):
if isDefined(m.config, "nimdiff"):
if fileExists(cfile.cname):
copyFile(cfile.cname, cfile.cname & ".backup")
echo "diff ", cfile.cname, ".backup ", cfile.cname
@ -1389,7 +1412,7 @@ proc writeModule(m: BModule, pending: bool) =
# generate code for the init statements of the module:
let cfile = getCFile(m)
if m.rd == nil or optForceFullMake in gGlobalOptions:
if m.rd == nil or optForceFullMake in m.config.globalOptions:
genInitCode(m)
finishTypeDescriptions(m)
if sfMainModule in m.module.flags:
@ -1397,35 +1420,35 @@ proc writeModule(m: BModule, pending: bool) =
add(m.s[cfsProcHeaders], m.g.mainModProcs)
generateThreadVarsSize(m)
var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
var code = genModule(m, cf)
when hasTinyCBackend:
if gCmd == cmdRun:
if conf.cmd == cmdRun:
tccgen.compileCCode($code)
return
if not shouldRecompile(code, cf): cf.flags = {CfileFlag.Cached}
addFileToCompile(cf)
if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached}
addFileToCompile(m.config, cf)
elif pending and mergeRequired(m) and sfMainModule notin m.module.flags:
let cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
let cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
mergeFiles(cfile, m)
genInitCode(m)
finishTypeDescriptions(m)
var code = genModule(m, cf)
writeRope(code, cfile)
addFileToCompile(cf)
addFileToCompile(m.config, cf)
else:
# Consider: first compilation compiles ``system.nim`` and produces
# ``system.c`` but then compilation fails due to an error. This means
# that ``system.o`` is missing, so we need to call the C compiler for it:
var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
if not existsFile(cf.obj): cf.flags = {CfileFlag.Cached}
addFileToCompile(cf)
addFileToCompile(m.config, cf)
close(m.ndi)
proc updateCachedModule(m: BModule) =
let cfile = getCFile(m)
var cf = Cfile(cname: cfile, obj: completeCFilePath(toObjFile(cfile)), flags: {})
var cf = Cfile(cname: cfile, obj: completeCFilePath(m.config, toObjFile(m.config, cfile)), flags: {})
if mergeRequired(m) and sfMainModule notin m.module.flags:
mergeFiles(cfile, m)
@ -1436,12 +1459,13 @@ proc updateCachedModule(m: BModule) =
writeRope(code, cfile)
else:
cf.flags = {CfileFlag.Cached}
addFileToCompile(cf)
addFileToCompile(m.config, cf)
proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
result = n
if b == nil or passes.skipCodegen(n): return
if b == nil: return
var m = BModule(b)
if passes.skipCodegen(m.config, n): return
# if the module is cached, we don't regenerate the main proc
# nor the dispatchers? But if the dispatchers changed?
# XXX emit the dispatchers into its own .c file?
@ -1475,7 +1499,7 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
m.updateCachedModule
else:
m.writeModule(pending=true)
writeMapping(g.mapping)
writeMapping(config, g.mapping)
if g.generatedHeader != nil: writeHeader(g.generatedHeader)
const cgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)

View file

@ -14,6 +14,7 @@ import
tables, ndi
from msgs import TLineInfo
from modulegraphs import ModuleGraph
type
TLabel* = Rope # for the C generator a label is just a rope
@ -70,11 +71,10 @@ type
threadVarAccessed*: bool # true if the proc already accessed some threadvar
lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements
currLineInfo*: TLineInfo # AST codegen will make this superfluous
nestedTryStmts*: seq[PNode] # in how many nested try statements we are
# (the vars must be volatile then)
inExceptBlock*: int # are we currently inside an except block?
# leaving such scopes by raise or by return must
# execute any applicable finally blocks
nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]]
# in how many nested try statements we are
# (the vars must be volatile then)
# bool is true when are in the except part of a try block
finallySafePoints*: seq[Rope] # For correctly cleaning up exceptions when
# using return in finally statements
labels*: Natural # for generating unique labels in the C proc
@ -117,6 +117,8 @@ type
breakpoints*: Rope # later the breakpoints are inserted into the main proc
typeInfoMarker*: TypeCache
config*: ConfigRef
graph*: ModuleGraph
strVersion*, seqVersion*: int # version of the string/seq implementation to use
TCGen = object of TPassContext # represents a C source file
s*: TCFileSections # sections of the C file
@ -148,6 +150,9 @@ type
g*: BModuleList
ndi*: NdiFile
template config*(m: BModule): ConfigRef = m.g.config
template config*(p: BProc): ConfigRef = p.module.g.config
proc includeHeader*(this: BModule; header: string) =
if not this.headerFiles.contains header:
this.headerFiles.add header
@ -165,14 +170,15 @@ proc newProc*(prc: PSym, module: BModule): BProc =
result.prc = prc
result.module = module
if prc != nil: result.options = prc.options
else: result.options = gOptions
else: result.options = module.config.options
newSeq(result.blocks, 1)
result.nestedTryStmts = @[]
result.finallySafePoints = @[]
result.sigConflicts = initCountTable[string]()
proc newModuleList*(config: ConfigRef): BModuleList =
BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: config)
proc newModuleList*(g: ModuleGraph): BModuleList =
BModuleList(modules: @[], typeInfoMarker: initTable[SigHash, Rope](), config: g.config,
graph: g)
iterator cgenModules*(g: BModuleList): BModule =
for i in 0..high(g.modules):

View file

@ -11,9 +11,9 @@
import
intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
sempass2, strutils, modulegraphs
sempass2, strutils, modulegraphs, configuration
proc genConv(n: PNode, d: PType, downcast: bool): PNode =
proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
var dest = skipTypes(d, abstractPtrs)
var source = skipTypes(n.typ, abstractPtrs)
if (source.kind == tyObject) and (dest.kind == tyObject):
@ -24,12 +24,12 @@ proc genConv(n: PNode, d: PType, downcast: bool): PNode =
elif diff < 0:
result = newNodeIT(nkObjUpConv, n.info, d)
addSon(result, n)
if downcast: internalError(n.info, "cgmeth.genConv: no upcast allowed")
if downcast: internalError(conf, n.info, "cgmeth.genConv: no upcast allowed")
elif diff > 0:
result = newNodeIT(nkObjDownConv, n.info, d)
addSon(result, n)
if not downcast:
internalError(n.info, "cgmeth.genConv: no downcast allowed")
internalError(conf, n.info, "cgmeth.genConv: no downcast allowed")
else:
result = n
else:
@ -42,7 +42,7 @@ proc getDispatcher*(s: PSym): PSym =
let disp = dispn.sym
if sfDispatcher in disp.flags: result = disp
proc methodCall*(n: PNode): PNode =
proc methodCall*(n: PNode; conf: ConfigRef): PNode =
result = n
# replace ordinary method by dispatcher method:
let disp = getDispatcher(result.sons[0].sym)
@ -50,9 +50,9 @@ proc methodCall*(n: PNode): PNode =
result.sons[0].sym = disp
# change the arguments to up/downcasts to fit the dispatcher's parameters:
for i in countup(1, sonsLen(result)-1):
result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true)
result.sons[i] = genConv(result.sons[i], disp.typ.sons[i], true, conf)
else:
localError(n.info, "'" & $result.sons[0] & "' lacks a dispatcher")
localError(conf, n.info, "'" & $result.sons[0] & "' lacks a dispatcher")
type
MethodResult = enum No, Invalid, Yes
@ -68,7 +68,7 @@ proc sameMethodBucket(a, b: PSym): MethodResult =
while true:
aa = skipTypes(aa, {tyGenericInst, tyAlias})
bb = skipTypes(bb, {tyGenericInst, tyAlias})
if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef}:
if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent}:
aa = aa.lastSon
bb = bb.lastSon
else:
@ -130,7 +130,7 @@ proc createDispatcher(s: PSym): PSym =
attachDispatcher(disp, newSymNode(disp))
return disp
proc fixupDispatcher(meth, disp: PSym) =
proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
# We may have constructed the dispatcher from a method prototype
# and need to augment the incomplete dispatcher with information
# from later definitions, particularly the resultPos slot. Also,
@ -149,7 +149,7 @@ proc fixupDispatcher(meth, disp: PSym) =
disp.typ.lockLevel = meth.typ.lockLevel
elif meth.typ.lockLevel != UnspecifiedLockLevel and
meth.typ.lockLevel != disp.typ.lockLevel:
message(meth.info, warnLockLevel,
message(conf, meth.info, warnLockLevel,
"method has lock level $1, but another method has $2" %
[$meth.typ.lockLevel, $disp.typ.lockLevel])
# XXX The following code silences a duplicate warning in
@ -166,13 +166,13 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
of Yes:
add(g.methods[i].methods, s)
attachDispatcher(s, lastSon(disp.ast))
fixupDispatcher(s, disp)
fixupDispatcher(s, disp, g.config)
#echo "fixup ", disp.name.s, " ", disp.id
when useEffectSystem: checkMethodEffects(disp, s)
when useEffectSystem: checkMethodEffects(g, disp, s)
if {sfBase, sfFromGeneric} * s.flags == {sfBase} and
g.methods[i].methods[0] != s:
# already exists due to forwarding definition?
localError(s.info, "method is not a base")
localError(g.config, s.info, "method is not a base")
return
of No: discard
of Invalid:
@ -183,10 +183,10 @@ proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) =
#if fromCache:
# internalError(s.info, "no method dispatcher found")
if witness != nil:
localError(s.info, "invalid declaration order; cannot attach '" & s.name.s &
localError(g.config, s.info, "invalid declaration order; cannot attach '" & s.name.s &
"' to method defined here: " & $witness.info)
elif sfBase notin s.flags:
message(s.info, warnUseBase)
message(g.config, s.info, warnUseBase)
proc relevantCol(methods: TSymSeq, col: int): bool =
# returns true iff the position is relevant
@ -225,32 +225,33 @@ proc sortBucket(a: var TSymSeq, relevantCols: IntSet) =
a[j] = v
if h == 1: break
proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
proc genDispatcher(g: ModuleGraph; methods: TSymSeq, relevantCols: IntSet): PSym =
var base = lastSon(methods[0].ast).sym
result = base
var paramLen = sonsLen(base.typ)
var nilchecks = newNodeI(nkStmtList, base.info)
var disp = newNodeI(nkIfStmt, base.info)
var ands = getSysSym("and")
var iss = getSysSym("of")
var ands = getSysSym(g, unknownLineInfo(), "and")
var iss = getSysSym(g, unknownLineInfo(), "of")
let boolType = getSysType(g, unknownLineInfo(), tyBool)
for col in countup(1, paramLen - 1):
if contains(relevantCols, col):
let param = base.typ.n.sons[col].sym
if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
addSon(nilchecks, newTree(nkCall,
newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(param)))
for meth in countup(0, high(methods)):
var curr = methods[meth] # generate condition:
var cond: PNode = nil
for col in countup(1, paramLen - 1):
if contains(relevantCols, col):
var isn = newNodeIT(nkCall, base.info, getSysType(tyBool))
var isn = newNodeIT(nkCall, base.info, boolType)
addSon(isn, newSymNode(iss))
let param = base.typ.n.sons[col].sym
addSon(isn, newSymNode(param))
addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
if cond != nil:
var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
var a = newNodeIT(nkCall, base.info, boolType)
addSon(a, newSymNode(ands))
addSon(a, cond)
addSon(a, isn)
@ -262,7 +263,7 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
addSon(call, newSymNode(curr))
for col in countup(1, paramLen - 1):
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
curr.typ.sons[col], false))
curr.typ.sons[col], false, g.config))
var ret: PNode
if retTyp != nil:
var a = newNodeI(nkFastAsgn, base.info)
@ -290,4 +291,4 @@ proc generateMethodDispatchers*(g: ModuleGraph): PNode =
if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col)
sortBucket(g.methods[bucket].methods, relevantCols)
addSon(result,
newSymNode(genDispatcher(g.methods[bucket].methods, relevantCols)))
newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols)))

1287
compiler/closureiters.nim Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -12,78 +12,30 @@
import
strtabs, platform, strutils, idents
# We need to use a StringTableRef here as defined symbols are always guaranteed
# to be style insensitive. Otherwise hell would break lose.
var gSymbols: StringTableRef
const
catNone = "false"
proc defineSymbol*(symbol: string, value: string = "true") =
gSymbols[symbol] = value
proc defineSymbol*(symbols: StringTableRef; symbol: string, value: string = "true") =
symbols[symbol] = value
proc undefSymbol*(symbol: string) =
gSymbols[symbol] = catNone
proc undefSymbol*(symbols: StringTableRef; symbol: string) =
symbols[symbol] = catNone
proc isDefined*(symbol: string): bool =
if gSymbols.hasKey(symbol):
result = gSymbols[symbol] != catNone
elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
result = true
elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0:
result = true
else:
case symbol.normalize
of "x86": result = targetCPU == cpuI386
of "itanium": result = targetCPU == cpuIa64
of "x8664": result = targetCPU == cpuAmd64
of "posix", "unix":
result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
osQnx, osAtari, osAix,
osHaiku, osVxWorks, osSolaris, osNetbsd,
osFreebsd, osOpenbsd, osDragonfly, osMacosx,
osAndroid}
of "linux":
result = targetOS in {osLinux, osAndroid}
of "bsd":
result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
of "emulatedthreadvars":
result = platform.OS[targetOS].props.contains(ospLacksThreadVars)
of "msdos": result = targetOS == osDos
of "mswindows", "win32": result = targetOS == osWindows
of "macintosh": result = targetOS in {osMacos, osMacosx}
of "sunos": result = targetOS == osSolaris
of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian
of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian
of "cpu8": result = CPU[targetCPU].bit == 8
of "cpu16": result = CPU[targetCPU].bit == 16
of "cpu32": result = CPU[targetCPU].bit == 32
of "cpu64": result = CPU[targetCPU].bit == 64
of "nimrawsetjmp":
result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
osDragonfly, osMacosx}
else: discard
#proc lookupSymbol*(symbols: StringTableRef; symbol: string): string =
# result = if isDefined(symbol): gSymbols[symbol] else: nil
proc isDefined*(symbol: PIdent): bool = isDefined(symbol.s)
proc lookupSymbol*(symbol: string): string =
result = if isDefined(symbol): gSymbols[symbol] else: nil
proc lookupSymbol*(symbol: PIdent): string = lookupSymbol(symbol.s)
iterator definedSymbolNames*: string =
for key, val in pairs(gSymbols):
iterator definedSymbolNames*(symbols: StringTableRef): string =
for key, val in pairs(symbols):
if val != catNone: yield key
proc countDefinedSymbols*(): int =
proc countDefinedSymbols*(symbols: StringTableRef): int =
result = 0
for key, val in pairs(gSymbols):
for key, val in pairs(symbols):
if val != catNone: inc(result)
proc initDefines*() =
gSymbols = newStringTable(modeStyleInsensitive)
defineSymbol("nimrod") # 'nimrod' is always defined
proc initDefines*(symbols: StringTableRef) =
# for bootstrapping purposes and old code:
template defineSymbol(s) = symbols.defineSymbol(s)
defineSymbol("nimhygiene")
defineSymbol("niminheritable")
defineSymbol("nimmixin")
@ -112,3 +64,9 @@ proc initDefines*() =
defineSymbol("nimNewRoof")
defineSymbol("nimHasRunnableExamples")
defineSymbol("nimNewDot")
defineSymbol("nimHasNilChecks")
defineSymbol("nimSymKind")
defineSymbol("nimVmEqIdent")
defineSymbol("nimNoNil")
defineSymbol("nimNoZeroTerminator")
defineSymbol("nimNotNil")

360
compiler/configuration.nim Normal file
View file

@ -0,0 +1,360 @@
#
#
# The Nim Compiler
# (c) Copyright 2018 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module contains the rather excessive configuration object that
## needs to be passed around to everything so that the compiler becomes
## more useful as a library.
import tables
const
explanationsBaseUrl* = "https://nim-lang.org/docs/manual"
type
TMsgKind* = enum
errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile,
errXExpected,
errGridTableNotImplemented,
errGeneralParseError,
errNewSectionExpected,
errInvalidDirectiveX,
errGenerated,
errUser,
warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
warnDeprecated, warnConfigDeprecated,
warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel,
warnUnknownSubstitutionX, warnLanguageXNotSupported,
warnFieldXNotSupported, warnCommentXIgnored,
warnTypelessParam,
warnUseBase, warnWriteToForeignHeap, warnUnsafeCode,
warnEachIdentIsTuple, warnShadowIdent,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
warnInconsistentSpacing, warnUser,
hintSuccess, hintSuccessX,
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled,
hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath,
hintConditionAlwaysTrue, hintName, hintPattern,
hintExecuting, hintLinking, hintDependency,
hintSource, hintPerformance, hintStackTrace, hintGCStats,
hintUser, hintUserRaw
const
MsgKindToStr*: array[TMsgKind, string] = [
errUnknown: "unknown error",
errInternal: "internal error: $1",
errIllFormedAstX: "illformed AST: $1",
errCannotOpenFile: "cannot open '$1'",
errXExpected: "'$1' expected",
errGridTableNotImplemented: "grid table is not implemented",
errGeneralParseError: "general parse error",
errNewSectionExpected: "new section expected",
errInvalidDirectiveX: "invalid directive: '$1'",
errGenerated: "$1",
errUser: "$1",
warnCannotOpenFile: "cannot open '$1'",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
warnXIsNeverRead: "'$1' is never read",
warnXmightNotBeenInit: "'$1' might not have been initialized",
warnDeprecated: "$1 is deprecated",
warnConfigDeprecated: "config file '$1' is deprecated",
warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)",
warnUnknownMagic: "unknown magic '$1' might crash the compiler",
warnRedefinitionOfLabel: "redefinition of label '$1'",
warnUnknownSubstitutionX: "unknown substitution '$1'",
warnLanguageXNotSupported: "language '$1' not supported",
warnFieldXNotSupported: "field '$1' not supported",
warnCommentXIgnored: "comment '$1' ignored",
warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'",
warnUseBase: "use {.base.} for base methods; baseless methods are deprecated",
warnWriteToForeignHeap: "write to foreign heap",
warnUnsafeCode: "unsafe code: '$1'",
warnEachIdentIsTuple: "each identifier is a tuple",
warnShadowIdent: "shadowed identifier: '$1'",
warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
warnProveField: "cannot prove that field '$1' is accessible",
warnProveIndex: "cannot prove index '$1' is valid",
warnGcUnsafe: "not GC-safe: '$1'",
warnGcUnsafe2: "$1",
warnUninit: "'$1' might not have been initialized",
warnGcMem: "'$1' uses GC'ed memory",
warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.",
warnLockLevel: "$1",
warnResultShadowed: "Special variable 'result' is shadowed.",
warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
warnUser: "$1",
hintSuccess: "operation successful",
hintSuccessX: "operation successful ($# lines compiled; $# sec total; $#; $#)",
hintLineTooLong: "line too long",
hintXDeclaredButNotUsed: "'$1' is declared but not used",
hintConvToBaseNotNeeded: "conversion to base object is not needed",
hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless",
hintExprAlwaysX: "expression evaluates always to '$1'",
hintQuitCalled: "quit() called",
hintProcessing: "$1",
hintCodeBegin: "generated code listing:",
hintCodeEnd: "end of listing",
hintConf: "used config file '$1'",
hintPath: "added path: '$1'",
hintConditionAlwaysTrue: "condition is always true: '$1'",
hintName: "name should be: '$1'",
hintPattern: "$1",
hintExecuting: "$1",
hintLinking: "",
hintDependency: "$1",
hintSource: "$1",
hintPerformance: "$1",
hintStackTrace: "$1",
hintGCStats: "$1",
hintUser: "$1",
hintUserRaw: "$1"]
const
WarningsToStr* = ["CannotOpenFile", "OctalEscape",
"XIsNeverRead", "XmightNotBeenInit",
"Deprecated", "ConfigDeprecated",
"SmallLshouldNotBeUsed", "UnknownMagic",
"RedefinitionOfLabel", "UnknownSubstitutionX",
"LanguageXNotSupported", "FieldXNotSupported",
"CommentXIgnored",
"TypelessParam", "UseBase", "WriteToForeignHeap",
"UnsafeCode", "EachIdentIsTuple", "ShadowIdent",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
"Spacing", "User"]
HintsToStr* = ["Success", "SuccessX", "LineTooLong",
"XDeclaredButNotUsed", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded",
"ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf",
"Path", "CondTrue", "Name", "Pattern", "Exec", "Link", "Dependency",
"Source", "Performance", "StackTrace", "GCStats",
"User", "UserRaw"]
const
fatalMin* = errUnknown
fatalMax* = errInternal
errMin* = errUnknown
errMax* = errUser
warnMin* = warnCannotOpenFile
warnMax* = pred(hintSuccess)
hintMin* = hintSuccess
hintMax* = high(TMsgKind)
static:
doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1
doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1
type
TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints
TNoteKinds* = set[TNoteKind]
const
NotesVerbosity*: array[0..3, TNoteKinds] = [
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
warnProveField, warnProveIndex,
warnGcUnsafe,
hintSuccessX, hintPath, hintConf,
hintProcessing, hintPattern,
hintDependency,
hintExecuting, hintLinking,
hintCodeBegin, hintCodeEnd,
hintSource, hintStackTrace,
hintGCStats},
{low(TNoteKind)..high(TNoteKind)} - {warnShadowIdent, warnUninit,
warnProveField, warnProveIndex,
warnGcUnsafe,
hintPath,
hintDependency,
hintCodeBegin, hintCodeEnd,
hintSource, hintStackTrace,
hintGCStats},
{low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit},
{low(TNoteKind)..high(TNoteKind)}]
const
errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
#[
errStringLiteralExpected: "string literal expected",
errIntLiteralExpected: "integer literal expected",
errIdentifierExpected: "identifier expected, but found '$1'",
errNewlineExpected: "newline expected, but found '$1'",
errInvalidModuleName: "invalid module name: '$1'",
errOnOrOffExpected: "'on' or 'off' expected",
errNoneSpeedOrSizeExpected: "'none', 'speed' or 'size' expected",
errInvalidPragma: "invalid pragma",
errUnknownPragma: "unknown pragma: '$1'",
errAtPopWithoutPush: "'pop' without a 'push' pragma",
errEmptyAsm: "empty asm statement",
errInvalidIndentation: "invalid indentation",
errNoReturnWithReturnTypeNotAllowed: "routines with NoReturn pragma are not allowed to have return type",
errAttemptToRedefine: ,
errStmtInvalidAfterReturn: "statement not allowed after 'return', 'break', 'raise', 'continue' or proc call with noreturn pragma",
errStmtExpected: "statement expected",
errInvalidLabel: "'$1' is no label",
errInvalidCmdLineOption: "invalid command line option: '$1'",
errCmdLineArgExpected: "argument for command line option expected: '$1'",
errCmdLineNoArgExpected: "invalid argument for command line option: '$1'",
errInvalidVarSubstitution: "invalid variable substitution in '$1'",
errUnknownVar: "unknown variable: '$1'",
errUnknownCcompiler: "unknown C compiler: '$1'",
errOnOrOffExpectedButXFound: "'on' or 'off' expected, but '$1' found",
errOnOffOrListExpectedButXFound: "'on', 'off' or 'list' expected, but '$1' found",
errGenOutExpectedButXFound: "'c', 'c++' or 'yaml' expected, but '$1' found",
,
errInvalidMultipleAsgn: "multiple assignment is not allowed",
errColonOrEqualsExpected: "':' or '=' expected, but found '$1'",
errUndeclaredField: "undeclared field: '$1'",
errUndeclaredRoutine: "attempting to call undeclared routine: '$1'",
errUseQualifier: "ambiguous identifier: '$1' -- use a qualifier",
errTypeExpected: "type expected",
errSystemNeeds: "system module needs '$1'",
errExecutionOfProgramFailed: "execution of an external program failed: '$1'",
errNotOverloadable: ,
errInvalidArgForX: "invalid argument for '$1'",
errStmtHasNoEffect: "statement has no effect",
,
errXExpectsArrayType: "'$1' expects an array type",
errIteratorCannotBeInstantiated: "'$1' cannot be instantiated because its body has not been compiled yet",
errExprXAmbiguous: "expression '$1' ambiguous in this context",
errConstantDivisionByZero: ,
errOrdinalOrFloatTypeExpected: "ordinal or float type expected",
errOverOrUnderflow: ,
errCannotEvalXBecauseIncompletelyDefined: ,
errChrExpectsRange0_255: "'chr' expects an int in the range 0..255",
errDynlibRequiresExportc: "'dynlib' requires 'exportc'",
errNilAccess: "attempt to access a nil address",
errIndexOutOfBounds: "index out of bounds",
errIndexTypesDoNotMatch: "index types do not match",
errBracketsInvalidForType: "'[]' operator invalid for this type",
errValueOutOfSetBounds: "value out of set bounds",
errFieldNotInit: "field '$1' not initialized",
errExprXCannotBeCalled: "expression '$1' cannot be called",
errExprHasNoType: "expression has no type",
errExprXHasNoType:,
errCastNotInSafeMode: "'cast' not allowed in safe mode",
errExprCannotBeCastToX: ,
errCommaOrParRiExpected: "',' or ')' expected",
errCurlyLeOrParLeExpected: "'{' or '(' expected",
errSectionExpected: "section ('type', 'proc', etc.) expected",
errRangeExpected: "range expected",
errMagicOnlyInSystem: "'magic' only allowed in system module",
errPowerOfTwoExpected: "power of two expected",
errStringMayNotBeEmpty: "string literal may not be empty",
errCallConvExpected: "calling convention expected",
errProcOnlyOneCallConv: "a proc can only have one calling convention",
errSymbolMustBeImported: "symbol must be imported if 'lib' pragma is used",
errExprMustBeBool: "expression must be of type 'bool'",
errConstExprExpected: "constant expression expected",
errDuplicateCaseLabel: "duplicate case label",
errRangeIsEmpty: "range is empty",
,
errSelectorMustBeOrdinal: "selector must be of an ordinal type",
errOrdXMustNotBeNegative: "ord($1) must not be negative",
errLenXinvalid: "len($1) must be less than 32768",
errTypeXhasUnknownSize: "type '$1' has unknown size",
errConstNeedsConstExpr: "a constant can only be initialized with a constant expression",
errConstNeedsValue: "a constant needs a value",
errResultCannotBeOpenArray: "the result type cannot be on open array",
errSizeTooBig: "computing the type's size produced an overflow",
errInheritanceOnlyWithEnums: "inheritance only works with an enum",
errIllegalRecursionInTypeX:,
errCannotInstantiateX: "cannot instantiate: '$1'",
errTypeMismatch: "type mismatch: got <",
errButExpected: "but expected one of: ",
errButExpectedX: "but expected '$1'",
errAmbiguousCallXYZ: "ambiguous call; both $1 and $2 match for: $3",
errWrongNumberOfArguments: "wrong number of arguments",
errWrongNumberOfArgumentsInCall: "wrong number of arguments in call to '$1'",
errMissingGenericParamsForTemplate: "'$1' has unspecified generic parameters",
errXCannotBePassedToProcVar: ,
,
errImplOfXexpected: ,
errIllegalConvFromXtoY: ,
errCannotBindXTwice: "cannot bind parameter '$1' twice",
errInvalidOrderInArrayConstructor: ,
errInvalidOrderInEnumX: "invalid order in enum '$1'",
errEnumXHasHoles: "enum '$1' has holes",
errExceptExpected: "'except' or 'finally' expected",
errInvalidTry: "after catch all 'except' or 'finally' no section may follow",
errOptionExpected: ,
errXisNoLabel: "'$1' is not a label",
errNotAllCasesCovered: "not all cases are covered",
errUnknownSubstitionVar: "unknown substitution variable: '$1'",
errComplexStmtRequiresInd: "complex statement requires indentation",
errXisNotCallable: "'$1' is not callable",
errNoPragmasAllowedForX: "no pragmas allowed for $1",
,
errInvalidParamKindX: "invalid param kind: '$1'",
errDefaultArgumentInvalid: "default argument invalid",
errNamedParamHasToBeIdent: "named parameter has to be an identifier",
errNoReturnTypeForX: "no return type allowed for $1",
errConvNeedsOneArg: "a type conversion needs exactly one argument",
errInvalidPragmaX: ,
errXNotAllowedHere: "$1 not allowed here",
errXisNoType: "invalid type: '$1'",
errCircumNeedsPointer: "'[]' needs a pointer or reference type",
errInvalidExpression: "invalid expression",
errInvalidExpressionX: "invalid expression: '$1'",
errEnumHasNoValueX: "enum has no value '$1'",
,
errNoCommand: "no command given",
errInvalidCommandX: "invalid command: '$1'",
errXNeedsParamObjectType: ,
errTemplateInstantiationTooNested: "template instantiation too nested, try --evalTemplateLimit:N",
errMacroInstantiationTooNested: "macro instantiation too nested, try --evalMacroLimit:N",
errInstantiationFrom: "template/generic instantiation from here",
errInvalidIndexValueForTuple: "invalid index value for tuple subscript",
errCommandExpectsFilename: "command expects a filename argument",
errMainModuleMustBeSpecified: "please, specify a main module in the project configuration file",
errXExpected: "'$1' expected",
,
errCastToANonConcreteType: "cannot cast to a non concrete type: '$1'",
errInvalidSectionStart: "invalid section start",
errGridTableNotImplemented: "grid table is not implemented",
errGeneralParseError: "general parse error",
errNewSectionExpected: "new section expected",
errWhitespaceExpected: "whitespace expected, got '$1'",
errXisNoValidIndexFile: "'$1' is no valid index file",
errCannotRenderX: "cannot render reStructuredText element '$1'",
errVarVarTypeNotAllowed: ,
errInstantiateXExplicitly: "instantiate '$1' explicitly",
errOnlyACallOpCanBeDelegator: ,
errUsingNoSymbol: "'$1' is not a variable, constant or a proc name",
errMacroBodyDependsOnGenericTypes: "the macro body cannot be compiled, " &
"because the parameter '$1' has a generic type",
errDestructorNotGenericEnough: "Destructor signature is too specific. " &
"A destructor must be associated will all instantiations of a generic type",
errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
"templates, macros and other inline iterators",
errXExpectsTwoArguments: "'$1' expects two arguments",
errXExpectsObjectTypes: "'$1' expects object types",
errXcanNeverBeOfThisSubtype: "'$1' can never be of this subtype",
errTooManyIterations: "interpretation requires too many iterations; " &
"if you are sure this is not a bug in your code edit " &
"compiler/vmdef.MaxLoopIterations and rebuild the compiler",
errFieldXNotFound: "field '$1' cannot be found",
errInvalidConversionFromTypeX: "invalid conversion from type '$1'",
errAssertionFailed: "assertion failed",
errCannotGenerateCodeForX: "cannot generate code for '$1'",
errXRequiresOneArgument: "$1 requires one parameter",
errUnhandledExceptionX: "unhandled exception: $1",
errCyclicTree: "macro returned a cyclic abstract syntax tree",
errXisNoMacroOrTemplate: "'$1' is no macro or template",
errXhasSideEffects: "'$1' can have side effects",
errWrongSymbolX:,
errIllegalCaptureX: "illegal capture '$1'",
errXCannotBeClosure: "'$1' cannot have 'closure' calling convention",
,
]#

View file

@ -19,6 +19,7 @@ proc generateDot*(project: string)
type
TGen = object of TPassContext
module*: PSym
config: ConfigRef
PGen = ref TGen
var gDotGraph: Rope # the generated DOT file; we need a global variable
@ -33,10 +34,10 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode =
case n.kind
of nkImportStmt:
for i in countup(0, sonsLen(n) - 1):
var imported = getModuleName(n.sons[i])
var imported = getModuleName(g.config, n.sons[i])
addDependencyAux(g.module.name.s, imported)
of nkFromStmt, nkImportExceptStmt:
var imported = getModuleName(n.sons[0])
var imported = getModuleName(g.config, n.sons[0])
addDependencyAux(g.module.name.s, imported)
of nkStmtList, nkBlockStmt, nkStmtListExpr, nkBlockExpr:
for i in countup(0, sonsLen(n) - 1): discard addDotDependency(c, n.sons[i])
@ -52,6 +53,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
var g: PGen
new(g)
g.module = module
g.config = graph.config
result = g
const gendependPass* = makePass(open = myOpen, process = addDotDependency)

View file

@ -89,11 +89,35 @@
## tmp.bar))
## destroy(tmp.bar)
## destroy(tmp.x); destroy(tmp.y)
##
##[
From https://github.com/nim-lang/Nim/wiki/Destructors
Rule Pattern Transformed into
---- ------- ----------------
1.1 var x: T; stmts var x: T; try stmts
finally: `=destroy`(x)
1.2 var x: sink T; stmts var x: sink T; stmts; ensureEmpty(x)
2 x = f() `=sink`(x, f())
3 x = lastReadOf z `=sink`(x, z)
4.1 y = sinkParam `=sink`(y, sinkParam)
4.2 x = y `=`(x, y) # a copy
5.1 f_sink(g()) f_sink(g())
5.2 f_sink(y) f_sink(copy y); # copy unless we can see it's the last read
5.3 f_sink(move y) f_sink(y); reset(y) # explicit moves empties 'y'
5.4 f_noSink(g()) var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp)
Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently
not allowed as a local variable.
``move`` builtin needs to be implemented.
]##
import
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
strutils, options, dfa, lowerings, rodread
strutils, options, dfa, lowerings, rodread, tables, modulegraphs,
configuration
const
InterestingSyms = {skVar, skResult, skLet}
@ -106,6 +130,15 @@ type
tmpObj: PType
tmp: PSym
destroys, topLevelVars: PNode
toDropBit: Table[int, PSym]
graph: ModuleGraph
proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode =
# XXX why are temps fields in an object here?
let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, info)
f.typ = typ
rawAddField c.tmpObj, f
result = rawDirectAccess(c.tmp, f)
proc isHarmlessVar*(s: PSym; c: Con): bool =
# 's' is harmless if it used only once and its
@ -174,7 +207,7 @@ proc patchHead(n: PNode) =
if n[1].typ.isNil:
# XXX toptree crashes without this workaround. Figure out why.
return
let t = n[1].typ.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
let t = n[1].typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred})
template patch(op, field) =
if s.name.s == op and field != nil and field != s:
n.sons[0].sym = field
@ -191,47 +224,118 @@ proc patchHead(s: PSym) =
template genOp(opr, opname) =
let op = opr
if op == nil:
globalError(dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t))
globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator not found for type " & typeToString(t))
elif op.ast[genericParamsPos].kind != nkEmpty:
globalError(dest.info, "internal error: '" & opname & "' operator is generic")
globalError(c.graph.config, dest.info, "internal error: '" & opname & "' operator is generic")
patchHead op
result = newTree(nkCall, newSymNode(op), newTree(nkHiddenAddr, dest))
proc genSink(t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias})
proc genSink(c: Con; t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
genOp(if t.sink != nil: t.sink else: t.assignment, "=sink")
proc genCopy(t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias})
proc genCopy(c: Con; t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
genOp(t.assignment, "=")
proc genDestroy(t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias})
proc genDestroy(c: Con; t: PType; dest: PNode): PNode =
let t = t.skipTypes({tyGenericInst, tyAlias, tySink})
genOp(t.destructor, "=destroy")
proc addTopVar(c: var Con; v: PNode) =
c.topLevelVars.add newTree(nkIdentDefs, v, emptyNode, emptyNode)
proc dropBit(c: var Con; s: PSym): PSym =
result = c.toDropBit.getOrDefault(s.id)
assert result != nil
proc registerDropBit(c: var Con; s: PSym) =
let result = newSym(skTemp, getIdent(s.name.s & "_AliveBit"), c.owner, s.info)
result.typ = getSysType(c.graph, s.info, tyBool)
let trueVal = newIntTypeNode(nkIntLit, 1, result.typ)
c.topLevelVars.add newTree(nkIdentDefs, newSymNode result, emptyNode, trueVal)
c.toDropBit[s.id] = result
# generate:
# if not sinkParam_AliveBit: `=destroy`(sinkParam)
c.destroys.add newTree(nkIfStmt,
newTree(nkElifBranch, newSymNode result, genDestroy(c, s.typ, newSymNode s)))
proc p(n: PNode; c: var Con): PNode
template recurse(n, dest) =
for i in 0..<n.len:
dest.add p(n[i], c)
proc isSinkParam(s: PSym): bool {.inline.} =
result = s.kind == skParam and s.typ.kind == tySink
const constrExprs = nkCallKinds+{nkObjConstr}
proc destructiveMoveSink(n: PNode; c: var Con): PNode =
# generate: (chckMove(sinkParam_AliveBit); sinkParam_AliveBit = false; sinkParam)
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let bit = newSymNode dropBit(c, n.sym)
if optMoveCheck in c.owner.options:
result.add callCodegenProc(c.graph, "chckMove", bit)
result.add newTree(nkAsgn, bit,
newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool)))
result.add n
proc moveOrCopy(dest, ri: PNode; c: var Con): PNode =
if ri.kind in nkCallKinds+{nkObjConstr}:
result = genSink(ri.typ, dest)
if ri.kind in constrExprs:
result = genSink(c, ri.typ, dest)
# watch out and no not transform 'ri' twice if it's a call:
let ri2 = copyNode(ri)
recurse(ri, ri2)
result.add ri2
elif ri.kind == nkSym and isHarmlessVar(ri.sym, c):
result = genSink(ri.typ, dest)
result = genSink(c, ri.typ, dest)
result.add p(ri, c)
elif ri.kind == nkSym and isSinkParam(ri.sym):
result = genSink(c, ri.typ, dest)
result.add destructiveMoveSink(ri, c)
else:
result = genCopy(ri.typ, dest)
result = genCopy(c, ri.typ, dest)
result.add p(ri, c)
proc passCopyToSink(n: PNode; c: var Con): PNode =
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let tmp = getTemp(c, n.typ, n.info)
if hasDestructor(n.typ):
var m = genCopy(c, n.typ, tmp)
m.add p(n, c)
result.add m
message(c.graph.config, n.info, hintPerformance,
"passing '$1' to a sink parameter introduces an implicit copy; " &
"use 'move($1)' to prevent it" % $n)
else:
result.add newTree(nkAsgn, tmp, p(n, c))
result.add tmp
proc genReset(n: PNode; c: var Con): PNode =
result = newNodeI(nkCall, n.info)
result.add(newSymNode(createMagic(c.graph, "reset", mReset)))
# The mReset builtin does not take the address:
result.add n
proc destructiveMoveVar(n: PNode; c: var Con): PNode =
# generate: (let tmp = v; reset(v); tmp)
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
var temp = newSym(skLet, getIdent("blitTmp"), c.owner, n.info)
var v = newNodeI(nkLetSection, n.info)
let tempAsNode = newSymNode(temp)
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
vpart.sons[0] = tempAsNode
vpart.sons[1] = ast.emptyNode
vpart.sons[2] = n
add(v, vpart)
result.add v
result.add genReset(n, c)
result.add tempAsNode
proc p(n: PNode; c: var Con): PNode =
case n.kind
of nkVarSection, nkLetSection:
@ -243,7 +347,7 @@ proc p(n: PNode; c: var Con): PNode =
let L = it.len-1
let ri = it[L]
if it.kind == nkVarTuple and hasDestructor(ri.typ):
let x = lowerTupleUnpacking(it, c.owner)
let x = lowerTupleUnpacking(c.graph, it, c.owner)
result.add p(x, c)
elif it.kind == nkIdentDefs and hasDestructor(it[0].typ):
for j in 0..L-2:
@ -252,7 +356,7 @@ proc p(n: PNode; c: var Con): PNode =
# move the variable declaration to the top of the frame:
c.addTopVar v
# make sure it's destroyed at the end of the proc:
c.destroys.add genDestroy(v.typ, v)
c.destroys.add genDestroy(c, v.typ, v)
if ri.kind != nkEmpty:
let r = moveOrCopy(v, ri, c)
result.add r
@ -266,22 +370,45 @@ proc p(n: PNode; c: var Con): PNode =
varSection.add itCopy
result.add varSection
of nkCallKinds:
let parameters = n[0].typ
let L = if parameters != nil: parameters.len else: 0
for i in 1 ..< n.len:
let arg = n[i]
if i < L and parameters[i].kind == tySink:
if arg.kind in nkCallKinds:
# recurse but skip the call expression in order to prevent
# destructor injections: Rule 5.1 is different from rule 5.4!
let a = copyNode(arg)
recurse(arg, a)
n.sons[i] = a
elif arg.kind in {nkObjConstr, nkCharLit..nkFloat128Lit}:
discard "object construction to sink parameter: nothing to do"
elif arg.kind == nkSym and isHarmlessVar(arg.sym, c):
# if x is a variable and it its last read we eliminate its
# destructor invokation, but don't. We need to reset its memory
# to disable its destructor which we have not elided:
n.sons[i] = destructiveMoveVar(arg, c)
elif arg.kind == nkSym and isSinkParam(arg.sym):
# mark the sink parameter as used:
n.sons[i] = destructiveMoveSink(arg, c)
else:
# an object that is not temporary but passed to a 'sink' parameter
# results in a copy.
n.sons[i] = passCopyToSink(arg, c)
else:
n.sons[i] = p(arg, c)
if n.typ != nil and hasDestructor(n.typ):
discard "produce temp creation"
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let f = newSym(skField, getIdent(":d" & $c.tmpObj.n.len), c.owner, n.info)
f.typ = n.typ
rawAddField c.tmpObj, f
var m = genSink(n.typ, rawDirectAccess(c.tmp, f))
var call = copyNode(n)
recurse(n, call)
m.add call
result.add m
result.add rawDirectAccess(c.tmp, f)
c.destroys.add genDestroy(n.typ, rawDirectAccess(c.tmp, f))
let tmp = getTemp(c, n.typ, n.info)
var sinkExpr = genSink(c, n.typ, tmp)
sinkExpr.add n
result.add sinkExpr
result.add tmp
c.destroys.add genDestroy(c, n.typ, tmp)
else:
result = copyNode(n)
recurse(n, result)
result = n
of nkAsgn, nkFastAsgn:
if hasDestructor(n[0].typ):
result = moveOrCopy(n[0], n[1], c)
@ -295,22 +422,29 @@ proc p(n: PNode; c: var Con): PNode =
result = copyNode(n)
recurse(n, result)
proc injectDestructorCalls*(owner: PSym; n: PNode): PNode =
proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode =
when defined(nimDebugDestroys):
echo "injecting into ", n
var c: Con
c.owner = owner
c.tmp = newSym(skTemp, getIdent":d", owner, n.info)
c.tmpObj = createObj(owner, n.info)
c.tmpObj = createObj(g, owner, n.info)
c.tmp.typ = c.tmpObj
c.destroys = newNodeI(nkStmtList, n.info)
c.topLevelVars = newNodeI(nkVarSection, n.info)
c.toDropBit = initTable[int, PSym]()
c.graph = g
let cfg = constructCfg(owner, n)
shallowCopy(c.g, cfg)
c.jumpTargets = initIntSet()
for i in 0..<c.g.len:
if c.g[i].kind in {goto, fork}:
c.jumpTargets.incl(i+c.g[i].dest)
if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
let params = owner.typ.n
for i in 1 ..< params.len:
let param = params[i].sym
if param.typ.kind == tySink: registerDropBit(c, param)
let body = p(n, c)
if c.tmp.typ.n.len > 0:
c.addTopVar(newSymNode c.tmp)

View file

@ -23,7 +23,7 @@
## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
import ast, astalgo, types, intsets, tables, msgs
import ast, astalgo, types, intsets, tables, msgs, options
type
InstrKind* = enum
@ -102,14 +102,14 @@ proc genLabel(c: Con): TPosition =
proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
let dist = p.int - c.code.len
internalAssert(-0x7fff < dist and dist < 0x7fff)
doAssert(-0x7fff < dist and dist < 0x7fff)
c.code.add Instr(n: n, kind: goto, dest: dist)
proc patch(c: var Con, p: TPosition) =
# patch with current index
let p = p.int
let diff = c.code.len - p
internalAssert(-0x7fff < diff and diff < 0x7fff)
doAssert(-0x7fff < diff and diff < 0x7fff)
c.code[p].dest = diff
proc popBlock(c: var Con; oldLen: int) =
@ -160,7 +160,7 @@ proc genBreak(c: var Con; n: PNode) =
if c.blocks[i].label == n.sons[0].sym:
c.blocks[i].fixups.add L1
return
globalError(n.info, errGenerated, "VM problem: cannot find 'break' target")
#globalError(n.info, "VM problem: cannot find 'break' target")
else:
c.blocks[c.blocks.high].fixups.add L1
@ -323,7 +323,7 @@ proc gen(c: var Con; n: PNode) =
of nkBreakStmt: genBreak(c, n)
of nkTryStmt: genTry(c, n)
of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
nkBracket, nkCurly, nkPar, nkClosure, nkObjConstr:
nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr:
for x in n: gen(c, x)
of nkPragmaBlock: gen(c, n.lastSon)
of nkDiscardStmt: gen(c, n.sons[0])
@ -334,7 +334,7 @@ proc gen(c: var Con; n: PNode) =
of nkVarSection, nkLetSection: genVarSection(c, n)
else: discard
proc dfa(code: seq[Instr]) =
proc dfa(code: seq[Instr]; conf: ConfigRef) =
var u = newSeq[IntSet](code.len) # usages
var d = newSeq[IntSet](code.len) # defs
var c = newSeq[IntSet](code.len) # consumed
@ -426,17 +426,17 @@ proc dfa(code: seq[Instr]) =
of use, useWithinCall:
let s = code[i].sym
if s.id notin d[i]:
localError(code[i].n.info, "usage of uninitialized variable: " & s.name.s)
localError(conf, code[i].n.info, "usage of uninitialized variable: " & s.name.s)
if s.id in c[i]:
localError(code[i].n.info, "usage of an already consumed variable: " & s.name.s)
localError(conf, code[i].n.info, "usage of an already consumed variable: " & s.name.s)
else: discard
proc dataflowAnalysis*(s: PSym; body: PNode) =
proc dataflowAnalysis*(s: PSym; body: PNode; conf: ConfigRef) =
var c = Con(code: @[], blocks: @[])
gen(c, body)
when defined(useDfa) and defined(debugDfa): echoCfg(c.code)
dfa(c.code)
dfa(c.code, conf)
proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
## constructs a control flow graph for ``body``.

View file

@ -16,7 +16,7 @@ import
wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
packages/docutils/rst, packages/docutils/rstgen, times,
packages/docutils/highlite, sempass2, json, xmltree, cgi,
typesrenderer, astalgo, modulepaths
typesrenderer, astalgo, modulepaths, configuration
type
TSections = array[TSymKind, Rope]
@ -29,6 +29,7 @@ type
jArray: JsonNode
types: TStrTable
isPureRst: bool
conf*: ConfigRef
PDoc* = ref TDocumentor ## Alias to type less.
@ -53,42 +54,47 @@ proc attachToType(d: PDoc; p: PSym): PSym =
if params.len > 0: check(0)
for i in 2..<params.len: check(i)
proc compilerMsgHandler(filename: string, line, col: int,
msgKind: rst.MsgKind, arg: string) {.procvar.} =
# translate msg kind:
var k: msgs.TMsgKind
case msgKind
of meCannotOpenFile: k = errCannotOpenFile
of meExpected: k = errXExpected
of meGridTableNotImplemented: k = errGridTableNotImplemented
of meNewSectionExpected: k = errNewSectionExpected
of meGeneralParseError: k = errGeneralParseError
of meInvalidDirective: k = errInvalidDirectiveX
of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel
of mwUnknownSubstitution: k = warnUnknownSubstitutionX
of mwUnsupportedLanguage: k = warnLanguageXNotSupported
of mwUnsupportedField: k = warnFieldXNotSupported
globalError(newLineInfo(filename, line, col), k, arg)
template declareClosures =
proc compilerMsgHandler(filename: string, line, col: int,
msgKind: rst.MsgKind, arg: string) {.procvar.} =
# translate msg kind:
var k: TMsgKind
case msgKind
of meCannotOpenFile: k = errCannotOpenFile
of meExpected: k = errXExpected
of meGridTableNotImplemented: k = errGridTableNotImplemented
of meNewSectionExpected: k = errNewSectionExpected
of meGeneralParseError: k = errGeneralParseError
of meInvalidDirective: k = errInvalidDirectiveX
of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel
of mwUnknownSubstitution: k = warnUnknownSubstitutionX
of mwUnsupportedLanguage: k = warnLanguageXNotSupported
of mwUnsupportedField: k = warnFieldXNotSupported
globalError(conf, newLineInfo(conf, filename, line, col), k, arg)
proc docgenFindFile(s: string): string {.procvar.} =
result = options.findFile(s)
if result.len == 0:
result = getCurrentDir() / s
if not existsFile(result): result = ""
proc docgenFindFile(s: string): string {.procvar.} =
result = options.findFile(conf, s)
if result.len == 0:
result = getCurrentDir() / s
if not existsFile(result): result = ""
proc parseRst(text, filename: string,
line, column: int, hasToc: var bool,
rstOptions: RstParseOptions): PRstNode =
rstOptions: RstParseOptions;
conf: ConfigRef): PRstNode =
declareClosures()
result = rstParse(text, filename, line, column, hasToc, rstOptions,
docgenFindFile, compilerMsgHandler)
proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
proc newDocumentor*(filename: string, conf: ConfigRef): PDoc =
declareClosures()
new(result)
initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
options.gConfigVars, filename, {roSupportRawDirective},
result.conf = conf
initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex),
conf.configVars, filename, {roSupportRawDirective},
docgenFindFile, compilerMsgHandler)
if config.hasKey("doc.googleAnalytics"):
if conf.configVars.hasKey("doc.googleAnalytics"):
result.analytics = """
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
@ -100,7 +106,7 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
ga('send', 'pageview');
</script>
""" % [config.getOrDefault"doc.googleAnalytics"]
""" % [conf.configVars.getOrDefault"doc.googleAnalytics"]
else:
result.analytics = ""
@ -109,10 +115,10 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
result.jArray = newJArray()
initStrTable result.types
result.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) =
localError(newLineInfo(d.filename, -1, -1), warnUser, "only 'rst2html' supports the ':test:' attribute")
localError(conf, newLineInfo(conf, d.filename, -1, -1), warnUser, "only 'rst2html' supports the ':test:' attribute")
proc dispA(dest: var Rope, xml, tex: string, args: openArray[Rope]) =
if gCmd != cmdRst2tex: addf(dest, xml, args)
proc dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) =
if conf.cmd != cmdRst2tex: addf(dest, xml, args)
else: addf(dest, tex, args)
proc getVarIdx(varnames: openArray[string], id: string): int =
@ -121,7 +127,8 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
return i
result = -1
proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
proc ropeFormatNamedVars(conf: ConfigRef; frmt: FormatStr,
varnames: openArray[string],
varvalues: openArray[Rope]): Rope =
var i = 0
var L = len(frmt)
@ -144,7 +151,8 @@ proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
j = (j * 10) + ord(frmt[i]) - ord('0')
inc(i)
if (i > L + 0 - 1) or not (frmt[i] in {'0'..'9'}): break
if j > high(varvalues) + 1: internalError("ropeFormatNamedVars")
if j > high(varvalues) + 1:
rawMessage(conf, errGenerated, "Invalid format string; too many $s: " & frmt)
num = j
add(result, varvalues[j - 1])
of 'A'..'Z', 'a'..'z', '\x80'..'\xFF':
@ -155,20 +163,23 @@ proc ropeFormatNamedVars(frmt: FormatStr, varnames: openArray[string],
if not (frmt[i] in {'A'..'Z', '_', 'a'..'z', '\x80'..'\xFF'}): break
var idx = getVarIdx(varnames, id)
if idx >= 0: add(result, varvalues[idx])
else: rawMessage(errUnknownSubstitionVar, id)
else: rawMessage(conf, errGenerated, "unknown substition variable: " & id)
of '{':
var id = ""
inc(i)
while frmt[i] != '}':
if frmt[i] == '\0': rawMessage(errTokenExpected, "}")
while i < frmt.len and frmt[i] != '}':
add(id, frmt[i])
inc(i)
inc(i) # skip }
# search for the variable:
var idx = getVarIdx(varnames, id)
if i >= frmt.len:
rawMessage(conf, errGenerated, "expected closing '}'")
else:
inc(i) # skip }
# search for the variable:
let idx = getVarIdx(varnames, id)
if idx >= 0: add(result, varvalues[idx])
else: rawMessage(errUnknownSubstitionVar, id)
else: internalError("ropeFormatNamedVars")
else: rawMessage(conf, errGenerated, "unknown substition variable: " & id)
else:
add(result, "$")
var start = i
while i < L:
if frmt[i] != '$': inc(i)
@ -181,7 +192,7 @@ proc genComment(d: PDoc, n: PNode): string =
if n.comment != nil:
renderRstToOut(d[], parseRst(n.comment, toFilename(n.info),
toLinenumber(n.info), toColumn(n.info),
dummyHasToc, d.options), result)
dummyHasToc, d.options, d.conf), result)
proc genRecComment(d: PDoc, n: PNode): Rope =
if n == nil: return nil
@ -220,45 +231,46 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
of tkEof:
break
of tkComment:
dispA(result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
dispA(d.conf, result, "<span class=\"Comment\">$1</span>", "\\spanComment{$1}",
[rope(esc(d.target, literal))])
of tokKeywordLow..tokKeywordHigh:
dispA(result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
dispA(d.conf, result, "<span class=\"Keyword\">$1</span>", "\\spanKeyword{$1}",
[rope(literal)])
of tkOpr:
dispA(result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
dispA(d.conf, result, "<span class=\"Operator\">$1</span>", "\\spanOperator{$1}",
[rope(esc(d.target, literal))])
of tkStrLit..tkTripleStrLit:
dispA(result, "<span class=\"StringLit\">$1</span>",
dispA(d.conf, result, "<span class=\"StringLit\">$1</span>",
"\\spanStringLit{$1}", [rope(esc(d.target, literal))])
of tkCharLit:
dispA(result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
dispA(d.conf, result, "<span class=\"CharLit\">$1</span>", "\\spanCharLit{$1}",
[rope(esc(d.target, literal))])
of tkIntLit..tkUInt64Lit:
dispA(result, "<span class=\"DecNumber\">$1</span>",
dispA(d.conf, result, "<span class=\"DecNumber\">$1</span>",
"\\spanDecNumber{$1}", [rope(esc(d.target, literal))])
of tkFloatLit..tkFloat128Lit:
dispA(result, "<span class=\"FloatNumber\">$1</span>",
dispA(d.conf, result, "<span class=\"FloatNumber\">$1</span>",
"\\spanFloatNumber{$1}", [rope(esc(d.target, literal))])
of tkSymbol:
dispA(result, "<span class=\"Identifier\">$1</span>",
dispA(d.conf, result, "<span class=\"Identifier\">$1</span>",
"\\spanIdentifier{$1}", [rope(esc(d.target, literal))])
of tkSpaces, tkInvalid:
add(result, literal)
of tkCurlyDotLe:
dispA(result, """<span class="Other pragmabegin">$1</span><div class="pragma">""",
dispA(d.conf, result, """<span class="Other pragmabegin">$1</span><div class="pragma">""",
"\\spanOther{$1}",
[rope(esc(d.target, literal))])
of tkCurlyDotRi:
dispA(result, "</div><span class=\"Other pragmaend\">$1</span>",
dispA(d.conf, result, "</div><span class=\"Other pragmaend\">$1</span>",
"\\spanOther{$1}",
[rope(esc(d.target, literal))])
of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
tkBracketDotLe, tkBracketDotRi, tkParDotLe,
tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
tkAccent, tkColonColon,
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr:
dispA(result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr,
tkBracketLeColon:
dispA(d.conf, result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
[rope(esc(d.target, literal))])
proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
@ -266,7 +278,7 @@ proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
of nkCallKinds:
if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and
n.len >= 2 and n.lastSon.kind == nkStmtList:
dispA(dest, "\n<strong class=\"examples_text\">$1</strong>\n",
dispA(d.conf, dest, "\n<strong class=\"examples_text\">$1</strong>\n",
"\n\\textbf{$1}\n", [rope"Examples:"])
inc d.listingCounter
let id = $d.listingCounter
@ -335,7 +347,6 @@ proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
of nkOpenSymChoice, nkClosedSymChoice:
result = getName(d, n[0], splitAfter)
else:
internalError(n.info, "getName()")
result = ""
proc getNameIdent(n: PNode): PIdent =
@ -365,7 +376,6 @@ proc getRstName(n: PNode): PRstNode =
of nkOpenSymChoice, nkClosedSymChoice:
result = getRstName(n[0])
else:
internalError(n.info, "getRstName()")
result = nil
proc newUniquePlainSymbol(d: PDoc, original: string): string =
@ -489,22 +499,22 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
symbolOrIdEncRope = encodeUrl(symbolOrId).rope
var seeSrcRope: Rope = nil
let docItemSeeSrc = getConfigVar("doc.item.seesrc")
let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
if docItemSeeSrc.len > 0:
let cwd = getCurrentDir().canonicalizePath()
let cwd = canonicalizePath(d.conf, getCurrentDir())
var path = n.info.toFullPath
if path.startsWith(cwd):
path = path[cwd.len+1 .. ^1].replace('\\', '/')
let gitUrl = getConfigVar("git.url")
let gitUrl = getConfigVar(d.conf, "git.url")
if gitUrl.len > 0:
var commit = getConfigVar("git.commit")
var commit = getConfigVar(d.conf, "git.commit")
if commit.len == 0: commit = "master"
dispA(seeSrcRope, "$1", "", [ropeFormatNamedVars(docItemSeeSrc,
dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc,
["path", "line", "url", "commit"], [rope path,
rope($n.info.line), rope gitUrl,
rope commit])])
add(d.section[k], ropeFormatNamedVars(getConfigVar("doc.item"),
add(d.section[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"),
["name", "header", "desc", "itemID", "header_plain", "itemSym",
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc"],
[nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope,
@ -515,7 +525,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
let att = attachToType(d, nameNode.sym)
if att != nil:
attype = rope esc(d.target, att.name.s)
add(d.toc[k], ropeFormatNamedVars(getConfigVar("doc.item.toc"),
add(d.toc[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item.toc"),
["name", "header", "desc", "itemID", "header_plain", "itemSym",
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "attype"],
[rope(getName(d, nameNode, d.splitAfter)), result, comm,
@ -543,7 +553,7 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode =
initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
result = %{ "name": %name, "type": %($k), "line": %n.info.line,
result = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
"col": %n.info.col}
if comm != nil and comm != "":
result["description"] = %comm
@ -568,9 +578,9 @@ proc traceDeps(d: PDoc, it: PNode) =
traceDeps(d, a)
else:
if d.section[k] != nil: add(d.section[k], ", ")
dispA(d.section[k],
dispA(d.conf, d.section[k],
"<a class=\"reference external\" href=\"$1.html\">$1</a>",
"$1", [rope(getModuleName(it))])
"$1", [rope(getModuleName(d.conf, it))])
proc generateDoc*(d: PDoc, n: PNode) =
case n.kind
@ -617,7 +627,7 @@ proc generateJson*(d: PDoc, n: PNode) =
of nkCommentStmt:
if n.comment != nil and startsWith(n.comment, "##"):
let stripped = n.comment.substr(2).strip
d.add %{ "comment": %stripped, "line": %n.info.line,
d.add %{ "comment": %stripped, "line": %n.info.line.int,
"col": %n.info.col }
of nkProcDef:
when useEffectSystem: documentRaises(n)
@ -703,10 +713,10 @@ proc genSection(d: PDoc, kind: TSymKind) =
]
if d.section[kind] == nil: return
var title = sectionNames[kind].rope
d.section[kind] = ropeFormatNamedVars(getConfigVar("doc.section"), [
d.section[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [
ord(kind).rope, title, rope(ord(kind) + 50), d.section[kind]])
d.toc[kind] = ropeFormatNamedVars(getConfigVar("doc.section.toc"), [
d.toc[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section.toc"), [
"sectionid", "sectionTitle", "sectionTitleID", "content"], [
ord(kind).rope, title, rope(ord(kind) + 50), d.toc[kind]])
@ -722,7 +732,7 @@ proc genOutFile(d: PDoc): Rope =
genSection(d, i)
add(toc, d.toc[i])
if toc != nil:
toc = ropeFormatNamedVars(getConfigVar("doc.toc"), ["content"], [toc])
toc = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.toc"), ["content"], [toc])
for i in countup(low(TSymKind), high(TSymKind)): add(code, d.section[i])
# Extract the title. Non API modules generate an entry in the index table.
@ -736,13 +746,13 @@ proc genOutFile(d: PDoc): Rope =
let bodyname = if d.hasToc and not d.isPureRst: "doc.body_toc_group"
elif d.hasToc: "doc.body_toc"
else: "doc.body_no_toc"
content = ropeFormatNamedVars(getConfigVar(bodyname), ["title",
content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title",
"tableofcontents", "moduledesc", "date", "time", "content"],
[title.rope, toc, d.modDesc, rope(getDateStr()),
rope(getClockStr()), code])
if optCompileOnly notin gGlobalOptions:
if optCompileOnly notin d.conf.globalOptions:
# XXX what is this hack doing here? 'optCompileOnly' means raw output!?
code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
code = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time",
"content", "author", "version", "analytics"],
[title.rope, toc, d.modDesc, rope(getDateStr()),
@ -753,60 +763,60 @@ proc genOutFile(d: PDoc): Rope =
result = code
proc generateIndex*(d: PDoc) =
if optGenIndex in gGlobalOptions:
writeIndexFile(d[], splitFile(options.outFile).dir /
if optGenIndex in d.conf.globalOptions:
writeIndexFile(d[], splitFile(d.conf.outFile).dir /
splitFile(d.filename).name & IndexExt)
proc getOutFile2(filename, ext, dir: string): string =
if gWholeProject:
let d = if options.outFile != "": options.outFile else: dir
proc getOutFile2(conf: ConfigRef; filename, ext, dir: string): string =
if optWholeProject in conf.globalOptions:
let d = if conf.outFile != "": conf.outFile else: dir
createDir(d)
result = d / changeFileExt(filename, ext)
else:
result = getOutFile(filename, ext)
result = getOutFile(conf, filename, ext)
proc writeOutput*(d: PDoc, filename, outExt: string, useWarning = false) =
var content = genOutFile(d)
if optStdout in gGlobalOptions:
if optStdout in d.conf.globalOptions:
writeRope(stdout, content)
else:
writeRope(content, getOutFile2(filename, outExt, "htmldocs"), useWarning)
writeRope(content, getOutFile2(d.conf, filename, outExt, "htmldocs"), useWarning)
proc writeOutputJson*(d: PDoc, filename, outExt: string,
useWarning = false) =
let content = %*{"orig": d.filename,
"nimble": getPackageName(d.filename),
"nimble": getPackageName(d.conf, d.filename),
"entries": d.jArray}
if optStdout in gGlobalOptions:
if optStdout in d.conf.globalOptions:
write(stdout, $content)
else:
var f: File
if open(f, getOutFile2(splitFile(filename).name,
if open(f, getOutFile2(d.conf, splitFile(filename).name,
outExt, "jsondocs"), fmWrite):
write(f, $content)
close(f)
else:
discard "fixme: error report"
proc commandDoc*() =
var ast = parseFile(gProjectMainIdx, newIdentCache())
proc commandDoc*(conf: ConfigRef) =
var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars)
var d = newDocumentor(conf.projectFull, conf)
d.hasToc = true
generateDoc(d, ast)
writeOutput(d, gProjectFull, HtmlExt)
writeOutput(d, conf.projectFull, HtmlExt)
generateIndex(d)
proc commandRstAux(filename, outExt: string) =
proc commandRstAux(conf: ConfigRef; filename, outExt: string) =
var filen = addFileExt(filename, "txt")
var d = newDocumentor(filen, options.gConfigVars)
var d = newDocumentor(filen, conf)
d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
status: int; content: string) =
var outp: string
if filename.len == 0:
inc(d.id)
let nameOnly = splitFile(d.filename).name
let subdir = getNimcacheDir() / nameOnly
let subdir = getNimcacheDir(conf) / nameOnly
createDir(subdir)
outp = subdir / (nameOnly & "_snippet_" & $d.id & ".nim")
elif isAbsolute(filename):
@ -815,14 +825,14 @@ proc commandRstAux(filename, outExt: string) =
# Nim's convention: every path is relative to the file it was written in:
outp = splitFile(d.filename).dir / filename
writeFile(outp, content)
let cmd = unescape(cmd) % quoteShell(outp)
rawMessage(hintExecuting, cmd)
let cmd = cmd % quoteShell(outp)
rawMessage(conf, hintExecuting, cmd)
if execShellCmd(cmd) != status:
rawMessage(errExecutionOfProgramFailed, cmd)
rawMessage(conf, errGenerated, "executing of external program failed: " & cmd)
d.isPureRst = true
var rst = parseRst(readFile(filen), filen, 0, 1, d.hasToc,
{roSupportRawDirective})
{roSupportRawDirective}, conf)
var modDesc = newStringOfCap(30_000)
#d.modDesc = newMutableRope(30_000)
renderRstToOut(d[], rst, modDesc)
@ -831,50 +841,50 @@ proc commandRstAux(filename, outExt: string) =
writeOutput(d, filename, outExt)
generateIndex(d)
proc commandRst2Html*() =
commandRstAux(gProjectFull, HtmlExt)
proc commandRst2Html*(conf: ConfigRef) =
commandRstAux(conf, conf.projectFull, HtmlExt)
proc commandRst2TeX*() =
proc commandRst2TeX*(conf: ConfigRef) =
splitter = "\\-"
commandRstAux(gProjectFull, TexExt)
commandRstAux(conf, conf.projectFull, TexExt)
proc commandJson*() =
var ast = parseFile(gProjectMainIdx, newIdentCache())
proc commandJson*(conf: ConfigRef) =
var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars)
var d = newDocumentor(conf.projectFull, conf)
d.hasToc = true
generateJson(d, ast)
let json = d.jArray
let content = rope(pretty(json))
if optStdout in gGlobalOptions:
if optStdout in d.conf.globalOptions:
writeRope(stdout, content)
else:
#echo getOutFile(gProjectFull, JsonExt)
writeRope(content, getOutFile(gProjectFull, JsonExt), useWarning = false)
writeRope(content, getOutFile(conf, conf.projectFull, JsonExt), useWarning = false)
proc commandTags*() =
var ast = parseFile(gProjectMainIdx, newIdentCache())
proc commandTags*(conf: ConfigRef) =
var ast = parseFile(conf.projectMainIdx.FileIndex, newIdentCache(), conf)
if ast == nil: return
var d = newDocumentor(gProjectFull, options.gConfigVars)
var d = newDocumentor(conf.projectFull, conf)
d.hasToc = true
var
content: Rope
generateTags(d, ast, content)
if optStdout in gGlobalOptions:
if optStdout in d.conf.globalOptions:
writeRope(stdout, content)
else:
#echo getOutFile(gProjectFull, TagsExt)
writeRope(content, getOutFile(gProjectFull, TagsExt), useWarning = false)
writeRope(content, getOutFile(conf, conf.projectFull, TagsExt), useWarning = false)
proc commandBuildIndex*() =
var content = mergeIndexes(gProjectFull).rope
proc commandBuildIndex*(conf: ConfigRef) =
var content = mergeIndexes(conf.projectFull).rope
let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), ["title",
"tableofcontents", "moduledesc", "date", "time",
"content", "author", "version", "analytics"],
["Index".rope, nil, nil, rope(getDateStr()),
rope(getClockStr()), content, nil, nil, nil])
# no analytics because context is not available
writeRope(code, getOutFile("theindex", HtmlExt))
writeRope(code, getOutFile(conf, "theindex", HtmlExt))

View file

@ -25,8 +25,8 @@ template closeImpl(body: untyped) {.dirty.} =
var g = PGen(p)
let useWarning = sfMainModule notin g.module.flags
#echo g.module.name.s, " ", g.module.owner.id, " ", gMainPackageId
if (g.module.owner.id == gMainPackageId and gWholeProject) or
sfMainModule in g.module.flags:
if (g.module.owner.id == gMainPackageId and optWholeProject in g.doc.conf.globalOptions) or
sfMainModule in g.module.flags:
body
try:
generateIndex(g.doc)
@ -55,7 +55,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
var g: PGen
new(g)
g.module = module
var d = newDocumentor(module.filename, options.gConfigVars)
var d = newDocumentor(module.filename, graph.config)
d.hasToc = true
g.doc = d
result = g

View file

@ -86,10 +86,10 @@ proc mapType(t: ast.PType): ptr libffi.TType =
else: result = nil
of tyFloat, tyFloat64: result = addr libffi.type_double
of tyFloat32: result = addr libffi.type_float
of tyVar, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyExpr,
tyStmt, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
result = addr libffi.type_pointer
of tyDistinct, tyAlias:
of tyDistinct, tyAlias, tySink:
result = mapType(t.sons[0])
else:
result = nil
@ -112,12 +112,12 @@ template `+!`(x, y: untyped): untyped =
proc packSize(v: PNode, typ: PType): int =
## computes the size of the blob
case typ.kind
of tyPtr, tyRef, tyVar:
of tyPtr, tyRef, tyVar, tyLent:
if v.kind in {nkNilLit, nkPtrLit}:
result = sizeof(pointer)
else:
result = sizeof(pointer) + packSize(v.sons[0], typ.lastSon)
of tyDistinct, tyGenericInst, tyAlias:
of tyDistinct, tyGenericInst, tyAlias, tySink:
result = packSize(v, typ.sons[0])
of tyArray:
# consider: ptr array[0..1000_000, int] which is common for interfacing;
@ -151,7 +151,7 @@ proc getField(n: PNode; position: int): PSym =
else: discard
proc packObject(x: PNode, typ: PType, res: pointer) =
internalAssert x.kind in {nkObjConstr, nkPar}
internalAssert x.kind in {nkObjConstr, nkPar, nkTupleConstr}
# compute the field's offsets:
discard typ.getSize
for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1):
@ -209,7 +209,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
awr(cstring, cstring(v.strVal))
else:
globalError(v.info, "cannot map pointer/proc value to FFI")
of tyPtr, tyRef, tyVar:
of tyPtr, tyRef, tyVar, tyLent:
if v.kind == nkNilLit:
# nothing to do since the memory is 0 initialized anyway
discard
@ -231,7 +231,7 @@ proc pack(v: PNode, typ: PType, res: pointer) =
packObject(v, typ, res)
of tyNil:
discard
of tyDistinct, tyGenericInst, tyAlias:
of tyDistinct, tyGenericInst, tyAlias, tySink:
pack(v, typ.sons[0], res)
else:
globalError(v.info, "cannot map value to FFI " & typeToString(v.typ))
@ -260,14 +260,14 @@ proc unpackObject(x: pointer, typ: PType, n: PNode): PNode =
# iterate over any actual field of 'n' ... if n is nil we need to create
# the nkPar node:
if n.isNil:
result = newNode(nkPar)
result = newNode(nkTupleConstr)
result.typ = typ
if typ.n.isNil:
internalError("cannot unpack unnamed tuple")
unpackObjectAdd(x, typ.n, result)
else:
result = n
if result.kind notin {nkObjConstr, nkPar}:
if result.kind notin {nkObjConstr, nkPar, nkTupleConstr}:
globalError(n.info, "cannot map value from FFI")
if typ.n.isNil:
globalError(n.info, "cannot unpack unnamed tuple")
@ -364,7 +364,7 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
result = n
else:
awi(nkPtrLit, cast[ByteAddress](p))
of tyPtr, tyRef, tyVar:
of tyPtr, tyRef, tyVar, tyLent:
let p = rd(pointer, x)
if p.isNil:
setNil()
@ -388,14 +388,14 @@ proc unpack(x: pointer, typ: PType, n: PNode): PNode =
aws(nkStrLit, $p)
of tyNil:
setNil()
of tyDistinct, tyGenericInst, tyAlias:
of tyDistinct, tyGenericInst, tyAlias, tySink:
result = unpack(x, typ.lastSon, n)
else:
# XXX what to do with 'array' here?
globalError(n.info, "cannot map value from FFI " & typeToString(typ))
proc fficast*(x: PNode, destTyp: PType): PNode =
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyPointer,
if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyLent, tyPointer,
tyProc, tyCString, tyString,
tySequence}:
result = newNodeIT(x.kind, x.info, destTyp)

View file

@ -14,11 +14,12 @@ import
rodread
type
TemplCtx {.pure, final.} = object
TemplCtx = object
owner, genSymOwner: PSym
instLines: bool # use the instantiation lines numbers
mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
# new symbol
config: ConfigRef
proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
result = copyNode(a)
@ -42,7 +43,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam:
handleParam actual.sons[s.owner.typ.len + s.position - 1]
else:
internalAssert sfGenSym in s.flags or s.kind == skType
internalAssert c.config, sfGenSym in s.flags or s.kind == skType
var x = PSym(idTableGet(c.mapping, s))
if x == nil:
x = copySym(s, false)
@ -59,11 +60,16 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
evalTemplateAux(templ.sons[i], actual, c, res)
result.add res
proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
const
errWrongNumberOfArguments = "wrong number of arguments"
errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
errTemplateInstantiationTooNested = "template instantiation too nested"
proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode =
# if the template has zero arguments, it can be called without ``()``
# `n` is then a nkSym or something similar
var totalParams = case n.kind
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: n.len-1
of nkCallKinds: n.len-1
else: 0
var
@ -82,10 +88,10 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
if givenRegularParams < 0: givenRegularParams = 0
if totalParams > expectedRegularParams + genericParams:
globalError(n.info, errWrongNumberOfArguments)
globalError(conf, n.info, errWrongNumberOfArguments)
if totalParams < genericParams:
globalError(n.info, errMissingGenericParamsForTemplate,
globalError(conf, n.info, errMissingGenericParamsForTemplate %
n.renderTree)
result = newNodeI(nkArgList, n.info)
@ -97,7 +103,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
for i in givenRegularParams+1 .. expectedRegularParams:
let default = s.typ.n.sons[i].sym.ast
if default.isNil or default.kind == nkEmpty:
localError(n.info, errWrongNumberOfArguments)
localError(conf, n.info, errWrongNumberOfArguments)
addSon(result, ast.emptyNode)
else:
addSon(result, default.copyTree)
@ -106,8 +112,9 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
for i in 1 .. genericParams:
result.addSon n.sons[givenRegularParams + i]
var evalTemplateCounter* = 0
# to prevent endless recursion in templates instantiation
# to prevent endless recursion in template instantiation
const evalTemplateLimit* = 1000
var evalTemplateCounter* = 0 # XXX remove this global
proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
when true:
@ -131,17 +138,19 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode =
result.add res
result.typ = res.typ
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
conf: ConfigRef; fromHlo=false): PNode =
inc(evalTemplateCounter)
if evalTemplateCounter > 100:
globalError(n.info, errTemplateInstantiationTooNested)
if evalTemplateCounter > evalTemplateLimit:
globalError(conf, n.info, errTemplateInstantiationTooNested)
result = n
# replace each param by the corresponding node:
var args = evalTemplateArgs(n, tmpl, fromHlo)
var args = evalTemplateArgs(n, tmpl, conf, fromHlo)
var ctx: TemplCtx
ctx.owner = tmpl
ctx.genSymOwner = genSymOwner
ctx.config = conf
initIdTable(ctx.mapping)
let body = tmpl.getBody
@ -150,7 +159,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
evalTemplateAux(body, args, ctx, result)
if result.len == 1: result = result.sons[0]
else:
localError(result.info, errIllFormedAstX,
localError(conf, result.info, "illformed AST: " &
renderTree(result, {renderNoComments}))
else:
result = copyNode(body)

View file

@ -14,7 +14,7 @@
import
ropes, os, strutils, osproc, platform, condsyms, options, msgs,
securehash, streams
configuration, std / sha1, streams
#from debuginfo import writeDebugInfo
@ -176,10 +176,10 @@ compiler bcc:
result = (
name: "bcc",
objExt: "obj",
optSpeed: " -O2 -6 ",
optSpeed: " -O3 -6 ",
optSize: " -O1 -6 ",
compilerExe: "bcc32",
cppCompiler: "",
compilerExe: "bcc32c",
cppCompiler: "cpp32c",
compileTmpl: "-c $options $include -o$objfile $file",
buildGui: " -tW",
buildDll: " -tWD",
@ -193,7 +193,9 @@ compiler bcc:
pic: "",
asmStmtFrmt: "__asm{$n$1$n}$n",
structStmtFmt: "$1 $2",
props: {hasCpp})
props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard,
hasAttribute})
# Digital Mars C Compiler
compiler dmc:
@ -374,80 +376,81 @@ proc nameToCC*(name: string): TSystemCC =
return i
result = ccNone
proc getConfigVar(c: TSystemCC, suffix: string): string =
proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
# use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given
# for niminst support
let fullSuffix =
if gCmd == cmdCompileToCpp:
if conf.cmd == cmdCompileToCpp:
".cpp" & suffix
elif gCmd == cmdCompileToOC:
elif conf.cmd == cmdCompileToOC:
".objc" & suffix
elif gCmd == cmdCompileToJS:
elif conf.cmd == cmdCompileToJS:
".js" & suffix
else:
suffix
if (platform.hostOS != targetOS or platform.hostCPU != targetCPU) and
optCompileOnly notin gGlobalOptions:
optCompileOnly notin conf.globalOptions:
let fullCCname = platform.CPU[targetCPU].name & '.' &
platform.OS[targetOS].name & '.' &
CC[c].name & fullSuffix
result = getConfigVar(fullCCname)
result = getConfigVar(conf, fullCCname)
if result.len == 0:
# not overriden for this cross compilation setting?
result = getConfigVar(CC[c].name & fullSuffix)
result = getConfigVar(conf, CC[c].name & fullSuffix)
else:
result = getConfigVar(CC[c].name & fullSuffix)
result = getConfigVar(conf, CC[c].name & fullSuffix)
proc setCC*(ccname: string) =
proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) =
cCompiler = nameToCC(ccname)
if cCompiler == ccNone: rawMessage(errUnknownCcompiler, ccname)
compileOptions = getConfigVar(cCompiler, ".options.always")
if cCompiler == ccNone:
localError(conf, info, "unknown C compiler: '$1'" % ccname)
compileOptions = getConfigVar(conf, cCompiler, ".options.always")
linkOptions = ""
ccompilerpath = getConfigVar(cCompiler, ".path")
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[cCompiler].name)
ccompilerpath = getConfigVar(conf, cCompiler, ".path")
for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name)
defineSymbol(conf.symbols, CC[cCompiler].name)
proc addOpt(dest: var string, src: string) =
if len(dest) == 0 or dest[len(dest)-1] != ' ': add(dest, " ")
add(dest, src)
proc addLinkOption*(option: string) =
proc addLinkOption*(conf: ConfigRef; option: string) =
addOpt(linkOptions, option)
proc addCompileOption*(option: string) =
proc addCompileOption*(conf: ConfigRef; option: string) =
if strutils.find(compileOptions, option, 0) < 0:
addOpt(compileOptions, option)
proc addLinkOptionCmd*(option: string) =
proc addLinkOptionCmd*(conf: ConfigRef; option: string) =
addOpt(linkOptionsCmd, option)
proc addCompileOptionCmd*(option: string) =
proc addCompileOptionCmd*(conf: ConfigRef; option: string) =
compileOptionsCmd.add(option)
proc initVars*() =
proc initVars*(conf: ConfigRef) =
# we need to define the symbol here, because ``CC`` may have never been set!
for i in countup(low(CC), high(CC)): undefSymbol(CC[i].name)
defineSymbol(CC[cCompiler].name)
addCompileOption(getConfigVar(cCompiler, ".options.always"))
for i in countup(low(CC), high(CC)): undefSymbol(conf.symbols, CC[i].name)
defineSymbol(conf.symbols, CC[cCompiler].name)
addCompileOption(conf, getConfigVar(conf, cCompiler, ".options.always"))
#addLinkOption(getConfigVar(cCompiler, ".options.linker"))
if len(ccompilerpath) == 0:
ccompilerpath = getConfigVar(cCompiler, ".path")
ccompilerpath = getConfigVar(conf, cCompiler, ".path")
proc completeCFilePath*(cfile: string, createSubDir: bool = true): string =
result = completeGeneratedFilePath(cfile, createSubDir)
proc completeCFilePath*(conf: ConfigRef; cfile: string, createSubDir: bool = true): string =
result = completeGeneratedFilePath(conf, cfile, createSubDir)
proc toObjFile*(filename: string): string =
proc toObjFile*(conf: ConfigRef; filename: string): string =
# Object file for compilation
#if filename.endsWith(".cpp"):
# result = changeFileExt(filename, "cpp." & CC[cCompiler].objExt)
#else:
result = changeFileExt(filename, CC[cCompiler].objExt)
proc addFileToCompile*(cf: Cfile) =
proc addFileToCompile*(conf: ConfigRef; cf: Cfile) =
toCompile.add(cf)
proc resetCompilationLists* =
proc resetCompilationLists*(conf: ConfigRef) =
toCompile.setLen 0
## XXX: we must associate these with their originating module
# when the module is loaded/unloaded it adds/removes its items
@ -455,108 +458,112 @@ proc resetCompilationLists* =
# Maybe we can do that in checkDep on the other hand?
externalToLink.setLen 0
proc addExternalFileToLink*(filename: string) =
proc addExternalFileToLink*(conf: ConfigRef; filename: string) =
externalToLink.insert(filename, 0)
proc execWithEcho(cmd: string, msg = hintExecuting): int =
rawMessage(msg, cmd)
proc execWithEcho(conf: ConfigRef; cmd: string, msg = hintExecuting): int =
rawMessage(conf, msg, cmd)
result = execCmd(cmd)
proc execExternalProgram*(cmd: string, msg = hintExecuting) =
if execWithEcho(cmd, msg) != 0:
rawMessage(errExecutionOfProgramFailed, cmd)
proc execExternalProgram*(conf: ConfigRef; cmd: string, msg = hintExecuting) =
if execWithEcho(conf, cmd, msg) != 0:
rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
cmd)
proc generateScript(projectFile: string, script: Rope) =
proc generateScript(conf: ConfigRef; projectFile: string, script: Rope) =
let (dir, name, ext) = splitFile(projectFile)
writeRope(script, dir / addFileExt("compile_" & name,
writeRope(script, getNimcacheDir(conf) / addFileExt("compile_" & name,
platform.OS[targetOS].scriptExt))
copyFile(conf.libpath / "nimbase.h", getNimcacheDir(conf) / "nimbase.h")
proc getOptSpeed(c: TSystemCC): string =
result = getConfigVar(c, ".options.speed")
proc getOptSpeed(conf: ConfigRef; c: TSystemCC): string =
result = getConfigVar(conf, c, ".options.speed")
if result == "":
result = CC[c].optSpeed # use default settings from this file
proc getDebug(c: TSystemCC): string =
result = getConfigVar(c, ".options.debug")
proc getDebug(conf: ConfigRef; c: TSystemCC): string =
result = getConfigVar(conf, c, ".options.debug")
if result == "":
result = CC[c].debug # use default settings from this file
proc getOptSize(c: TSystemCC): string =
result = getConfigVar(c, ".options.size")
proc getOptSize(conf: ConfigRef; c: TSystemCC): string =
result = getConfigVar(conf, c, ".options.size")
if result == "":
result = CC[c].optSize # use default settings from this file
proc noAbsolutePaths: bool {.inline.} =
proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} =
# We used to check current OS != specified OS, but this makes no sense
# really: Cross compilation from Linux to Linux for example is entirely
# reasonable.
# `optGenMapping` is included here for niminst.
result = gGlobalOptions * {optGenScript, optGenMapping} != {}
result = conf.globalOptions * {optGenScript, optGenMapping} != {}
proc cFileSpecificOptions(cfilename: string): string =
proc cFileSpecificOptions(conf: ConfigRef; cfilename: string): string =
result = compileOptions
for option in compileOptionsCmd:
if strutils.find(result, option, 0) < 0:
addOpt(result, option)
var trunk = splitFile(cfilename).name
if optCDebug in gGlobalOptions:
var key = trunk & ".debug"
if existsConfigVar(key): addOpt(result, getConfigVar(key))
else: addOpt(result, getDebug(cCompiler))
if optOptimizeSpeed in gOptions:
var key = trunk & ".speed"
if existsConfigVar(key): addOpt(result, getConfigVar(key))
else: addOpt(result, getOptSpeed(cCompiler))
elif optOptimizeSize in gOptions:
var key = trunk & ".size"
if existsConfigVar(key): addOpt(result, getConfigVar(key))
else: addOpt(result, getOptSize(cCompiler))
var key = trunk & ".always"
if existsConfigVar(key): addOpt(result, getConfigVar(key))
let trunk = splitFile(cfilename).name
if optCDebug in conf.globalOptions:
let key = trunk & ".debug"
if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
else: addOpt(result, getDebug(conf, cCompiler))
if optOptimizeSpeed in conf.options:
let key = trunk & ".speed"
if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
else: addOpt(result, getOptSpeed(conf, cCompiler))
elif optOptimizeSize in conf.options:
let key = trunk & ".size"
if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
else: addOpt(result, getOptSize(conf, cCompiler))
let key = trunk & ".always"
if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key))
proc getCompileOptions: string =
result = cFileSpecificOptions("__dummy__")
proc getCompileOptions(conf: ConfigRef): string =
result = cFileSpecificOptions(conf, "__dummy__")
proc getLinkOptions: string =
proc getLinkOptions(conf: ConfigRef): string =
result = linkOptions & " " & linkOptionsCmd & " "
for linkedLib in items(cLinkedLibs):
result.add(CC[cCompiler].linkLibCmd % linkedLib.quoteShell)
for libDir in items(cLibs):
result.add(join([CC[cCompiler].linkDirCmd, libDir.quoteShell]))
proc needsExeExt(): bool {.inline.} =
result = (optGenScript in gGlobalOptions and targetOS == osWindows) or
proc needsExeExt(conf: ConfigRef): bool {.inline.} =
result = (optGenScript in conf.globalOptions and targetOS == osWindows) or
(platform.hostOS == osWindows)
proc getCompilerExe(compiler: TSystemCC; cfile: string): string =
result = if gCmd == cmdCompileToCpp and not cfile.endsWith(".c"):
proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: string): string =
result = if conf.cmd == cmdCompileToCpp and not cfile.endsWith(".c"):
CC[compiler].cppCompiler
else:
CC[compiler].compilerExe
if result.len == 0:
rawMessage(errCompilerDoesntSupportTarget, CC[compiler].name)
rawMessage(conf, errGenerated,
"Compiler '$1' doesn't support the requested target" %
CC[compiler].name)
proc getLinkerExe(compiler: TSystemCC): string =
proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string =
result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe
elif gMixedMode and gCmd != cmdCompileToCpp: CC[compiler].cppCompiler
else: compiler.getCompilerExe("")
elif gMixedMode and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler
else: getCompilerExe(conf, compiler, "")
proc getCompileCFileCmd*(cfile: Cfile): string =
proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile): string =
var c = cCompiler
var options = cFileSpecificOptions(cfile.cname)
var exe = getConfigVar(c, ".exe")
if exe.len == 0: exe = c.getCompilerExe(cfile.cname)
var options = cFileSpecificOptions(conf, cfile.cname)
var exe = getConfigVar(conf, c, ".exe")
if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname)
if needsExeExt(): exe = addFileExt(exe, "exe")
if optGenDynLib in gGlobalOptions and
if needsExeExt(conf): exe = addFileExt(exe, "exe")
if optGenDynLib in conf.globalOptions and
ospNeedsPIC in platform.OS[targetOS].props:
add(options, ' ' & CC[c].pic)
var includeCmd, compilePattern: string
if not noAbsolutePaths():
if not noAbsolutePaths(conf):
# compute include paths:
includeCmd = CC[c].includeCmd & quoteShell(libpath)
includeCmd = CC[c].includeCmd & quoteShell(conf.libpath)
for includeDir in items(cIncludes):
includeCmd.add(join([CC[c].includeCmd, includeDir.quoteShell]))
@ -564,18 +571,18 @@ proc getCompileCFileCmd*(cfile: Cfile): string =
compilePattern = joinPath(ccompilerpath, exe)
else:
includeCmd = ""
compilePattern = c.getCompilerExe(cfile.cname)
compilePattern = getCompilerExe(conf, c, cfile.cname)
var cf = if noAbsolutePaths(): extractFilename(cfile.cname)
var cf = if noAbsolutePaths(conf): extractFilename(cfile.cname)
else: cfile.cname
var objfile =
if cfile.obj.len == 0:
if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths():
toObjFile(cf)
if not cfile.flags.contains(CfileFlag.External) or noAbsolutePaths(conf):
toObjFile(conf, cf)
else:
completeCFilePath(toObjFile(cf))
elif noAbsolutePaths():
completeCFilePath(conf, toObjFile(conf, cf))
elif noAbsolutePaths(conf):
extractFilename(cfile.obj)
else:
cfile.obj
@ -584,30 +591,30 @@ proc getCompileCFileCmd*(cfile: Cfile): string =
cf = quoteShell(cf)
result = quoteShell(compilePattern % [
"file", cf, "objfile", objfile, "options", options,
"include", includeCmd, "nim", getPrefixDir(),
"nim", getPrefixDir(), "lib", libpath])
"include", includeCmd, "nim", getPrefixDir(conf),
"nim", getPrefixDir(conf), "lib", conf.libpath])
add(result, ' ')
addf(result, CC[c].compileTmpl, [
"file", cf, "objfile", objfile,
"options", options, "include", includeCmd,
"nim", quoteShell(getPrefixDir()),
"nim", quoteShell(getPrefixDir()),
"lib", quoteShell(libpath)])
"nim", quoteShell(getPrefixDir(conf)),
"nim", quoteShell(getPrefixDir(conf)),
"lib", quoteShell(conf.libpath)])
proc footprint(cfile: Cfile): SecureHash =
proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash =
result = secureHash(
$secureHashFile(cfile.cname) &
platform.OS[targetOS].name &
platform.CPU[targetCPU].name &
extccomp.CC[extccomp.cCompiler].name &
getCompileCFileCmd(cfile))
getCompileCFileCmd(conf, cfile))
proc externalFileChanged(cfile: Cfile): bool =
if gCmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
if conf.cmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM}:
return false
var hashFile = toGeneratedFile(cfile.cname.withPackageName, "sha1")
var currentHash = footprint(cfile)
var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1")
var currentHash = footprint(conf, cfile)
var f: File
if open(f, hashFile, fmRead):
let oldHash = parseSecureHash(f.readLine())
@ -620,133 +627,137 @@ proc externalFileChanged(cfile: Cfile): bool =
f.writeLine($currentHash)
close(f)
proc addExternalFileToCompile*(c: var Cfile) =
if optForceFullMake notin gGlobalOptions and not externalFileChanged(c):
proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) =
if optForceFullMake notin conf.globalOptions and not externalFileChanged(conf, c):
c.flags.incl CfileFlag.Cached
toCompile.add(c)
proc addExternalFileToCompile*(filename: string) =
proc addExternalFileToCompile*(conf: ConfigRef; filename: string) =
var c = Cfile(cname: filename,
obj: toObjFile(completeCFilePath(changeFileExt(filename, ""), false)),
obj: toObjFile(conf, completeCFilePath(conf, changeFileExt(filename, ""), false)),
flags: {CfileFlag.External})
addExternalFileToCompile(c)
addExternalFileToCompile(conf, c)
proc compileCFile(list: CFileList, script: var Rope, cmds: var TStringSeq,
proc compileCFile(conf: ConfigRef; list: CFileList, script: var Rope, cmds: var TStringSeq,
prettyCmds: var TStringSeq) =
for it in list:
# call the C compiler for the .c file:
if it.flags.contains(CfileFlag.Cached): continue
var compileCmd = getCompileCFileCmd(it)
if optCompileOnly notin gGlobalOptions:
var compileCmd = getCompileCFileCmd(conf, it)
if optCompileOnly notin conf.globalOptions:
add(cmds, compileCmd)
let (_, name, _) = splitFile(it.cname)
add(prettyCmds, "CC: " & name)
if optGenScript in gGlobalOptions:
if optGenScript in conf.globalOptions:
add(script, compileCmd)
add(script, tnl)
proc getLinkCmd(projectfile, objfiles: string): string =
if optGenStaticLib in gGlobalOptions:
proc getLinkCmd(conf: ConfigRef; projectfile, objfiles: string): string =
if optGenStaticLib in conf.globalOptions:
var libname: string
if options.outFile.len > 0:
libname = options.outFile.expandTilde
if conf.outFile.len > 0:
libname = conf.outFile.expandTilde
if not libname.isAbsolute():
libname = getCurrentDir() / libname
else:
libname = (libNameTmpl() % splitFile(gProjectName).name)
libname = (libNameTmpl() % splitFile(conf.projectName).name)
result = CC[cCompiler].buildLib % ["libfile", libname,
"objfiles", objfiles]
else:
var linkerExe = getConfigVar(cCompiler, ".linkerexe")
if len(linkerExe) == 0: linkerExe = cCompiler.getLinkerExe
var linkerExe = getConfigVar(conf, cCompiler, ".linkerexe")
if len(linkerExe) == 0: linkerExe = getLinkerExe(conf, cCompiler)
# bug #6452: We must not use ``quoteShell`` here for ``linkerExe``
if needsExeExt(): linkerExe = addFileExt(linkerExe, "exe")
if noAbsolutePaths(): result = linkerExe
if needsExeExt(conf): linkerExe = addFileExt(linkerExe, "exe")
if noAbsolutePaths(conf): result = linkerExe
else: result = joinPath(ccompilerpath, linkerExe)
let buildgui = if optGenGuiApp in gGlobalOptions: CC[cCompiler].buildGui
let buildgui = if optGenGuiApp in conf.globalOptions: CC[cCompiler].buildGui
else: ""
var exefile, builddll: string
if optGenDynLib in gGlobalOptions:
if optGenDynLib in conf.globalOptions:
exefile = platform.OS[targetOS].dllFrmt % splitFile(projectfile).name
builddll = CC[cCompiler].buildDll
else:
exefile = splitFile(projectfile).name & platform.OS[targetOS].exeExt
builddll = ""
if options.outFile.len > 0:
exefile = options.outFile.expandTilde
if conf.outFile.len > 0:
exefile = conf.outFile.expandTilde
if not exefile.isAbsolute():
exefile = getCurrentDir() / exefile
if not noAbsolutePaths():
if not noAbsolutePaths(conf):
if not exefile.isAbsolute():
exefile = joinPath(splitFile(projectfile).dir, exefile)
when false:
if optCDebug in gGlobalOptions:
if optCDebug in conf.globalOptions:
writeDebugInfo(exefile.changeFileExt("ndb"))
exefile = quoteShell(exefile)
let linkOptions = getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker")
var linkTmpl = getConfigVar(cCompiler, ".linkTmpl")
let linkOptions = getLinkOptions(conf) & " " &
getConfigVar(conf, cCompiler, ".options.linker")
var linkTmpl = getConfigVar(conf, cCompiler, ".linkTmpl")
if linkTmpl.len == 0:
linkTmpl = CC[cCompiler].linkTmpl
result = quoteShell(result % ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
"exefile", exefile, "nim", getPrefixDir(), "lib", libpath])
"exefile", exefile, "nim", getPrefixDir(conf), "lib", conf.libpath])
result.add ' '
addf(result, linkTmpl, ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions,
"objfiles", objfiles, "exefile", exefile,
"nim", quoteShell(getPrefixDir()),
"lib", quoteShell(libpath)])
"nim", quoteShell(getPrefixDir(conf)),
"lib", quoteShell(conf.libpath)])
template tryExceptOSErrorMessage(errorPrefix: string = "", body: untyped): typed =
template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped): typed =
try:
body
except OSError:
let ose = (ref OSError)(getCurrentException())
if errorPrefix.len > 0:
rawMessage(errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode)
rawMessage(conf, errGenerated, errorPrefix & " " & ose.msg & " " & $ose.errorCode)
else:
rawMessage(errExecutionOfProgramFailed, ose.msg & " " & $ose.errorCode)
rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
(ose.msg & " " & $ose.errorCode))
raise
proc execLinkCmd(linkCmd: string) =
tryExceptOSErrorMessage("invocation of external linker program failed."):
execExternalProgram(linkCmd,
if optListCmd in gGlobalOptions or gVerbosity > 1: hintExecuting else: hintLinking)
proc execLinkCmd(conf: ConfigRef; linkCmd: string) =
tryExceptOSErrorMessage(conf, "invocation of external linker program failed."):
execExternalProgram(conf, linkCmd,
if optListCmd in conf.globalOptions or conf.verbosity > 1: hintExecuting else: hintLinking)
proc execCmdsInParallel(cmds: seq[string]; prettyCb: proc (idx: int)) =
proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx: int)) =
let runCb = proc (idx: int, p: Process) =
let exitCode = p.peekExitCode
if exitCode != 0:
rawMessage(errGenerated, "execution of an external compiler program '" &
rawMessage(conf, errGenerated, "execution of an external compiler program '" &
cmds[idx] & "' failed with exit code: " & $exitCode & "\n\n" &
p.outputStream.readAll.strip)
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
if conf.numberOfProcessors == 0: conf.numberOfProcessors = countProcessors()
var res = 0
if gNumberOfProcessors <= 1:
if conf.numberOfProcessors <= 1:
for i in countup(0, high(cmds)):
tryExceptOSErrorMessage("invocation of external compiler program failed."):
res = execWithEcho(cmds[i])
if res != 0: rawMessage(errExecutionOfProgramFailed, cmds[i])
tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
res = execWithEcho(conf, cmds[i])
if res != 0:
rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
cmds[i])
else:
tryExceptOSErrorMessage("invocation of external compiler program failed."):
if optListCmd in gGlobalOptions or gVerbosity > 1:
tryExceptOSErrorMessage(conf, "invocation of external compiler program failed."):
if optListCmd in conf.globalOptions or conf.verbosity > 1:
res = execProcesses(cmds, {poEchoCmd, poStdErrToStdOut, poUsePath},
gNumberOfProcessors, afterRunEvent=runCb)
elif gVerbosity == 1:
conf.numberOfProcessors, afterRunEvent=runCb)
elif conf.verbosity == 1:
res = execProcesses(cmds, {poStdErrToStdOut, poUsePath},
gNumberOfProcessors, prettyCb, afterRunEvent=runCb)
conf.numberOfProcessors, prettyCb, afterRunEvent=runCb)
else:
res = execProcesses(cmds, {poStdErrToStdOut, poUsePath},
gNumberOfProcessors, afterRunEvent=runCb)
conf.numberOfProcessors, afterRunEvent=runCb)
if res != 0:
if gNumberOfProcessors <= 1:
rawMessage(errExecutionOfProgramFailed, cmds.join())
if conf.numberOfProcessors <= 1:
rawMessage(conf, errGenerated, "execution of an external program failed: '$1'" %
cmds.join())
proc callCCompiler*(projectfile: string) =
proc callCCompiler*(conf: ConfigRef; projectfile: string) =
var
linkCmd: string
if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
return # speed up that call if only compiling and no script shall be
# generated
#var c = cCompiler
@ -755,36 +766,36 @@ proc callCCompiler*(projectfile: string) =
var prettyCmds: TStringSeq = @[]
let prettyCb = proc (idx: int) =
echo prettyCmds[idx]
compileCFile(toCompile, script, cmds, prettyCmds)
if optCompileOnly notin gGlobalOptions:
execCmdsInParallel(cmds, prettyCb)
if optNoLinking notin gGlobalOptions:
compileCFile(conf, toCompile, script, cmds, prettyCmds)
if optCompileOnly notin conf.globalOptions:
execCmdsInParallel(conf, cmds, prettyCb)
if optNoLinking notin conf.globalOptions:
# call the linker:
var objfiles = ""
for it in externalToLink:
let objFile = if noAbsolutePaths(): it.extractFilename else: it
let objFile = if noAbsolutePaths(conf): it.extractFilename else: it
add(objfiles, ' ')
add(objfiles, quoteShell(
addFileExt(objFile, CC[cCompiler].objExt)))
for x in toCompile:
let objFile = if noAbsolutePaths(): x.obj.extractFilename else: x.obj
let objFile = if noAbsolutePaths(conf): x.obj.extractFilename else: x.obj
add(objfiles, ' ')
add(objfiles, quoteShell(objFile))
linkCmd = getLinkCmd(projectfile, objfiles)
if optCompileOnly notin gGlobalOptions:
execLinkCmd(linkCmd)
linkCmd = getLinkCmd(conf, projectfile, objfiles)
if optCompileOnly notin conf.globalOptions:
execLinkCmd(conf, linkCmd)
else:
linkCmd = ""
if optGenScript in gGlobalOptions:
if optGenScript in conf.globalOptions:
add(script, linkCmd)
add(script, tnl)
generateScript(projectfile, script)
generateScript(conf, projectfile, script)
#from json import escapeJson
import json
proc writeJsonBuildInstructions*(projectfile: string) =
proc writeJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
template lit(x: untyped) = f.write x
template str(x: untyped) =
when compiles(escapeJson(x, buf)):
@ -794,11 +805,11 @@ proc writeJsonBuildInstructions*(projectfile: string) =
else:
f.write escapeJson(x)
proc cfiles(f: File; buf: var string; clist: CfileList, isExternal: bool) =
proc cfiles(conf: ConfigRef; f: File; buf: var string; clist: CfileList, isExternal: bool) =
var pastStart = false
for it in clist:
if CfileFlag.Cached in it.flags: continue
let compileCmd = getCompileCFileCmd(it)
let compileCmd = getCompileCFileCmd(conf, it)
if pastStart: lit "],\L"
lit "["
str it.cname
@ -807,11 +818,11 @@ proc writeJsonBuildInstructions*(projectfile: string) =
pastStart = true
lit "]\L"
proc linkfiles(f: File; buf, objfiles: var string; clist: CfileList;
proc linkfiles(conf: ConfigRef; f: File; buf, objfiles: var string; clist: CfileList;
llist: seq[string]) =
var pastStart = false
for it in llist:
let objfile = if noAbsolutePaths(): it.extractFilename
let objfile = if noAbsolutePaths(conf): it.extractFilename
else: it
let objstr = addFileExt(objfile, CC[cCompiler].objExt)
add(objfiles, ' ')
@ -832,25 +843,25 @@ proc writeJsonBuildInstructions*(projectfile: string) =
var buf = newStringOfCap(50)
let file = projectfile.splitFile.name
let jsonFile = toGeneratedFile(file, "json")
let jsonFile = toGeneratedFile(conf, file, "json")
var f: File
if open(f, jsonFile, fmWrite):
lit "{\"compile\":[\L"
cfiles(f, buf, toCompile, false)
cfiles(conf, f, buf, toCompile, false)
lit "],\L\"link\":[\L"
var objfiles = ""
# XXX add every file here that is to link
linkfiles(f, buf, objfiles, toCompile, externalToLink)
linkfiles(conf, f, buf, objfiles, toCompile, externalToLink)
lit "],\L\"linkcmd\": "
str getLinkCmd(projectfile, objfiles)
str getLinkCmd(conf, projectfile, objfiles)
lit "\L}\L"
close(f)
proc runJsonBuildInstructions*(projectfile: string) =
proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: string) =
let file = projectfile.splitFile.name
let jsonFile = toGeneratedFile(file, "json")
let jsonFile = toGeneratedFile(conf, file, "json")
try:
let data = json.parseFile(jsonFile)
let toCompile = data["compile"]
@ -867,32 +878,32 @@ proc runJsonBuildInstructions*(projectfile: string) =
let prettyCb = proc (idx: int) =
echo prettyCmds[idx]
execCmdsInParallel(cmds, prettyCb)
execCmdsInParallel(conf, cmds, prettyCb)
let linkCmd = data["linkcmd"]
doAssert linkCmd.kind == JString
execLinkCmd(linkCmd.getStr)
execLinkCmd(conf, linkCmd.getStr)
except:
echo getCurrentException().getStackTrace()
quit "error evaluating JSON file: " & jsonFile
proc genMappingFiles(list: CFileList): Rope =
proc genMappingFiles(conf: ConfigRef; list: CFileList): Rope =
for it in list:
addf(result, "--file:r\"$1\"$N", [rope(it.cname)])
proc writeMapping*(gSymbolMapping: Rope) =
if optGenMapping notin gGlobalOptions: return
proc writeMapping*(conf: ConfigRef; symbolMapping: Rope) =
if optGenMapping notin conf.globalOptions: return
var code = rope("[C_Files]\n")
add(code, genMappingFiles(toCompile))
add(code, genMappingFiles(conf, toCompile))
add(code, "\n[C_Compiler]\nFlags=")
add(code, strutils.escape(getCompileOptions()))
add(code, strutils.escape(getCompileOptions(conf)))
add(code, "\n[Linker]\nFlags=")
add(code, strutils.escape(getLinkOptions() & " " &
getConfigVar(cCompiler, ".options.linker")))
add(code, strutils.escape(getLinkOptions(conf) & " " &
getConfigVar(conf, cCompiler, ".options.linker")))
add(code, "\n[Environment]\nlibpath=")
add(code, strutils.escape(libpath))
add(code, strutils.escape(conf.libpath))
addf(code, "\n[Symbols]$n$1", [gSymbolMapping])
writeRope(code, joinPath(gProjectPath, "mapping.txt"))
addf(code, "\n[Symbols]$n$1", [symbolMapping])
writeRope(code, joinPath(conf.projectPath, "mapping.txt"))

View file

@ -27,7 +27,7 @@ type
emit, conc, toStr: string
curly, bracket, par: int
pendingExprLine: bool
config: ConfigRef
const
PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
@ -35,16 +35,15 @@ const
proc newLine(p: var TTmplParser) =
llStreamWrite(p.outp, repeat(')', p.emitPar))
p.emitPar = 0
if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
if p.info.line > uint16(1): llStreamWrite(p.outp, "\n")
if p.pendingExprLine:
llStreamWrite(p.outp, spaces(2))
p.pendingExprLine = false
proc scanPar(p: var TTmplParser, d: int) =
var i = d
while true:
while i < p.x.len:
case p.x[i]
of '\0': break
of '(': inc(p.par)
of ')': dec(p.par)
of '[': inc(p.bracket)
@ -63,16 +62,19 @@ const
proc parseLine(p: var TTmplParser) =
var j = 0
while p.x[j] == ' ': inc(j)
if p.x[0] == p.nimDirective and p.x[1] == '?':
let len = p.x.len
while j < len and p.x[j] == ' ': inc(j)
if len >= 2 and p.x[0] == p.nimDirective and p.x[1] == '?':
newLine(p)
elif p.x[j] == p.nimDirective:
elif j < len and p.x[j] == p.nimDirective:
newLine(p)
inc(j)
while p.x[j] == ' ': inc(j)
while j < len and p.x[j] == ' ': inc(j)
let d = j
var keyw = ""
while p.x[j] in PatternChars:
while j < len and p.x[j] in PatternChars:
add(keyw, p.x[j])
inc(j)
@ -84,7 +86,7 @@ proc parseLine(p: var TTmplParser) =
dec(p.indent, 2)
else:
p.info.col = int16(j)
localError(p.info, errXNotAllowedHere, "end")
localError(p.config, p.info, "'end' does not close a control flow construct")
llStreamWrite(p.outp, spaces(p.indent))
llStreamWrite(p.outp, "#end")
of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator",
@ -126,10 +128,8 @@ proc parseLine(p: var TTmplParser) =
llStreamWrite(p.outp, "(\"")
inc(p.emitPar)
p.state = psTempl
while true:
while j < len:
case p.x[j]
of '\0':
break
of '\x01'..'\x1F', '\x80'..'\xFF':
llStreamWrite(p.outp, "\\x")
llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
@ -156,11 +156,8 @@ proc parseLine(p: var TTmplParser) =
llStreamWrite(p.outp, '(')
inc(j)
var curly = 0
while true:
while j < len:
case p.x[j]
of '\0':
localError(p.info, errXExpected, "}")
break
of '{':
inc(j)
inc(curly)
@ -173,6 +170,9 @@ proc parseLine(p: var TTmplParser) =
else:
llStreamWrite(p.outp, p.x[j])
inc(j)
if curly > 0:
localError(p.config, p.info, "expected closing '}'")
break
llStreamWrite(p.outp, ')')
llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, '\"')
@ -181,7 +181,7 @@ proc parseLine(p: var TTmplParser) =
llStreamWrite(p.outp, p.conc)
llStreamWrite(p.outp, p.toStr)
llStreamWrite(p.outp, '(')
while p.x[j] in PatternChars:
while j < len and p.x[j] in PatternChars:
llStreamWrite(p.outp, p.x[j])
inc(j)
llStreamWrite(p.outp, ')')
@ -193,28 +193,29 @@ proc parseLine(p: var TTmplParser) =
inc(j)
else:
p.info.col = int16(j)
localError(p.info, errInvalidExpression, "$")
localError(p.config, p.info, "invalid expression")
else:
llStreamWrite(p.outp, p.x[j])
inc(j)
llStreamWrite(p.outp, "\\n\"")
proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode; conf: ConfigRef): PLLStream =
var p: TTmplParser
p.info = newLineInfo(filename, 0, 0)
p.config = conf
p.info = newLineInfo(conf, filename, 0, 0)
p.outp = llStreamOpen("")
p.inp = stdin
p.subsChar = charArg(call, "subschar", 1, '$')
p.nimDirective = charArg(call, "metachar", 2, '#')
p.emit = strArg(call, "emit", 3, "result.add")
p.conc = strArg(call, "conc", 4, " & ")
p.toStr = strArg(call, "tostring", 5, "$")
p.subsChar = charArg(conf, call, "subschar", 1, '$')
p.nimDirective = charArg(conf, call, "metachar", 2, '#')
p.emit = strArg(conf, call, "emit", 3, "result.add")
p.conc = strArg(conf, call, "conc", 4, " & ")
p.toStr = strArg(conf, call, "tostring", 5, "$")
p.x = newStringOfCap(120)
# do not process the first line which contains the directive:
if llStreamReadLine(p.inp, p.x):
p.info.line = p.info.line + int16(1)
p.info.line = p.info.line + 1'u16
while llStreamReadLine(p.inp, p.x):
p.info.line = p.info.line + int16(1)
p.info.line = p.info.line + 1'u16
parseLine(p)
newLine(p)
result = p.outp

View file

@ -13,43 +13,44 @@ import
llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
renderer
proc invalidPragma(n: PNode) =
localError(n.info, errXNotAllowedHere, renderTree(n, {renderNoComments}))
proc invalidPragma(conf: ConfigRef; n: PNode) =
localError(conf, n.info,
"'$1' not allowed here" % renderTree(n, {renderNoComments}))
proc getArg(n: PNode, name: string, pos: int): PNode =
proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode =
result = nil
if n.kind in {nkEmpty..nkNilLit}: return
for i in countup(1, sonsLen(n) - 1):
if n.sons[i].kind == nkExprEqExpr:
if n.sons[i].sons[0].kind != nkIdent: invalidPragma(n)
if n.sons[i].sons[0].kind != nkIdent: invalidPragma(conf, n)
if cmpIgnoreStyle(n.sons[i].sons[0].ident.s, name) == 0:
return n.sons[i].sons[1]
elif i == pos:
return n.sons[i]
proc charArg*(n: PNode, name: string, pos: int, default: char): char =
var x = getArg(n, name, pos)
proc charArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: char): char =
var x = getArg(conf, n, name, pos)
if x == nil: result = default
elif x.kind == nkCharLit: result = chr(int(x.intVal))
else: invalidPragma(n)
else: invalidPragma(conf, n)
proc strArg*(n: PNode, name: string, pos: int, default: string): string =
var x = getArg(n, name, pos)
proc strArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: string): string =
var x = getArg(conf, n, name, pos)
if x == nil: result = default
elif x.kind in {nkStrLit..nkTripleStrLit}: result = x.strVal
else: invalidPragma(n)
else: invalidPragma(conf, n)
proc boolArg*(n: PNode, name: string, pos: int, default: bool): bool =
var x = getArg(n, name, pos)
proc boolArg*(conf: ConfigRef; n: PNode, name: string, pos: int, default: bool): bool =
var x = getArg(conf, n, name, pos)
if x == nil: result = default
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "true") == 0: result = true
elif x.kind == nkIdent and cmpIgnoreStyle(x.ident.s, "false") == 0: result = false
else: invalidPragma(n)
else: invalidPragma(conf, n)
proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var pattern = strArg(call, "startswith", 1, "")
var leading = boolArg(call, "leading", 2, true)
var trailing = boolArg(call, "trailing", 3, true)
proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNode): PLLStream =
var pattern = strArg(conf, call, "startswith", 1, "")
var leading = boolArg(conf, call, "leading", 2, true)
var trailing = boolArg(conf, call, "trailing", 3, true)
result = llStreamOpen("")
var line = newStringOfCap(80)
while llStreamReadLine(stdin, line):
@ -60,10 +61,10 @@ proc filterStrip*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
llStreamWriteln(result, line)
llStreamClose(stdin)
proc filterReplace*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
var sub = strArg(call, "sub", 1, "")
if len(sub) == 0: invalidPragma(call)
var by = strArg(call, "by", 2, "")
proc filterReplace*(conf: ConfigRef; stdin: PLLStream, filename: string, call: PNode): PLLStream =
var sub = strArg(conf, call, "sub", 1, "")
if len(sub) == 0: invalidPragma(conf, call)
var by = strArg(conf, call, "by", 2, "")
result = llStreamOpen("")
var line = newStringOfCap(80)
while llStreamReadLine(stdin, line):

View file

@ -9,7 +9,7 @@
## Module that implements ``gorge`` for the compiler.
import msgs, securehash, os, osproc, streams, strutils, options
import msgs, std / sha1, os, osproc, streams, strutils, options
proc readOutput(p: Process): (string, int) =
result[0] = ""
@ -21,11 +21,11 @@ proc readOutput(p: Process): (string, int) =
result[0].setLen(result[0].len - "\n".len)
result[1] = p.waitForExit
proc opGorge*(cmd, input, cache: string, info: TLineInfo): (string, int) =
proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
let workingDir = parentDir(info.toFullPath)
if cache.len > 0:# and optForceFullMake notin gGlobalOptions:
let h = secureHash(cmd & "\t" & input & "\t" & cache)
let filename = options.toGeneratedFile("gorge_" & $h, "txt")
let filename = options.toGeneratedFile(conf, "gorge_" & $h, "txt")
var f: File
if open(f, filename):
result = (f.readAll, 0)

View file

@ -10,7 +10,7 @@
## This module implements the 'implies' relation for guards.
import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents,
saturate
saturate, modulegraphs, options, configuration
const
someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc,
@ -83,18 +83,25 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
proc interestingCaseExpr*(m: PNode): bool = isLetLocation(m, true)
let
opLe = createMagic("<=", mLeI)
opLt = createMagic("<", mLtI)
opAnd = createMagic("and", mAnd)
opOr = createMagic("or", mOr)
opIsNil = createMagic("isnil", mIsNil)
opEq = createMagic("==", mEqI)
opAdd = createMagic("+", mAddI)
opSub = createMagic("-", mSubI)
opMul = createMagic("*", mMulI)
opDiv = createMagic("div", mDivI)
opLen = createMagic("len", mLengthSeq)
type
Operators* = object
opNot, opContains, opLe, opLt, opAnd, opOr, opIsNil, opEq: PSym
opAdd, opSub, opMul, opDiv, opLen: PSym
proc initOperators*(g: ModuleGraph): Operators =
result.opLe = createMagic(g, "<=", mLeI)
result.opLt = createMagic(g, "<", mLtI)
result.opAnd = createMagic(g, "and", mAnd)
result.opOr = createMagic(g, "or", mOr)
result.opIsNil = createMagic(g, "isnil", mIsNil)
result.opEq = createMagic(g, "==", mEqI)
result.opAdd = createMagic(g, "+", mAddI)
result.opSub = createMagic(g, "-", mSubI)
result.opMul = createMagic(g, "*", mMulI)
result.opDiv = createMagic(g, "div", mDivI)
result.opLen = createMagic(g, "len", mLengthSeq)
result.opNot = createMagic(g, "not", mNot)
result.opContains = createMagic(g, "contains", mInSet)
proc swapArgs(fact: PNode, newOp: PSym): PNode =
result = newNodeI(nkCall, fact.info, 3)
@ -102,16 +109,16 @@ proc swapArgs(fact: PNode, newOp: PSym): PNode =
result.sons[1] = fact.sons[2]
result.sons[2] = fact.sons[1]
proc neg(n: PNode): PNode =
proc neg(n: PNode; o: Operators): PNode =
if n == nil: return nil
case n.getMagic
of mNot:
result = n.sons[1]
of someLt:
# not (a < b) == a >= b == b <= a
result = swapArgs(n, opLe)
result = swapArgs(n, o.opLe)
of someLe:
result = swapArgs(n, opLt)
result = swapArgs(n, o.opLt)
of mInSet:
if n.sons[1].kind != nkCurly: return nil
let t = n.sons[2].typ.skipTypes(abstractInst)
@ -133,11 +140,11 @@ proc neg(n: PNode): PNode =
of mOr:
# not (a or b) --> not a and not b
let
a = n.sons[1].neg
b = n.sons[2].neg
a = n.sons[1].neg(o)
b = n.sons[2].neg(o)
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(opAnd)
result.sons[0] = newSymNode(o.opAnd)
result.sons[1] = a
result.sons[2] = b
elif a != nil:
@ -147,7 +154,7 @@ proc neg(n: PNode): PNode =
else:
# leave not (a == 4) as it is
result = newNodeI(nkCall, n.info, 2)
result.sons[0] = newSymNode(opNot)
result.sons[0] = newSymNode(o.opNot)
result.sons[1] = n
proc buildCall(op: PSym; a: PNode): PNode =
@ -181,7 +188,7 @@ proc `|div|`(a, b: PNode): PNode =
if a.kind in {nkCharLit..nkUInt64Lit}: result.intVal = a.intVal div b.intVal
else: result.floatVal = a.floatVal / b.floatVal
proc negate(a, b, res: PNode): PNode =
proc negate(a, b, res: PNode; o: Operators): PNode =
if b.kind in {nkCharLit..nkUInt64Lit} and b.intVal != low(BiggestInt):
var b = copyNode(b)
b.intVal = -b.intVal
@ -189,11 +196,11 @@ proc negate(a, b, res: PNode): PNode =
b.intVal = b.intVal |+| a.intVal
result = b
else:
result = buildCall(opAdd, a, b)
result = buildCall(o.opAdd, a, b)
elif b.kind in {nkFloatLit..nkFloat64Lit}:
var b = copyNode(b)
b.floatVal = -b.floatVal
result = buildCall(opAdd, a, b)
result = buildCall(o.opAdd, a, b)
else:
result = res
@ -205,7 +212,7 @@ proc lowBound*(x: PNode): PNode =
result = nkIntLit.newIntNode(firstOrd(x.typ))
result.info = x.info
proc highBound*(x: PNode): PNode =
proc highBound*(x: PNode; o: Operators): PNode =
let typ = x.typ.skipTypes(abstractInst)
result = if typ.kind == tyArray:
nkIntLit.newIntNode(lastOrd(typ))
@ -213,23 +220,23 @@ proc highBound*(x: PNode): PNode =
x.sym.kind == skConst:
nkIntLit.newIntNode(x.sym.ast.len-1)
else:
opAdd.buildCall(opLen.buildCall(x), minusOne())
o.opAdd.buildCall(o.opLen.buildCall(x), minusOne())
result.info = x.info
proc reassociation(n: PNode): PNode =
proc reassociation(n: PNode; o: Operators): PNode =
result = n
# (foo+5)+5 --> foo+10; same for '*'
case result.getMagic
of someAdd:
if result[2].isValue and
result[1].getMagic in someAdd and result[1][2].isValue:
result = opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
result = o.opAdd.buildCall(result[1][1], result[1][2] |+| result[2])
if result[2].intVal == 0:
result = result[1]
of someMul:
if result[2].isValue and
result[1].getMagic in someMul and result[1][2].isValue:
result = opMul.buildCall(result[1][1], result[1][2] |*| result[2])
result = o.opMul.buildCall(result[1][1], result[1][2] |*| result[2])
if result[2].intVal == 1:
result = result[1]
elif result[2].intVal == 0:
@ -243,12 +250,12 @@ proc pred(n: PNode): PNode =
else:
result = n
proc canon*(n: PNode): PNode =
proc canon*(n: PNode; o: Operators): PNode =
# XXX for now only the new code in 'semparallel' uses this
if n.safeLen >= 1:
result = shallowCopy(n)
for i in 0 ..< n.len:
result.sons[i] = canon(n.sons[i])
result.sons[i] = canon(n.sons[i], o)
elif n.kind == nkSym and n.sym.kind == skLet and
n.sym.ast.getMagic in (someEq + someAdd + someMul + someMin +
someMax + someHigh + {mUnaryLt} + someSub + someLen + someDiv):
@ -263,24 +270,24 @@ proc canon*(n: PNode): PNode =
# (4 + foo) + 2 --> (foo + 4) + 2
of someHigh:
# high == len+(-1)
result = opAdd.buildCall(opLen.buildCall(result[1]), minusOne())
result = o.opAdd.buildCall(o.opLen.buildCall(result[1]), minusOne())
of mUnaryLt:
result = buildCall(opAdd, result[1], minusOne())
result = buildCall(o.opAdd, result[1], minusOne())
of someSub:
# x - 4 --> x + (-4)
result = negate(result[1], result[2], result)
result = negate(result[1], result[2], result, o)
of someLen:
result.sons[0] = opLen.newSymNode
result.sons[0] = o.opLen.newSymNode
of someLt:
# x < y same as x <= y-1:
let y = n[2].canon
let y = n[2].canon(o)
let p = pred(y)
let minus = if p != y: p else: opAdd.buildCall(y, minusOne()).canon
result = opLe.buildCall(n[1].canon, minus)
let minus = if p != y: p else: o.opAdd.buildCall(y, minusOne()).canon(o)
result = o.opLe.buildCall(n[1].canon(o), minus)
else: discard
result = skipConv(result)
result = reassociation(result)
result = reassociation(result, o)
# most important rule: (x-4) <= a.len --> x <= a.len+4
case result.getMagic
of someLe:
@ -291,10 +298,10 @@ proc canon*(n: PNode): PNode =
case x.getMagic
of someSub:
result = buildCall(result[0].sym, x[1],
reassociation(opAdd.buildCall(y, x[2])))
reassociation(o.opAdd.buildCall(y, x[2]), o))
of someAdd:
# Rule A:
let plus = negate(y, x[2], nil).reassociation
let plus = negate(y, x[2], nil, o).reassociation(o)
if plus != nil: result = buildCall(result[0].sym, x[1], plus)
else: discard
elif y.kind in nkCallKinds and y.len == 3 and y[2].isValue and
@ -303,9 +310,9 @@ proc canon*(n: PNode): PNode =
case y.getMagic
of someSub:
result = buildCall(result[0].sym, y[1],
reassociation(opAdd.buildCall(x, y[2])))
reassociation(o.opAdd.buildCall(x, y[2]), o))
of someAdd:
let plus = negate(x, y[2], nil).reassociation
let plus = negate(x, y[2], nil, o).reassociation(o)
# ensure that Rule A will not trigger afterwards with the
# additional 'not isLetLocation' constraint:
if plus != nil and not isLetLocation(x, true):
@ -323,15 +330,15 @@ proc canon*(n: PNode): PNode =
result.sons[2] = y[1]
else: discard
proc `+@`*(a: PNode; b: BiggestInt): PNode =
canon(if b != 0: opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a)
proc buildAdd*(a: PNode; b: BiggestInt; o: Operators): PNode =
canon(if b != 0: o.opAdd.buildCall(a, nkIntLit.newIntNode(b)) else: a, o)
proc usefulFact(n: PNode): PNode =
proc usefulFact(n: PNode; o: Operators): PNode =
case n.getMagic
of someEq:
if skipConv(n.sons[2]).kind == nkNilLit and (
isLetLocation(n.sons[1], false) or isVar(n.sons[1])):
result = opIsNil.buildCall(n.sons[1])
result = o.opIsNil.buildCall(n.sons[1])
else:
if isLetLocation(n.sons[1], true) or isLetLocation(n.sons[2], true):
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
@ -351,11 +358,11 @@ proc usefulFact(n: PNode): PNode =
result = n
of mAnd:
let
a = usefulFact(n.sons[1])
b = usefulFact(n.sons[2])
a = usefulFact(n.sons[1], o)
b = usefulFact(n.sons[2], o)
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(opAnd)
result.sons[0] = newSymNode(o.opAnd)
result.sons[1] = a
result.sons[2] = b
elif a != nil:
@ -363,9 +370,9 @@ proc usefulFact(n: PNode): PNode =
elif b != nil:
result = b
of mNot:
let a = usefulFact(n.sons[1])
let a = usefulFact(n.sons[1], o)
if a != nil:
result = a.neg
result = a.neg(o)
of mOr:
# 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
# with that knowledge...
@ -374,14 +381,14 @@ proc usefulFact(n: PNode): PNode =
# (x == 3) or (y == 2) ---> not ( not (x==3) and not (y == 2))
# not (x != 3 and y != 2)
let
a = usefulFact(n.sons[1]).neg
b = usefulFact(n.sons[2]).neg
a = usefulFact(n.sons[1], o).neg(o)
b = usefulFact(n.sons[2], o).neg(o)
if a != nil and b != nil:
result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(opAnd)
result.sons[0] = newSymNode(o.opAnd)
result.sons[1] = a
result.sons[2] = b
result = result.neg
result = result.neg(o)
elif n.kind == nkSym and n.sym.kind == skLet:
# consider:
# let a = 2 < x
@ -389,32 +396,34 @@ proc usefulFact(n: PNode): PNode =
# ...
# We make can easily replace 'a' by '2 < x' here:
if n.sym.ast != nil:
result = usefulFact(n.sym.ast)
result = usefulFact(n.sym.ast, o)
elif n.kind == nkStmtListExpr:
result = usefulFact(n.lastSon)
result = usefulFact(n.lastSon, o)
type
TModel* = seq[PNode] # the "knowledge base"
TModel* = object
s*: seq[PNode] # the "knowledge base"
o*: Operators
proc addFact*(m: var TModel, nn: PNode) =
let n = usefulFact(nn)
if n != nil: m.add n
let n = usefulFact(nn, m.o)
if n != nil: m.s.add n
proc addFactNeg*(m: var TModel, n: PNode) =
let n = n.neg
let n = n.neg(m.o)
if n != nil: addFact(m, n)
proc canonOpr(opr: PSym): PSym =
case opr.magic
of someEq: result = opEq
of someLe: result = opLe
of someLt: result = opLt
of someLen: result = opLen
of someAdd: result = opAdd
of someSub: result = opSub
of someMul: result = opMul
of someDiv: result = opDiv
else: result = opr
proc sameOpr(a, b: PSym): bool =
case a.magic
of someEq: result = b.magic in someEq
of someLe: result = b.magic in someLe
of someLt: result = b.magic in someLt
of someLen: result = b.magic in someLen
of someAdd: result = b.magic in someAdd
of someSub: result = b.magic in someSub
of someMul: result = b.magic in someMul
of someDiv: result = b.magic in someDiv
else: result = a == b
proc sameTree*(a, b: PNode): bool =
result = false
@ -425,7 +434,7 @@ proc sameTree*(a, b: PNode): bool =
of nkSym:
result = a.sym == b.sym
if not result and a.sym.magic != mNone:
result = a.sym.magic == b.sym.magic or canonOpr(a.sym) == canonOpr(b.sym)
result = a.sym.magic == b.sym.magic or sameOpr(a.sym, b.sym)
of nkIdent: result = a.ident.id == b.ident.id
of nkCharLit..nkInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
@ -462,8 +471,8 @@ proc invalidateFacts*(m: var TModel, n: PNode) =
# The same mechanism could be used for more complex data stored on the heap;
# procs that 'write: []' cannot invalidate 'n.kind' for instance. In fact, we
# could CSE these expressions then and help C's optimizer.
for i in 0..high(m):
if m[i] != nil and m[i].hasSubTree(n): m[i] = nil
for i in 0..high(m.s):
if m.s[i] != nil and m.s[i].hasSubTree(n): m.s[i] = nil
proc valuesUnequal(a, b: PNode): bool =
if a.isValue and b.isValue:
@ -486,7 +495,7 @@ proc impliesEq(fact, eq: PNode): TImplication =
if sameTree(fact.sons[2], eq.sons[loc]) and isValue(eq.sons[val]):
if inSet(fact.sons[1], eq.sons[val]): result = impYes
else: result = impNo
of mNot, mOr, mAnd: internalError(eq.info, "impliesEq")
of mNot, mOr, mAnd: assert(false, "impliesEq")
else: discard
proc leImpliesIn(x, c, aSet: PNode): TImplication =
@ -549,7 +558,7 @@ proc impliesIn(fact, loc, aSet: PNode): TImplication =
elif sameTree(fact.sons[2], loc):
# 4 < x --> 3 <= x
result = geImpliesIn(fact.sons[2], fact.sons[1].pred, aSet)
of mNot, mOr, mAnd: internalError(loc.info, "impliesIn")
of mNot, mOr, mAnd: assert(false, "impliesIn")
else: discard
proc valueIsNil(n: PNode): TImplication =
@ -567,11 +576,11 @@ proc impliesIsNil(fact, eq: PNode): TImplication =
result = valueIsNil(fact.sons[2].skipConv)
elif sameTree(fact.sons[2], eq.sons[1]):
result = valueIsNil(fact.sons[1].skipConv)
of mNot, mOr, mAnd: internalError(eq.info, "impliesIsNil")
of mNot, mOr, mAnd: assert(false, "impliesIsNil")
else: discard
proc impliesGe(fact, x, c: PNode): TImplication =
internalAssert isLocation(x)
assert isLocation(x)
case fact.sons[0].sym.magic
of someEq:
if sameTree(fact.sons[1], x):
@ -603,7 +612,7 @@ proc impliesGe(fact, x, c: PNode): TImplication =
# fact: 3 <= x; question: x >= 2 ? --> true iff 2 <= 3
if isValue(fact.sons[1]) and isValue(c):
if leValue(c, fact.sons[1]): result = impYes
of mNot, mOr, mAnd: internalError(x.info, "impliesGe")
of mNot, mOr, mAnd: assert(false, "impliesGe")
else: discard
proc impliesLe(fact, x, c: PNode): TImplication =
@ -643,7 +652,7 @@ proc impliesLe(fact, x, c: PNode): TImplication =
if isValue(fact.sons[1]) and isValue(c):
if leValue(c, fact.sons[1].pred): result = impNo
of mNot, mOr, mAnd: internalError(x.info, "impliesLe")
of mNot, mOr, mAnd: assert(false, "impliesLe")
else: discard
proc impliesLt(fact, x, c: PNode): TImplication =
@ -707,14 +716,14 @@ proc factImplies(fact, prop: PNode): TImplication =
proc doesImply*(facts: TModel, prop: PNode): TImplication =
assert prop.kind in nkCallKinds
for f in facts:
for f in facts.s:
# facts can be invalidated, in which case they are 'nil':
if not f.isNil:
result = f.factImplies(prop)
if result != impUnknown: return
proc impliesNotNil*(facts: TModel, arg: PNode): TImplication =
result = doesImply(facts, opIsNil.buildCall(arg).neg)
proc impliesNotNil*(m: TModel, arg: PNode): TImplication =
result = doesImply(m, m.o.opIsNil.buildCall(arg).neg(m.o))
proc simpleSlice*(a, b: PNode): BiggestInt =
# returns 'c' if a..b matches (i+c)..(i+c), -1 otherwise. (i)..(i) is matched
@ -768,8 +777,10 @@ macro `=~`(x: PNode, pat: untyped): bool =
var conds = newTree(nnkBracket)
m(x, pat, conds)
when declared(macros.toNimIdent):
result = nestList(toNimIdent"and", conds)
when compiles(nestList(ident"and", conds)):
result = nestList(ident"and", conds)
#elif declared(macros.toNimIdent):
# result = nestList(toNimIdent"and", conds)
else:
result = nestList(!"and", conds)
@ -815,20 +826,20 @@ proc ple(m: TModel; a, b: PNode): TImplication =
if a.getMagic in someMul and a[2].isValue and a[1].getMagic in someDiv and
a[1][2].isValue:
# simplify (x div 4) * 2 <= y to x div (c div d) <= y
if ple(m, buildCall(opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
if ple(m, buildCall(m.o.opDiv, a[1][1], `|div|`(a[1][2], a[2])), b) == impYes:
return impYes
# x*3 + x == x*4. It follows that:
# x*3 + y <= x*4 if y <= x and 3 <= 4
if a =~ x*dc + y and b =~ x2*ec:
if sameTree(x, x2):
let ec1 = opAdd.buildCall(ec, minusOne())
let ec1 = m.o.opAdd.buildCall(ec, minusOne())
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? x:
return impYes
elif a =~ x*dc and b =~ x2*ec + y:
#echo "BUG cam ehrer e ", a, " <=? ", b
if sameTree(x, x2):
let ec1 = opAdd.buildCall(ec, minusOne())
let ec1 = m.o.opAdd.buildCall(ec, minusOne())
if x >=? 1 and ec >=? 1 and dc >=? 1 and dc <=? ec1 and y <=? zero():
return impYes
@ -861,9 +872,9 @@ proc ple(m: TModel; a, b: PNode): TImplication =
# use the knowledge base:
return pleViaModel(m, a, b)
#return doesImply(m, opLe.buildCall(a, b))
#return doesImply(m, o.opLe.buildCall(a, b))
type TReplacements = seq[tuple[a,b: PNode]]
type TReplacements = seq[tuple[a, b: PNode]]
proc replaceSubTree(n, x, by: PNode): PNode =
if sameTree(n, x):
@ -881,11 +892,11 @@ proc applyReplacements(n: PNode; rep: TReplacements): PNode =
proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
# now check for inferrable facts: a <= b and b <= c implies a <= c
for i in 0..m.high:
let fact = m[i]
for i in 0..m.s.high:
let fact = m.s[i]
if fact != nil and fact.getMagic in someLe:
# mark as used:
m[i] = nil
m.s[i] = nil
# i <= len-100
# i <=? len-1
# --> true if (len-100) <= (len-1)
@ -917,7 +928,7 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication =
proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
# compute replacements:
var replacements: TReplacements = @[]
for fact in model:
for fact in model.s:
if fact != nil and fact.getMagic in someEq:
let a = fact[1]
let b = fact[2]
@ -927,12 +938,13 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
var a = aa
var b = bb
if replacements.len > 0:
m = @[]
m.s = @[]
m.o = model.o
# make the other facts consistent:
for fact in model:
for fact in model.s:
if fact != nil and fact.getMagic notin someEq:
# XXX 'canon' should not be necessary here, but it is
m.add applyReplacements(fact, replacements).canon
m.s.add applyReplacements(fact, replacements).canon(m.o)
a = applyReplacements(aa, replacements)
b = applyReplacements(bb, replacements)
else:
@ -941,31 +953,31 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
result = pleViaModelRec(m, a, b)
proc proveLe*(m: TModel; a, b: PNode): TImplication =
let x = canon(opLe.buildCall(a, b))
let x = canon(m.o.opLe.buildCall(a, b), m.o)
#echo "ROOT ", renderTree(x[1]), " <=? ", renderTree(x[2])
result = ple(m, x[1], x[2])
if result == impUnknown:
# try an alternative: a <= b iff not (b < a) iff not (b+1 <= a):
let y = canon(opLe.buildCall(opAdd.buildCall(b, one()), a))
let y = canon(m.o.opLe.buildCall(m.o.opAdd.buildCall(b, one()), a), m.o)
result = ~ple(m, y[1], y[2])
proc addFactLe*(m: var TModel; a, b: PNode) =
m.add canon(opLe.buildCall(a, b))
m.s.add canon(m.o.opLe.buildCall(a, b), m.o)
proc settype(n: PNode): PType =
result = newType(tySet, n.typ.owner)
addSonSkipIntLit(result, n.typ)
proc buildOf(it, loc: PNode): PNode =
proc buildOf(it, loc: PNode; o: Operators): PNode =
var s = newNodeI(nkCurly, it.info, it.len-1)
s.typ = settype(loc)
for i in 0..it.len-2: s.sons[i] = it.sons[i]
result = newNodeI(nkCall, it.info, 3)
result.sons[0] = newSymNode(opContains)
result.sons[0] = newSymNode(o.opContains)
result.sons[1] = s
result.sons[2] = loc
proc buildElse(n: PNode): PNode =
proc buildElse(n: PNode; o: Operators): PNode =
var s = newNodeIT(nkCurly, n.info, settype(n.sons[0]))
for i in 1..n.len-2:
let branch = n.sons[i]
@ -973,23 +985,23 @@ proc buildElse(n: PNode): PNode =
for j in 0..branch.len-2:
s.add(branch.sons[j])
result = newNodeI(nkCall, n.info, 3)
result.sons[0] = newSymNode(opContains)
result.sons[0] = newSymNode(o.opContains)
result.sons[1] = s
result.sons[2] = n.sons[0]
proc addDiscriminantFact*(m: var TModel, n: PNode) =
var fact = newNodeI(nkCall, n.info, 3)
fact.sons[0] = newSymNode(opEq)
fact.sons[0] = newSymNode(m.o.opEq)
fact.sons[1] = n.sons[0]
fact.sons[2] = n.sons[1]
m.add fact
m.s.add fact
proc addAsgnFact*(m: var TModel, key, value: PNode) =
var fact = newNodeI(nkCall, key.info, 3)
fact.sons[0] = newSymNode(opEq)
fact.sons[0] = newSymNode(m.o.opEq)
fact.sons[1] = key
fact.sons[2] = value
m.add fact
m.s.add fact
proc sameSubexprs*(m: TModel; a, b: PNode): bool =
# This should be used to check whether two *path expressions* refer to the
@ -1002,7 +1014,7 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
# However, nil checking requires exactly the same mechanism! But for now
# we simply use sameTree and live with the unsoundness of the analysis.
var check = newNodeI(nkCall, a.info, 3)
check.sons[0] = newSymNode(opEq)
check.sons[0] = newSymNode(m.o.opEq)
check.sons[1] = a
check.sons[2] = b
result = m.doesImply(check) == impYes
@ -1010,11 +1022,11 @@ proc sameSubexprs*(m: TModel; a, b: PNode): bool =
proc addCaseBranchFacts*(m: var TModel, n: PNode, i: int) =
let branch = n.sons[i]
if branch.kind == nkOfBranch:
m.add buildOf(branch, n.sons[0])
m.s.add buildOf(branch, n.sons[0], m.o)
else:
m.add n.buildElse.neg
m.s.add n.buildElse(m.o).neg(m.o)
proc buildProperFieldCheck(access, check: PNode): PNode =
proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
if check.sons[1].kind == nkCurly:
result = copyTree(check)
if access.kind == nkDotExpr:
@ -1026,10 +1038,10 @@ proc buildProperFieldCheck(access, check: PNode): PNode =
else:
# it is some 'not'
assert check.getMagic == mNot
result = buildProperFieldCheck(access, check.sons[1]).neg
result = buildProperFieldCheck(access, check.sons[1], o).neg(o)
proc checkFieldAccess*(m: TModel, n: PNode) =
proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) =
for i in 1..n.len-1:
let check = buildProperFieldCheck(n.sons[0], n.sons[i])
let check = buildProperFieldCheck(n.sons[0], n.sons[i], m.o)
if check != nil and m.doesImply(check) != impYes:
message(n.info, warnProveField, renderTree(n.sons[0])); break
message(conf, n.info, warnProveField, renderTree(n.sons[0])); break

View file

@ -12,12 +12,12 @@
proc hlo(c: PContext, n: PNode): PNode
proc evalPattern(c: PContext, n, orig: PNode): PNode =
internalAssert n.kind == nkCall and n.sons[0].kind == nkSym
internalAssert c.config, n.kind == nkCall and n.sons[0].kind == nkSym
# we need to ensure that the resulting AST is semchecked. However, it's
# aweful to semcheck before macro invocation, so we don't and treat
# templates and macros as immediate in this context.
var rule: string
if optHints in gOptions and hintPattern in gNotes:
if optHints in c.config.options and hintPattern in c.config.notes:
rule = renderTree(n, {renderNoComments})
let s = n.sons[0].sym
case s.kind
@ -27,8 +27,8 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
result = semTemplateExpr(c, n, s, {efFromHlo})
else:
result = semDirectOp(c, n, {})
if optHints in gOptions and hintPattern in gNotes:
message(orig.info, hintPattern, rule & " --> '" &
if optHints in c.config.options and hintPattern in c.config.notes:
message(c.config, orig.info, hintPattern, rule & " --> '" &
renderTree(result, {renderNoComments}) & "'")
proc applyPatterns(c: PContext, n: PNode): PNode =
@ -44,8 +44,8 @@ proc applyPatterns(c: PContext, n: PNode): PNode =
assert x.kind in {nkStmtList, nkCall}
# better be safe than sorry, so check evalTemplateCounter too:
inc(evalTemplateCounter)
if evalTemplateCounter > 100:
globalError(n.info, errTemplateInstantiationTooNested)
if evalTemplateCounter > evalTemplateLimit:
globalError(c.config, n.info, "template instantiation too nested")
# deactivate this pattern:
c.patterns[i] = nil
if x.kind == nkStmtList:
@ -86,18 +86,18 @@ proc hlo(c: PContext, n: PNode): PNode =
else:
result = fitNode(c, n.typ, result, n.info)
# optimization has been applied so check again:
result = commonOptimizations(c.module, result)
result = commonOptimizations(c.graph, c.module, result)
result = hlo(c, result)
result = commonOptimizations(c.module, result)
result = commonOptimizations(c.graph, c.module, result)
proc hloBody(c: PContext, n: PNode): PNode =
# fast exit:
if c.patterns.len == 0 or optPatterns notin gOptions: return n
if c.patterns.len == 0 or optPatterns notin c.config.options: return n
c.hloLoopDetector = 0
result = hlo(c, n)
proc hloStmt(c: PContext, n: PNode): PNode =
# fast exit:
if c.patterns.len == 0 or optPatterns notin gOptions: return n
if c.patterns.len == 0 or optPatterns notin c.config.options: return n
c.hloLoopDetector = 0
result = hlo(c, n)

View file

@ -37,7 +37,7 @@ proc resetIdentCache*() =
for i in low(legacy.buckets)..high(legacy.buckets):
legacy.buckets[i] = nil
proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
proc cmpIgnoreStyle*(a, b: cstring, blen: int): int =
if a[0] != b[0]: return 1
var i = 0
var j = 0

View file

@ -36,20 +36,20 @@ proc setId*(id: int) {.inline.} =
proc idSynchronizationPoint*(idRange: int) =
gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1
proc toGid(f: string): string =
proc toGid(conf: ConfigRef; f: string): string =
# we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this
# will cause strange bugs if multiple projects are in the same folder, so
# we simply use a project independent name:
result = options.completeGeneratedFilePath("nim.gid")
result = options.completeGeneratedFilePath(conf, "nim.gid")
proc saveMaxIds*(project: string) =
var f = open(project.toGid, fmWrite)
proc saveMaxIds*(conf: ConfigRef; project: string) =
var f = open(toGid(conf, project), fmWrite)
f.writeLine($gFrontEndId)
f.close()
proc loadMaxIds*(project: string) =
proc loadMaxIds*(conf: ConfigRef; project: string) =
var f: File
if open(f, project.toGid, fmRead):
if open(f, toGid(conf, project), fmRead):
var line = newStringOfCap(20)
if f.readLine(line):
var frontEndId = parseInt(line)

View file

@ -11,11 +11,18 @@
import
intsets, strutils, os, ast, astalgo, msgs, options, idents, rodread, lookups,
semdata, passes, renderer, modulepaths
semdata, passes, renderer, modulepaths, sigmatch, configuration
proc evalImport*(c: PContext, n: PNode): PNode
proc evalFrom*(c: PContext, n: PNode): PNode
proc readExceptSet*(c: PContext, n: PNode): IntSet =
assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
result = initIntSet()
for i in 1 ..< n.len:
let ident = lookups.considerQuotedIdent(c.config, n[i])
result.incl(ident.id)
proc importPureEnumField*(c: PContext; s: PSym) =
var check = strTableGet(c.importTable.symbols, s.name)
if check == nil:
@ -40,7 +47,7 @@ proc rawImportSymbol(c: PContext, s: PSym) =
for j in countup(0, sonsLen(etyp.n) - 1):
var e = etyp.n.sons[j].sym
if e.kind != skEnumField:
internalError(s.info, "rawImportSymbol")
internalError(c.config, s.info, "rawImportSymbol")
# BUGFIX: because of aliases for enums the symbol may already
# have been put into the symbol table
# BUGFIX: but only iff they are the same symbols!
@ -62,14 +69,14 @@ proc rawImportSymbol(c: PContext, s: PSym) =
if hasPattern(s): addPattern(c, s)
proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
let ident = lookups.considerQuotedIdent(n)
let ident = lookups.considerQuotedIdent(c.config, n)
let s = strTableGet(fromMod.tab, ident)
if s == nil:
errorUndeclaredIdentifier(c, n.info, ident.s)
else:
if s.kind == skStub: loadStub(s)
if s.kind notin ExportableSymKinds:
internalError(n.info, "importSymbol: 2")
internalError(c.config, n.info, "importSymbol: 2")
# for an enumeration we have to add all identifiers
case s.kind
of skProcKinds:
@ -77,7 +84,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
var it: TIdentIter
var e = initIdentIter(it, fromMod.tab, s.name)
while e != nil:
if e.name.id != s.name.id: internalError(n.info, "importSymbol: 3")
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
rawImportSymbol(c, e)
e = nextIdentIter(it, fromMod.tab)
else: rawImportSymbol(c, s)
@ -89,7 +96,7 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
if s.kind != skModule:
if s.kind != skEnumField:
if s.kind notin ExportableSymKinds:
internalError(s.info, "importAllSymbols: " & $s.kind)
internalError(c.config, s.info, "importAllSymbols: " & $s.kind)
if exceptSet.isNil or s.name.id notin exceptSet:
rawImportSymbol(c, s)
s = nextIter(i, fromMod.tab)
@ -110,22 +117,23 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet) =
elif exceptSet.isNil or s.name.id notin exceptSet:
rawImportSymbol(c, s)
of nkExportExceptStmt:
localError(n.info, errGenerated, "'export except' not implemented")
localError(c.config, n.info, "'export except' not implemented")
else:
for i in 0..safeLen(n)-1:
importForwarded(c, n.sons[i], exceptSet)
proc importModuleAs(n: PNode, realModule: PSym): PSym =
proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym =
result = realModule
if n.kind != nkImportAs: discard
elif n.len != 2 or n.sons[1].kind != nkIdent:
localError(n.info, errGenerated, "module alias must be an identifier")
localError(c.config, n.info, "module alias must be an identifier")
elif n.sons[1].ident.id != realModule.name.id:
# some misguided guy will write 'import abc.foo as foo' ...
result = createModuleAlias(realModule, n.sons[1].ident, realModule.info)
result = createModuleAlias(realModule, n.sons[1].ident, realModule.info,
c.config.options)
proc myImportModule(c: PContext, n: PNode): PSym =
var f = checkModuleName(n)
var f = checkModuleName(c.config, n)
if f != InvalidFileIDX:
let L = c.graph.importStack.len
let recursion = c.graph.importStack.find(f)
@ -138,7 +146,7 @@ proc myImportModule(c: PContext, n: PNode): PSym =
err.add toFullPath(c.graph.importStack[i]) & " imports " &
toFullPath(c.graph.importStack[i+1])
c.recursiveDep = err
result = importModuleAs(n, gImportModule(c.graph, c.module, f, c.cache))
result = importModuleAs(c, n, gImportModule(c.graph, c.module, f, c.cache))
#echo "set back to ", L
c.graph.importStack.setLen(L)
# we cannot perform this check reliably because of
@ -146,10 +154,13 @@ proc myImportModule(c: PContext, n: PNode): PSym =
when true:
if result.info.fileIndex == c.module.info.fileIndex and
result.info.fileIndex == n.info.fileIndex:
localError(n.info, errGenerated, "A module cannot import itself")
localError(c.config, n.info, "A module cannot import itself")
if sfDeprecated in result.flags:
message(n.info, warnDeprecated, result.name.s)
#suggestSym(n.info, result, false)
if result.constraint != nil:
message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s)
else:
message(c.config, n.info, warnDeprecated, result.name.s)
suggestSym(c.config, n.info, result, c.graph.usageSym, false)
proc impMod(c: PContext; it: PNode) =
let m = myImportModule(c, it)
@ -179,7 +190,7 @@ proc evalImport(c: PContext, n: PNode): PNode =
proc evalFrom(c: PContext, n: PNode): PNode =
result = n
checkMinSonsLen(n, 2)
checkMinSonsLen(n, 2, c.config)
var m = myImportModule(c, n.sons[0])
if m != nil:
n.sons[0] = newSymNode(m)
@ -190,14 +201,10 @@ proc evalFrom(c: PContext, n: PNode): PNode =
proc evalImportExcept*(c: PContext, n: PNode): PNode =
result = n
checkMinSonsLen(n, 2)
checkMinSonsLen(n, 2, c.config)
var m = myImportModule(c, n.sons[0])
if m != nil:
n.sons[0] = newSymNode(m)
addDecl(c, m, n.info) # add symbol to symbol table of module
var exceptSet = initIntSet()
for i in countup(1, sonsLen(n) - 1):
let ident = lookups.considerQuotedIdent(n.sons[i])
exceptSet.incl(ident.id)
importAllSymbolsExcept(c, m, exceptSet)
importAllSymbolsExcept(c, m, readExceptSet(c, n))
#importForwarded(c, m.ast, exceptSet)

View file

@ -6,7 +6,7 @@ Name: "Nim"
Version: "$version"
Platforms: """
windows: i386;amd64
linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64
linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv64
macosx: i386;amd64;powerpc64
solaris: i386;amd64;sparc;sparc64
freebsd: i386;amd64

File diff suppressed because it is too large Load diff

View file

@ -34,11 +34,11 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
s = genTypeInfo(p, field.typ)
result = ("{kind: 1, offset: \"$1\", len: 0, " &
"typ: $2, name: $3, sons: null}") %
[mangleName(field, p.target), s,
[mangleName(p.module, field), s,
makeJSString(field.name.s)]
of nkRecCase:
length = sonsLen(n)
if (n.sons[0].kind != nkSym): internalError(n.info, "genObjectFields")
if (n.sons[0].kind != nkSym): internalError(p.config, n.info, "genObjectFields")
field = n.sons[0].sym
s = genTypeInfo(p, field.typ)
for i in countup(1, length - 1):
@ -47,7 +47,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
case b.kind
of nkOfBranch:
if sonsLen(b) < 2:
internalError(b.info, "genObjectFields; nkOfBranch broken")
internalError(p.config, b.info, "genObjectFields; nkOfBranch broken")
for j in countup(0, sonsLen(b) - 2):
if u != nil: add(u, ", ")
if b.sons[j].kind == nkRange:
@ -57,15 +57,15 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
add(u, rope(getOrdValue(b.sons[j])))
of nkElse:
u = rope(lengthOrd(field.typ))
else: internalError(n.info, "genObjectFields(nkRecCase)")
else: internalError(p.config, n.info, "genObjectFields(nkRecCase)")
if result != nil: add(result, ", " & tnl)
addf(result, "[setConstr($1), $2]",
[u, genObjectFields(p, typ, lastSon(b))])
result = ("{kind: 3, offset: \"$1\", len: $3, " &
"typ: $2, name: $4, sons: [$5]}") % [
mangleName(field, p.target), s,
mangleName(p.module, field), s,
rope(lengthOrd(field.typ)), makeJSString(field.name.s), result]
else: internalError(n.info, "genObjectFields")
else: internalError(p.config, n.info, "genObjectFields")
proc objHasTypeField(t: PType): bool {.inline.} =
tfInheritable in t.flags or t.sons[0] != nil
@ -104,7 +104,7 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
let length = sonsLen(typ.n)
var s: Rope = nil
for i in countup(0, length - 1):
if (typ.n.sons[i].kind != nkSym): internalError(typ.n.info, "genEnumInfo")
if (typ.n.sons[i].kind != nkSym): internalError(p.config, typ.n.info, "genEnumInfo")
let field = typ.n.sons[i].sym
if i > 0: add(s, ", " & tnl)
let extName = if field.ast == nil: field.name.s else: field.ast.strVal
@ -121,27 +121,8 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
addf(p.g.typeInfo, "$1.base = $2;$n",
[name, genTypeInfo(p, typ.sons[0])])
proc genEnumInfoPHP(p: PProc; t: PType): Rope =
let t = t.skipTypes({tyGenericInst, tyDistinct, tyAlias})
result = "$$NTI$1" % [rope(t.id)]
p.declareGlobal(t.id, result)
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
let length = sonsLen(t.n)
var s: Rope = nil
for i in countup(0, length - 1):
if (t.n.sons[i].kind != nkSym): internalError(t.n.info, "genEnumInfo")
let field = t.n.sons[i].sym
if i > 0: add(s, ", " & tnl)
let extName = if field.ast == nil: field.name.s else: field.ast.strVal
addf(s, "$# => $#$n",
[rope(field.position), makeJSString(extName)])
prepend(p.g.typeInfo, "$$$# = $#;$n" % [result, s])
proc genTypeInfo(p: PProc, typ: PType): Rope =
if p.target == targetPHP:
return makeJSString(typeToString(typ, preferModuleInfo))
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias})
let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink})
result = "NTI$1" % [rope(t.id)]
if containsOrIncl(p.g.typeInfoGenerated, t.id): return
case t.kind
@ -152,7 +133,7 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))]
prepend(p.g.typeInfo, s)
of tyVar, tyRef, tyPtr, tySequence, tyRange, tySet:
of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet:
var s =
"var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
[result, rope(ord(t.kind))]
@ -171,5 +152,5 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
of tyTuple: genTupleInfo(p, t, result)
of tyStatic:
if t.n != nil: result = genTypeInfo(p, lastSon t)
else: internalError("genTypeInfo(" & $t.kind & ')')
else: internalError("genTypeInfo(" & $t.kind & ')')
else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')
else: internalError(p.config, "genTypeInfo(" & $t.kind & ')')

View file

@ -7,11 +7,11 @@
# distribution, for details about the copyright.
#
# This include file implements lambda lifting for the transformator.
# This file implements lambda lifting for the transformator.
import
intsets, strutils, options, ast, astalgo, trees, treetab, msgs, os,
idents, renderer, types, magicsys, rodread, lowerings, tables
intsets, strutils, options, ast, astalgo, trees, treetab, msgs,
idents, renderer, types, magicsys, rodread, lowerings, tables, modulegraphs
discard """
The basic approach is that captured vars need to be put on the heap and
@ -125,32 +125,32 @@ proc newCall(a: PSym, b: PNode): PNode =
result.add newSymNode(a)
result.add b
proc createStateType(iter: PSym): PType =
proc createClosureIterStateType*(g: ModuleGraph; iter: PSym): PType =
var n = newNodeI(nkRange, iter.info)
addSon(n, newIntNode(nkIntLit, -1))
addSon(n, newIntNode(nkIntLit, 0))
result = newType(tyRange, iter)
result.n = n
var intType = nilOrSysInt()
var intType = nilOrSysInt(g)
if intType.isNil: intType = newType(tyInt, iter)
rawAddSon(result, intType)
proc createStateField(iter: PSym): PSym =
proc createStateField(g: ModuleGraph; iter: PSym): PSym =
result = newSym(skField, getIdent(":state"), iter, iter.info)
result.typ = createStateType(iter)
result.typ = createClosureIterStateType(g, iter)
proc createEnvObj(owner: PSym; info: TLineInfo): PType =
proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType =
# YYY meh, just add the state field for every closure for now, it's too
# hard to figure out if it comes from a closure iterator:
result = createObj(owner, info, final=false)
rawAddField(result, createStateField(owner))
result = createObj(g, owner, info, final=false)
rawAddField(result, createStateField(g, owner))
proc getIterResult(iter: PSym): PSym =
proc getClosureIterResult*(iter: PSym): PSym =
if resultPos < iter.ast.len:
result = iter.ast.sons[resultPos].sym
else:
# XXX a bit hacky:
result = newSym(skResult, getIdent":result", iter, iter.info)
result = newSym(skResult, getIdent":result", iter, iter.info, {})
result.typ = iter.typ.sons[0]
incl(result.flags, sfUsed)
iter.ast.add newSymNode(result)
@ -166,7 +166,7 @@ proc addHiddenParam(routine: PSym, param: PSym) =
assert sfFromGeneric in param.flags
#echo "produced environment: ", param.id, " for ", routine.id
proc getHiddenParam(routine: PSym): PSym =
proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
let params = routine.ast.sons[paramsPos]
let hidden = lastSon(params)
if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
@ -174,7 +174,7 @@ proc getHiddenParam(routine: PSym): PSym =
assert sfFromGeneric in result.flags
else:
# writeStackTrace()
localError(routine.info, "internal error: could not find env param for " & routine.name.s)
localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
result = routine
proc getEnvParam*(routine: PSym): PSym =
@ -186,11 +186,12 @@ proc getEnvParam*(routine: PSym): PSym =
proc interestingVar(s: PSym): bool {.inline.} =
result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
sfGlobal notin s.flags
sfGlobal notin s.flags and
s.typ.kind notin {tyStatic, tyTypeDesc}
proc illegalCapture(s: PSym): bool {.inline.} =
result = skipTypes(s.typ, abstractInst).kind in
{tyVar, tyOpenArray, tyVarargs} or
{tyVar, tyOpenArray, tyVarargs, tyLent} or
s.kind == skResult
proc isInnerProc(s: PSym): bool =
@ -207,14 +208,14 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
result.sons[0] = le
result.sons[1] = ri
proc makeClosure*(prc: PSym; env: PNode; info: TLineInfo): PNode =
proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode =
result = newNodeIT(nkClosure, info, prc.typ)
result.add(newSymNode(prc))
if env == nil:
result.add(newNodeIT(nkNilLit, info, getSysType(tyNil)))
result.add(newNodeIT(nkNilLit, info, getSysType(g, info, tyNil)))
else:
if env.skipConv.kind == nkClosure:
localError(info, "internal error: taking closure of closure")
localError(g.config, info, "internal error: taking closure of closure")
result.add(env)
proc interestingIterVar(s: PSym): bool {.inline.} =
@ -226,23 +227,23 @@ proc interestingIterVar(s: PSym): bool {.inline.} =
template isIterator*(owner: PSym): bool =
owner.kind == skIterator and owner.typ.callConv == ccClosure
proc liftingHarmful(owner: PSym): bool {.inline.} =
proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
## lambda lifting can be harmful for JS-like code generators.
let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
result = gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime
result = conf.cmd == cmdCompileToJS and not isCompileTime
proc liftIterSym*(n: PNode; owner: PSym): PNode =
proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
# transforms (iter) to (let env = newClosure[iter](); (iter, env))
if liftingHarmful(owner): return n
if liftingHarmful(g.config, owner): return n
let iter = n.sym
assert iter.isIterator
result = newNodeIT(nkStmtListExpr, n.info, n.typ)
let hp = getHiddenParam(iter)
let hp = getHiddenParam(g, iter)
var env: PNode
if owner.isIterator:
let it = getHiddenParam(owner)
let it = getHiddenParam(g, owner)
addUniqueField(it.typ.sons[0], hp)
env = indirectAccess(newSymNode(it), hp, hp.info)
else:
@ -254,11 +255,11 @@ proc liftIterSym*(n: PNode; owner: PSym): PNode =
addVar(v, env)
result.add(v)
# add 'new' statement:
result.add newCall(getSysSym"internalNew", env)
result.add makeClosure(iter, env, n.info)
result.add newCall(getSysSym(g, n.info, "internalNew"), env)
result.add makeClosure(g, iter, env, n.info)
proc freshVarForClosureIter*(s, owner: PSym): PNode =
let envParam = getHiddenParam(owner)
proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode =
let envParam = getHiddenParam(g, owner)
let obj = envParam.typ.lastSon
addField(obj, s)
@ -268,15 +269,19 @@ proc freshVarForClosureIter*(s, owner: PSym): PNode =
if field != nil:
result = rawIndirectAccess(access, field, s.info)
else:
localError(s.info, "internal error: cannot generate fresh variable")
localError(g.config, s.info, "internal error: cannot generate fresh variable")
result = access
# ------------------ new stuff -------------------------------------------
proc markAsClosure(owner: PSym; n: PNode) =
proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
let s = n.sym
if illegalCapture(s) or owner.typ.callConv notin {ccClosure, ccDefault}:
localError(n.info, errIllegalCaptureX, s.name.s)
if illegalCapture(s):
localError(g.config, n.info, "illegal capture '$1' of type <$2> which is declared here: $3" %
[s.name.s, typeToString(s.typ), $s.info])
elif owner.typ.callConv notin {ccClosure, ccDefault}:
localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
[s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]])
incl(owner.typ.flags, tfCapturesEnv)
owner.typ.callConv = ccClosure
@ -285,12 +290,14 @@ type
processed, capturedVars: IntSet
ownerToType: Table[int, PType]
somethingToDo: bool
graph: ModuleGraph
proc initDetectionPass(fn: PSym): DetectionPass =
proc initDetectionPass(g: ModuleGraph; fn: PSym): DetectionPass =
result.processed = initIntSet()
result.capturedVars = initIntSet()
result.ownerToType = initTable[int, PType]()
result.processed.incl(fn.id)
result.graph = g
discard """
proc outer =
@ -307,7 +314,7 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
result = c.ownerToType.getOrDefault(owner.id)
if result.isNil:
result = newType(tyRef, owner)
let obj = createEnvObj(owner, info)
let obj = createEnvObj(c.graph, owner, info)
rawAddSon(result, obj)
c.ownerToType[owner.id] = result
@ -316,13 +323,13 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
let obj = refObj.lastSon
let fieldType = c.getEnvTypeForOwner(dep, info) #getHiddenParam(dep).typ
if refObj == fieldType:
localError(dep.info, "internal error: invalid up reference computed")
localError(c.graph.config, dep.info, "internal error: invalid up reference computed")
let upIdent = getIdent(upName)
let upField = lookupInRecord(obj.n, upIdent)
if upField != nil:
if upField.typ != fieldType:
localError(dep.info, "internal error: up references do not agree")
localError(c.graph.config, dep.info, "internal error: up references do not agree")
else:
let result = newSym(skField, upIdent, obj.owner, obj.owner.info)
result.typ = fieldType
@ -364,7 +371,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
cp.typ = t
addHiddenParam(fn, cp)
elif cp.typ != t and fn.kind != skIterator:
localError(fn.info, "internal error: inconsistent environment type")
localError(c.graph.config, fn.info, "internal error: inconsistent environment type")
#echo "adding closure to ", fn.name.s
proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
@ -390,9 +397,13 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
addClosureParam(c, owner, n.info)
if interestingIterVar(s):
if not c.capturedVars.containsOrIncl(s.id):
let obj = getHiddenParam(owner).typ.lastSon
let obj = getHiddenParam(c.graph, owner).typ.lastSon
#let obj = c.getEnvTypeForOwner(s.owner).lastSon
addField(obj, s)
if s.name == getIdent(":state"):
obj.n[0].sym.id = -s.id
else:
addField(obj, s)
# but always return because the rest of the proc is only relevant when
# ow != owner:
return
@ -410,7 +421,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
"""
# mark 'owner' as taking a closure:
c.somethingToDo = true
markAsClosure(owner, n)
markAsClosure(c.graph, owner, n)
addClosureParam(c, owner, n.info)
#echo "capturing ", n.info
# variable 's' is actually captured:
@ -435,7 +446,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
"""
let up = w.skipGenericOwner
#echo "up for ", w.name.s, " up ", up.name.s
markAsClosure(w, n)
markAsClosure(c.graph, w, n)
addClosureParam(c, w, n.info) # , ow
createUpField(c, w, up, n.info)
w = up
@ -462,10 +473,10 @@ proc initLiftingPass(fn: PSym): LiftingPass =
result.processed.incl(fn.id)
result.envVars = initTable[int, PNode]()
proc accessViaEnvParam(n: PNode; owner: PSym): PNode =
proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
let s = n.sym
# Type based expression construction for simplicity:
let envParam = getHiddenParam(owner)
let envParam = getHiddenParam(g, owner)
if not envParam.isNil:
var access = newSymNode(envParam)
while true:
@ -477,7 +488,7 @@ proc accessViaEnvParam(n: PNode; owner: PSym): PNode =
let upField = lookupInRecord(obj.n, getIdent(upName))
if upField == nil: break
access = rawIndirectAccess(access, upField, n.info)
localError(n.info, "internal error: environment misses: " & s.name.s)
localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
result = n
proc newEnvVar(owner: PSym; typ: PType): PNode =
@ -496,22 +507,22 @@ proc newEnvVar(owner: PSym; typ: PType): PNode =
proc setupEnvVar(owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode =
if owner.isIterator:
return getHiddenParam(owner).newSymNode
return getHiddenParam(d.graph, owner).newSymNode
result = c.envvars.getOrDefault(owner.id)
if result.isNil:
let envVarType = d.ownerToType.getOrDefault(owner.id)
if envVarType.isNil:
localError owner.info, "internal error: could not determine closure type"
localError d.graph.config, owner.info, "internal error: could not determine closure type"
result = newEnvVar(owner, envVarType)
c.envVars[owner.id] = result
proc getUpViaParam(owner: PSym): PNode =
let p = getHiddenParam(owner)
proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode =
let p = getHiddenParam(g, owner)
result = p.newSymNode
if owner.isIterator:
let upField = lookupInRecord(p.typ.lastSon.n, getIdent(upName))
if upField == nil:
localError(owner.info, "could not find up reference for closure iter")
localError(g.config, owner.info, "could not find up reference for closure iter")
else:
result = rawIndirectAccess(result, upField, p.info)
@ -521,7 +532,7 @@ proc rawClosureCreation(owner: PSym;
var env: PNode
if owner.isIterator:
env = getHiddenParam(owner).newSymNode
env = getHiddenParam(d.graph, owner).newSymNode
else:
env = setupEnvVar(owner, d, c)
if env.kind == nkSym:
@ -529,7 +540,7 @@ proc rawClosureCreation(owner: PSym;
addVar(v, env)
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym"internalNew", env))
result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env))
# add assignment statements for captured parameters:
for i in 1..<owner.typ.n.len:
let local = owner.typ.n[i].sym
@ -540,7 +551,7 @@ proc rawClosureCreation(owner: PSym;
let upField = lookupInRecord(env.typ.lastSon.n, getIdent(upName))
if upField != nil:
let up = getUpViaParam(owner)
let up = getUpViaParam(d.graph, owner)
if up != nil and upField.typ == up.typ:
result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
up, env.info))
@ -548,7 +559,7 @@ proc rawClosureCreation(owner: PSym;
# result.add(newAsgnStmt(rawIndirectAccess(env, upField, env.info),
# oldenv, env.info))
else:
localError(env.info, "internal error: cannot create up reference")
localError(d.graph.config, env.info, "internal error: cannot create up reference")
proc closureCreationForIter(iter: PNode;
d: DetectionPass; c: var LiftingPass): PNode =
@ -556,10 +567,10 @@ proc closureCreationForIter(iter: PNode;
let owner = iter.sym.skipGenericOwner
var v = newSym(skVar, getIdent(envName), owner, iter.info)
incl(v.flags, sfShadowed)
v.typ = getHiddenParam(iter.sym).typ
v.typ = getHiddenParam(d.graph, iter.sym).typ
var vnode: PNode
if owner.isIterator:
let it = getHiddenParam(owner)
let it = getHiddenParam(d.graph, owner)
addUniqueField(it.typ.sons[0], v)
vnode = indirectAccess(newSymNode(it), v, v.info)
else:
@ -567,7 +578,7 @@ proc closureCreationForIter(iter: PNode;
var vs = newNodeI(nkVarSection, iter.info)
addVar(vs, vnode)
result.add(vs)
result.add(newCall(getSysSym"internalNew", vnode))
result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
let upField = lookupInRecord(v.typ.lastSon.n, getIdent(upName))
if upField != nil:
@ -576,8 +587,8 @@ proc closureCreationForIter(iter: PNode;
result.add(newAsgnStmt(rawIndirectAccess(vnode, upField, iter.info),
u, iter.info))
else:
localError(iter.info, "internal error: cannot create up reference for iter")
result.add makeClosure(iter.sym, vnode, iter.info)
localError(d.graph.config, iter.info, "internal error: cannot create up reference for iter")
result.add makeClosure(d.graph, iter.sym, vnode, iter.info)
proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode =
@ -587,11 +598,11 @@ proc accessViaEnvVar(n: PNode; owner: PSym; d: DetectionPass;
if field != nil:
result = rawIndirectAccess(access, field, n.info)
else:
localError(n.info, "internal error: not part of closure object type")
localError(d.graph.config, n.info, "internal error: not part of closure object type")
result = n
proc getStateField(owner: PSym): PSym =
getHiddenParam(owner).typ.sons[0].n.sons[0].sym
proc getStateField*(g: ModuleGraph; owner: PSym): PSym =
getHiddenParam(g, owner).typ.sons[0].n.sons[0].sym
proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode
@ -599,8 +610,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode =
if c.inContainer > 0:
localError(n.info, "invalid control flow: 'yield' within a constructor")
let state = getStateField(owner)
localError(d.graph.config, n.info, "invalid control flow: 'yield' within a constructor")
let state = getStateField(d.graph, owner)
assert state != nil
assert state.typ != nil
assert state.typ.n != nil
@ -610,20 +621,22 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
var stateAsgnStmt = newNodeI(nkAsgn, n.info)
stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)),
state, n.info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo,
getSysType(d.graph, n.info, tyInt)))
var retStmt = newNodeI(nkReturnStmt, n.info)
if n.sons[0].kind != nkEmpty:
var a = newNodeI(nkAsgn, n.sons[0].info)
var retVal = liftCapturedVars(n.sons[0], owner, d, c)
addSon(a, newSymNode(getIterResult(owner)))
addSon(a, newSymNode(getClosureIterResult(owner)))
addSon(a, retVal)
retStmt.add(a)
else:
retStmt.add(emptyNode)
var stateLabelStmt = newNodeI(nkState, n.info)
stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, getSysType(tyInt)))
stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo,
getSysType(d.graph, n.info, tyInt)))
result = newNodeI(nkStmtList, n.info)
result.add(stateAsgnStmt)
@ -632,16 +645,16 @@ proc transformYield(n: PNode; owner: PSym; d: DetectionPass;
proc transformReturn(n: PNode; owner: PSym; d: DetectionPass;
c: var LiftingPass): PNode =
let state = getStateField(owner)
let state = getStateField(d.graph, owner)
result = newNodeI(nkStmtList, n.info)
var stateAsgnStmt = newNodeI(nkAsgn, n.info)
stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)),
state, n.info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(d.graph, n.info, tyInt)))
result.add(stateAsgnStmt)
result.add(n)
proc wrapIterBody(n: PNode; owner: PSym): PNode =
proc wrapIterBody(g: ModuleGraph; n: PNode; owner: PSym): PNode =
if not owner.isIterator: return n
when false:
# unfortunately control flow is still convoluted and we can end up
@ -655,7 +668,7 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode =
let info = n.info
result = newNodeI(nkStmtList, info)
var gs = newNodeI(nkGotoState, info)
gs.add(rawIndirectAccess(newSymNode(owner.getHiddenParam), getStateField(owner), info))
gs.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)), getStateField(g, owner), info))
result.add(gs)
var state0 = newNodeI(nkState, info)
state0.add(newIntNode(nkIntLit, 0))
@ -664,9 +677,9 @@ proc wrapIterBody(n: PNode; owner: PSym): PNode =
result.add(n)
var stateAsgnStmt = newNodeI(nkAsgn, info)
stateAsgnStmt.add(rawIndirectAccess(newSymNode(owner.getHiddenParam),
getStateField(owner), info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(tyInt)))
stateAsgnStmt.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)),
getStateField(g, owner), info))
stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(g, info, tyInt)))
result.add(stateAsgnStmt)
proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
@ -674,25 +687,25 @@ proc symToClosure(n: PNode; owner: PSym; d: DetectionPass;
let s = n.sym
if s == owner:
# recursive calls go through (lambda, hiddenParam):
let available = getHiddenParam(owner)
result = makeClosure(s, available.newSymNode, n.info)
let available = getHiddenParam(d.graph, owner)
result = makeClosure(d.graph, s, available.newSymNode, n.info)
elif s.isIterator:
result = closureCreationForIter(n, d, c)
elif s.skipGenericOwner == owner:
# direct dependency, so use the outer's env variable:
result = makeClosure(s, setupEnvVar(owner, d, c), n.info)
result = makeClosure(d.graph, s, setupEnvVar(owner, d, c), n.info)
else:
let available = getHiddenParam(owner)
let wanted = getHiddenParam(s).typ
let available = getHiddenParam(d.graph, owner)
let wanted = getHiddenParam(d.graph, s).typ
# ugh: call through some other inner proc;
var access = newSymNode(available)
while true:
if access.typ == wanted:
return makeClosure(s, access, n.info)
return makeClosure(d.graph, s, access, n.info)
let obj = access.typ.sons[0]
let upField = lookupInRecord(obj.n, getIdent(upName))
if upField == nil:
localError(n.info, "internal error: no environment found")
localError(d.graph.config, n.info, "internal error: no environment found")
return n
access = rawIndirectAccess(access, upField, n.info)
@ -708,7 +721,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
# echo renderTree(s.getBody, {renderIds})
let oldInContainer = c.inContainer
c.inContainer = 0
let body = wrapIterBody(liftCapturedVars(s.getBody, s, d, c), s)
var body = liftCapturedVars(s.getBody, s, d, c)
if oldIterTransf in d.graph.config.features:
body = wrapIterBody(d.graph, body, s)
if c.envvars.getOrDefault(s.id).isNil:
s.ast.sons[bodyPos] = body
else:
@ -718,9 +733,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
result = symToClosure(n, owner, d, c)
elif s.id in d.capturedVars:
if s.owner != owner:
result = accessViaEnvParam(n, owner)
result = accessViaEnvParam(d.graph, n, owner)
elif owner.isIterator and interestingIterVar(s):
result = accessViaEnvParam(n, owner)
result = accessViaEnvParam(d.graph, n, owner)
else:
result = accessViaEnvVar(n, owner, d, c)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
@ -751,9 +766,9 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass;
if n[1].kind == nkClosure: result = n[1]
else:
if owner.isIterator:
if n.kind == nkYieldStmt:
if oldIterTransf in d.graph.config.features and n.kind == nkYieldStmt:
return transformYield(n, owner, d, c)
elif n.kind == nkReturnStmt:
elif oldIterTransf in d.graph.config.features and n.kind == nkReturnStmt:
return transformReturn(n, owner, d, c)
elif nfLL in n.flags:
# special case 'when nimVm' due to bug #3636:
@ -786,8 +801,8 @@ proc semCaptureSym*(s, owner: PSym) =
# since the analysis is not entirely correct, we don't set 'tfCapturesEnv'
# here
proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
var d = initDetectionPass(fn)
proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNode =
var d = initDetectionPass(g, fn)
var c = initLiftingPass(fn)
# pretend 'fn' is a closure iterator for the analysis:
let oldKind = fn.kind
@ -796,11 +811,11 @@ proc liftIterToProc*(fn: PSym; body: PNode; ptrType: PType): PNode =
fn.typ.callConv = ccClosure
d.ownerToType[fn.id] = ptrType
detectCapturedVars(body, fn, d)
result = wrapIterBody(liftCapturedVars(body, fn, d, c), fn)
result = wrapIterBody(g, liftCapturedVars(body, fn, d, c), fn)
fn.kind = oldKind
fn.typ.callConv = oldCC
proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode =
# XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs
# the transformation even when compiling to JS ...
@ -808,23 +823,26 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
if body.kind == nkEmpty or (
gCmd in {cmdCompileToPHP, cmdCompileToJS} and not isCompileTime) or
g.config.cmd == cmdCompileToJS and not isCompileTime) or
fn.skipGenericOwner.kind != skModule:
# ignore forward declaration:
result = body
tooEarly = true
else:
var d = initDetectionPass(fn)
var d = initDetectionPass(g, fn)
detectCapturedVars(body, fn, d)
if not d.somethingToDo and fn.isIterator:
addClosureParam(d, fn, body.info)
d.somethingToDo = true
if d.somethingToDo:
var c = initLiftingPass(fn)
var newBody = liftCapturedVars(body, fn, d, c)
result = liftCapturedVars(body, fn, d, c)
if c.envvars.getOrDefault(fn.id) != nil:
newBody = newTree(nkStmtList, rawClosureCreation(fn, d, c), newBody)
result = wrapIterBody(newBody, fn)
result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result)
if oldIterTransf in g.config.features:
result = wrapIterBody(g, result, fn)
else:
result = body
#if fn.name.s == "get2":
@ -832,15 +850,12 @@ proc liftLambdas*(fn: PSym, body: PNode; tooEarly: var bool): PNode =
# echo renderTree(result, {renderIds})
proc liftLambdasForTopLevel*(module: PSym, body: PNode): PNode =
if body.kind == nkEmpty or gCmd == cmdCompileToJS:
result = body
else:
# XXX implement it properly
result = body
# XXX implement it properly
result = body
# ------------------- iterator transformation --------------------------------
proc liftForLoop*(body: PNode; owner: PSym): PNode =
proc liftForLoop*(g: ModuleGraph; body: PNode; owner: PSym): PNode =
# problem ahead: the iterator could be invoked indirectly, but then
# we don't know what environment to create here:
#
@ -865,13 +880,14 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
cl = createClosure()
while true:
let i = foo(cl)
nkBreakState(cl.state)
if (nkBreakState(cl.state)):
break
...
"""
if liftingHarmful(owner): return body
if liftingHarmful(g.config, owner): return body
var L = body.len
if not (body.kind == nkForStmt and body[L-2].kind in nkCallKinds):
localError(body.info, "ignored invalid for loop")
localError(g.config, body.info, "ignored invalid for loop")
return body
var call = body[L-2]
@ -884,7 +900,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
# createClosure()
let iter = op.sym
let hp = getHiddenParam(iter)
let hp = getHiddenParam(g, iter)
env = newSym(skLet, iter.name, owner, body.info)
env.typ = hp.typ
env.flags = hp.flags
@ -893,7 +909,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
addVar(v, newSymNode(env))
result.add(v)
# add 'new' statement:
result.add(newCall(getSysSym"internalNew", env.newSymNode))
result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
elif op.kind == nkStmtListExpr:
let closure = op.lastSon
if closure.kind == nkClosure:
@ -903,7 +919,7 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
var loopBody = newNodeI(nkStmtList, body.info, 3)
var whileLoop = newNodeI(nkWhileStmt, body.info, 2)
whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(tyBool))
whileLoop.sons[0] = newIntTypeNode(nkIntLit, 1, getSysType(g, body.info, tyBool))
whileLoop.sons[1] = loopBody
result.add whileLoop
@ -918,12 +934,23 @@ proc liftForLoop*(body: PNode; owner: PSym): PNode =
addSon(vpart, ast.emptyNode) # no explicit type
if not env.isNil:
call.sons[0] = makeClosure(call.sons[0].sym, env.newSymNode, body.info)
call.sons[0] = makeClosure(g, call.sons[0].sym, env.newSymNode, body.info)
addSon(vpart, call)
addSon(v2, vpart)
loopBody.sons[0] = v2
var bs = newNodeI(nkBreakState, body.info)
bs.addSon(call.sons[0])
loopBody.sons[1] = bs
let ibs = newNode(nkIfStmt)
let elifBranch = newNode(nkElifBranch)
elifBranch.add(bs)
let br = newNode(nkBreakStmt)
br.add(emptyNode)
elifBranch.add(br)
ibs.add(elifBranch)
loopBody.sons[1] = ibs
loopBody.sons[2] = body[L-1]

View file

@ -17,7 +17,7 @@
import
hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
wordrecg
wordrecg, configuration
const
MaxLineLength* = 80 # lines longer than this lead to a warning
@ -60,7 +60,7 @@ type
tkCurlyDotLe, tkCurlyDotRi, # {. and .}
tkParDotLe, tkParDotRi, # (. and .)
tkComma, tkSemiColon,
tkColon, tkColonColon, tkEquals, tkDot, tkDotDot,
tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkBracketLeColon,
tkOpr, tkComment, tkAccent,
tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
@ -98,7 +98,7 @@ const
"tkTripleStrLit", "tkGStrLit", "tkGTripleStrLit", "tkCharLit", "(",
")", "[", "]", "{", "}", "[.", ".]", "{.", ".}", "(.", ".)",
",", ";",
":", "::", "=", ".", "..",
":", "::", "=", ".", "..", "[:",
"tkOpr", "tkComment", "`",
"tkSpaces", "tkInfixOpr",
"tkPrefixOpr", "tkPostfixOpr"]
@ -131,9 +131,9 @@ type
# like 0b01 or r"\L" are unaffected
commentOffsetA*, commentOffsetB*: int
TErrorHandler* = proc (info: TLineInfo; msg: TMsgKind; arg: string)
TErrorHandler* = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string)
TLexer* = object of TBaseLexer
fileIdx*: int32
fileIdx*: FileIndex
indentAhead*: int # if > 0 an indendation has already been read
# this is needed because scanning comments
# needs so much look-ahead
@ -144,13 +144,12 @@ type
cache*: IdentCache
when defined(nimsuggest):
previousToken: TLineInfo
config*: ConfigRef
when defined(nimpretty):
var
gIndentationWidth*: int
var gLinesCompiled*: int # all lines that have been compiled
proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} =
result = newLineInfo(L.fileIdx, tok.line, tok.col)
when defined(nimpretty):
@ -165,13 +164,12 @@ proc isKeyword*(kind: TTokType): bool =
template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion
proc isNimIdentifier*(s: string): bool =
if s[0] in SymStartChars:
let sLen = s.len
if sLen > 0 and s[0] in SymStartChars:
var i = 1
var sLen = s.len
while i < sLen:
if s[i] == '_':
inc(i)
if s[i] notin SymChars: return
if s[i] == '_': inc(i)
if i < sLen and s[i] notin SymChars: return
inc(i)
result = true
@ -192,8 +190,8 @@ proc prettyTok*(tok: TToken): string =
if isKeyword(tok.tokType): result = "keyword " & tok.ident.s
else: result = tokToStr(tok)
proc printTok*(tok: TToken) =
msgWriteln($tok.line & ":" & $tok.col & "\t" &
proc printTok*(conf: ConfigRef; tok: TToken) =
msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" &
TokTypeToStr[tok.tokType] & " " & tokToStr(tok))
proc initToken*(L: var TToken) =
@ -222,8 +220,8 @@ proc fillToken(L: var TToken) =
L.commentOffsetA = 0
L.commentOffsetB = 0
proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream;
cache: IdentCache) =
proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream;
cache: IdentCache; config: ConfigRef) =
openBaseLexer(lex, inputstream)
lex.fileIdx = fileidx
lex.indentAhead = - 1
@ -232,13 +230,15 @@ proc openLexer*(lex: var TLexer, fileIdx: int32, inputstream: PLLStream;
lex.cache = cache
when defined(nimsuggest):
lex.previousToken.fileIndex = fileIdx
lex.config = config
proc openLexer*(lex: var TLexer, filename: string, inputstream: PLLStream;
cache: IdentCache) =
openLexer(lex, filename.fileInfoIdx, inputstream, cache)
cache: IdentCache; config: ConfigRef) =
openLexer(lex, fileInfoIdx(config, filename), inputstream, cache, config)
proc closeLexer*(lex: var TLexer) =
inc(gLinesCompiled, lex.lineNumber)
if lex.config != nil:
inc(lex.config.linesCompiled, lex.lineNumber)
closeBaseLexer(lex)
proc getLineInfo(L: TLexer): TLineInfo =
@ -246,9 +246,9 @@ proc getLineInfo(L: TLexer): TLineInfo =
proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) =
if L.errorHandler.isNil:
msgs.message(info, msg, arg)
msgs.message(L.config, info, msg, arg)
else:
L.errorHandler(info, msg, arg)
L.errorHandler(L.config, info, msg, arg)
proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") =
L.dispMessage(getLineInfo(L), msg, arg)
@ -274,7 +274,7 @@ template tokenEnd(tok, pos) {.dirty.} =
when defined(nimsuggest):
let colB = getColNumber(L, pos)+1
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
L.cursor = CursorPosition.InToken
gTrackPos.col = colA.int16
colA = 0
@ -285,9 +285,9 @@ template tokenEndIgnore(tok, pos) =
when defined(nimsuggest):
let colB = getColNumber(L, pos)
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
gTrackPos.fileIndex = trackPosInvalidFileIdx
gTrackPos.line = -1
gTrackPos.line = 0'u16
colA = 0
when defined(nimpretty):
tok.offsetB = L.offsetBase + pos
@ -299,7 +299,7 @@ template tokenEndPrevious(tok, pos) =
# the cursor in a string literal or comment:
let colB = getColNumber(L, pos)
if L.fileIdx == gTrackPos.fileIndex and gTrackPos.col in colA..colB and
L.lineNumber == gTrackPos.line and gIdeCmd in {ideSug, ideCon}:
L.lineNumber == gTrackPos.line.int and L.config.ideCmd in {ideSug, ideCon}:
L.cursor = CursorPosition.BeforeToken
gTrackPos = L.previousToken
gTrackPosAttached = true
@ -311,12 +311,12 @@ template tokenEndPrevious(tok, pos) =
# We need to parse the largest uint literal without overflow checks
proc unsafeParseUInt(s: string, b: var BiggestInt, start = 0): int =
var i = start
if s[i] in {'0'..'9'}:
if i < s.len and s[i] in {'0'..'9'}:
b = 0
while s[i] in {'0'..'9'}:
while i < s.len and s[i] in {'0'..'9'}:
b = b * 10 + (ord(s[i]) - ord('0'))
inc(i)
while s[i] == '_': inc(i) # underscores are allowed and ignored
while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
result = i - start
{.pop.} # overflowChecks
@ -341,7 +341,8 @@ proc getNumber(L: var TLexer, result: var TToken) =
break
if buf[pos] == '_':
if buf[pos+1] notin chars:
lexMessage(L, errInvalidToken, "_")
lexMessage(L, errGenerated,
"only single underscores may occur in a token: '__' is invalid")
break
add(tok.literal, '_')
inc(pos)
@ -355,7 +356,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
inc(pos)
L.bufpos = pos
proc lexMessageLitNum(L: var TLexer, msg: TMsgKind, startpos: int) =
proc lexMessageLitNum(L: var TLexer, msg: string, startpos: int) =
# Used to get slightly human friendlier err messages.
# Note: the erroneous 'O' char in the character set is intentional
const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O',
@ -376,7 +377,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
add(t.literal, L.buf[L.bufpos])
matchChars(L, t, {'0'..'9'})
L.bufpos = msgPos
lexMessage(L, msg, t.literal)
lexMessage(L, errGenerated, msg % t.literal)
var
startpos, endpos: int
@ -398,7 +399,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
eatChar(L, result, '0')
case L.buf[L.bufpos]
of 'O':
lexMessageLitNum(L, errInvalidNumberOctalCode, startpos)
lexMessageLitNum(L, "$1 is not a valid number; did you mean octal? Then use one of '0o', '0c' or '0C'.", startpos)
of 'x', 'X':
eatChar(L, result, 'x')
matchUnderscoreChars(L, result, {'0'..'9', 'a'..'f', 'A'..'F'})
@ -409,7 +410,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
eatChar(L, result, 'b')
matchUnderscoreChars(L, result, {'0'..'1'})
else:
internalError(getLineInfo(L), "getNumber")
internalError(L.config, getLineInfo(L), "getNumber")
else:
matchUnderscoreChars(L, result, {'0'..'9'})
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
@ -464,7 +465,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
result.tokType = tkInt8Lit
inc(postPos)
else:
lexMessageLitNum(L, errInvalidNumber, startpos)
lexMessageLitNum(L, "invalid number: '$1'", startpos)
of 'u', 'U':
inc(postPos)
if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'):
@ -482,12 +483,12 @@ proc getNumber(L: var TLexer, result: var TToken) =
else:
result.tokType = tkUIntLit
else:
lexMessageLitNum(L, errInvalidNumber, startpos)
lexMessageLitNum(L, "invalid number: '$1'", startpos)
# Is there still a literalish char awaiting? Then it's an error!
if L.buf[postPos] in literalishChars or
(L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}):
lexMessageLitNum(L, errInvalidNumber, startpos)
lexMessageLitNum(L, "invalid number: '$1'", startpos)
# Third stage, extract actual number
L.bufpos = startpos # restore position
@ -528,7 +529,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
else:
break
else:
internalError(getLineInfo(L), "getNumber")
internalError(L.config, getLineInfo(L), "getNumber")
case result.tokType
of tkIntLit, tkInt64Lit: result.iNumber = xi
@ -545,7 +546,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
# XXX: Test this on big endian machine!
of tkFloat64Lit, tkFloatLit:
result.fNumber = (cast[PFloat64](addr(xi)))[]
else: internalError(getLineInfo(L), "getNumber")
else: internalError(L.config, getLineInfo(L), "getNumber")
# Bounds checks. Non decimal literals are allowed to overflow the range of
# the datatype as long as their pattern don't overflow _bitwise_, hence
@ -561,7 +562,7 @@ proc getNumber(L: var TLexer, result: var TToken) =
if outOfRange:
#echo "out of range num: ", result.iNumber, " vs ", xi
lexMessageLitNum(L, errNumberOutOfRange, startpos)
lexMessageLitNum(L, "number out of range: '$1'", startpos)
else:
case result.tokType
@ -577,19 +578,20 @@ proc getNumber(L: var TLexer, result: var TToken) =
result.iNumber = parseBiggestInt(result.literal)
# Explicit bounds checks
let outOfRange = case result.tokType:
of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
result.iNumber > BiggestInt(uint8.high))
of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
result.iNumber > BiggestInt(uint16.high))
of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high)
of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or
result.iNumber > BiggestInt(uint32.high))
else: false
let outOfRange =
case result.tokType
of tkInt8Lit: (result.iNumber < int8.low or result.iNumber > int8.high)
of tkUInt8Lit: (result.iNumber < BiggestInt(uint8.low) or
result.iNumber > BiggestInt(uint8.high))
of tkInt16Lit: (result.iNumber < int16.low or result.iNumber > int16.high)
of tkUInt16Lit: (result.iNumber < BiggestInt(uint16.low) or
result.iNumber > BiggestInt(uint16.high))
of tkInt32Lit: (result.iNumber < int32.low or result.iNumber > int32.high)
of tkUInt32Lit: (result.iNumber < BiggestInt(uint32.low) or
result.iNumber > BiggestInt(uint32.high))
else: false
if outOfRange: lexMessageLitNum(L, errNumberOutOfRange, startpos)
if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos)
# Promote int literal to int64? Not always necessary, but more consistent
if result.tokType == tkIntLit:
@ -597,9 +599,9 @@ proc getNumber(L: var TLexer, result: var TToken) =
result.tokType = tkInt64Lit
except ValueError:
lexMessageLitNum(L, errInvalidNumber, startpos)
lexMessageLitNum(L, "invalid number: '$1'", startpos)
except OverflowError, RangeError:
lexMessageLitNum(L, errNumberOutOfRange, startpos)
lexMessageLitNum(L, "number out of range: '$1'", startpos)
tokenEnd(result, postPos-1)
L.bufpos = postPos
@ -625,7 +627,16 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) =
inc(L.bufpos) # skip '\'
case L.buf[L.bufpos]
of 'n', 'N':
if tok.tokType == tkCharLit: lexMessage(L, errNnotAllowedInCharacter)
if L.config.oldNewlines:
if tok.tokType == tkCharLit:
lexMessage(L, errGenerated, "\\n not allowed in character literal")
add(tok.literal, tnl)
else:
add(tok.literal, '\L')
inc(L.bufpos)
of 'p', 'P':
if tok.tokType == tkCharLit:
lexMessage(L, errGenerated, "\\p not allowed in character literal")
add(tok.literal, tnl)
inc(L.bufpos)
of 'r', 'R', 'c', 'C':
@ -687,8 +698,8 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) =
var xi = 0
handleDecChars(L, xi)
if (xi <= 255): add(tok.literal, chr(xi))
else: lexMessage(L, errInvalidCharacterConstant)
else: lexMessage(L, errInvalidCharacterConstant)
else: lexMessage(L, errGenerated, "invalid character constant")
else: lexMessage(L, errGenerated, "invalid character constant")
proc newString(s: cstring, len: int): string =
## XXX, how come there is no support for this?
@ -703,7 +714,7 @@ proc handleCRLF(L: var TLexer, pos: int): int =
if col > MaxLineLength:
lexMessagePos(L, hintLineTooLong, pos)
if optEmbedOrigSrc in gGlobalOptions:
if optEmbedOrigSrc in L.config.globalOptions:
let lineStart = cast[ByteAddress](L.buf) + L.lineStart
let line = newString(cast[cstring](lineStart), col)
addSourceLine(L.fileIdx, line)
@ -747,12 +758,12 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
tokenEndIgnore(tok, pos)
pos = handleCRLF(L, pos)
buf = L.buf
add(tok.literal, tnl)
add(tok.literal, "\n")
of nimlexbase.EndOfFile:
tokenEndIgnore(tok, pos)
var line2 = L.lineNumber
L.lineNumber = line
lexMessagePos(L, errClosingTripleQuoteExpected, L.lineStart)
lexMessagePos(L, errGenerated, L.lineStart, "closing \"\"\" expected, but end of file reached")
L.lineNumber = line2
L.bufpos = pos
break
@ -775,7 +786,7 @@ proc getString(L: var TLexer, tok: var TToken, rawMode: bool) =
break
elif c in {CR, LF, nimlexbase.EndOfFile}:
tokenEndIgnore(tok, pos)
lexMessage(L, errClosingQuoteExpected)
lexMessage(L, errGenerated, "closing \" expected")
break
elif (c == '\\') and not rawMode:
L.bufpos = pos
@ -791,12 +802,13 @@ proc getCharacter(L: var TLexer, tok: var TToken) =
inc(L.bufpos) # skip '
var c = L.buf[L.bufpos]
case c
of '\0'..pred(' '), '\'': lexMessage(L, errInvalidCharacterConstant)
of '\0'..pred(' '), '\'': lexMessage(L, errGenerated, "invalid character literal")
of '\\': getEscapedChar(L, tok)
else:
tok.literal = $c
inc(L.bufpos)
if L.buf[L.bufpos] != '\'': lexMessage(L, errMissingFinalQuote)
if L.buf[L.bufpos] != '\'':
lexMessage(L, errGenerated, "missing closing ' for character literal")
tokenEndIgnore(tok, L.bufpos)
inc(L.bufpos) # skip '
@ -817,7 +829,7 @@ proc getSymbol(L: var TLexer, tok: var TToken) =
inc(pos)
of '_':
if buf[pos+1] notin SymChars:
lexMessage(L, errInvalidToken, "_")
lexMessage(L, errGenerated, "invalid token: trailing underscore")
break
inc(pos)
else: break
@ -995,13 +1007,17 @@ proc skip(L: var TLexer, tok: var TToken) =
var buf = L.buf
tokenBegin(tok, pos)
tok.strongSpaceA = 0
when defined(nimpretty):
var hasComment = false
tok.commentOffsetA = L.offsetBase + pos
tok.commentOffsetB = tok.commentOffsetA
while true:
case buf[pos]
of ' ':
inc(pos)
inc(tok.strongSpaceA)
of '\t':
if not L.allowTabs: lexMessagePos(L, errTabulatorsAreNotAllowed, pos)
if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabulators are not allowed")
inc(pos)
of CR, LF:
tokenEndPrevious(tok, pos)
@ -1013,6 +1029,7 @@ proc skip(L: var TLexer, tok: var TToken) =
inc(pos)
inc(indent)
elif buf[pos] == '#' and buf[pos+1] == '[':
when defined(nimpretty): hasComment = true
skipMultiLineComment(L, tok, pos+2, false)
pos = L.bufpos
buf = L.buf
@ -1026,14 +1043,11 @@ proc skip(L: var TLexer, tok: var TToken) =
of '#':
# do not skip documentation comment:
if buf[pos+1] == '#': break
when defined(nimpretty):
tok.commentOffsetA = L.offsetBase + pos
when defined(nimpretty): hasComment = true
if buf[pos+1] == '[':
skipMultiLineComment(L, tok, pos+2, false)
pos = L.bufpos
buf = L.buf
when defined(nimpretty):
tok.commentOffsetB = L.offsetBase + pos
else:
tokenBegin(tok, pos)
while buf[pos] notin {CR, LF, nimlexbase.EndOfFile}: inc(pos)
@ -1045,6 +1059,9 @@ proc skip(L: var TLexer, tok: var TToken) =
tokenEndPrevious(tok, pos-1)
L.bufpos = pos
when defined(nimpretty):
if hasComment:
tok.commentOffsetB = L.offsetBase + pos
tok.tokType = tkComment
if gIndentationWidth <= 0:
gIndentationWidth = tok.indent
@ -1053,7 +1070,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
when defined(nimsuggest):
# we attach the cursor to the last *strong* token
if tok.tokType notin weakTokens:
L.previousToken.line = tok.line.int16
L.previousToken.line = tok.line.uint16
L.previousToken.col = tok.col.int16
when defined(nimsuggest):
@ -1066,6 +1083,10 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
else:
tok.indent = -1
skip(L, tok)
when defined(nimpretty):
if tok.tokType == tkComment:
L.indentAhead = L.currLineIndent
return
var c = L.buf[L.bufpos]
tok.line = L.lineNumber
tok.col = getColNumber(L, L.bufpos)
@ -1101,7 +1122,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
tok.tokType = tkParLe
when defined(nimsuggest):
if L.fileIdx == gTrackPos.fileIndex and tok.col < gTrackPos.col and
tok.line == gTrackPos.line and gIdeCmd == ideCon:
tok.line == gTrackPos.line.int and L.config.ideCmd == ideCon:
gTrackPos.col = tok.col.int16
of ')':
tok.tokType = tkParRi
@ -1111,6 +1132,9 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
if L.buf[L.bufpos] == '.' and L.buf[L.bufpos+1] != '.':
tok.tokType = tkBracketDotLe
inc(L.bufpos)
elif L.buf[L.bufpos] == ':':
tok.tokType = tkBracketLeColon
inc(L.bufpos)
else:
tok.tokType = tkBracketLe
of ']':
@ -1119,7 +1143,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
of '.':
when defined(nimsuggest):
if L.fileIdx == gTrackPos.fileIndex and tok.col+1 == gTrackPos.col and
tok.line == gTrackPos.line and gIdeCmd == ideSug:
tok.line == gTrackPos.line.int and L.config.ideCmd == ideSug:
tok.tokType = tkDot
L.cursor = CursorPosition.InToken
gTrackPos.col = tok.col.int16
@ -1161,7 +1185,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
else:
tok.literal = $c
tok.tokType = tkInvalid
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
of '\"':
# check for extended raw string literal:
var rawMode = L.bufpos > 0 and L.buf[L.bufpos-1] in SymChars
@ -1178,7 +1202,7 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
getNumber(L, tok)
let c = L.buf[L.bufpos]
if c in SymChars+{'_'}:
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier")
else:
if c in OpChars:
getOperator(L, tok)
@ -1188,6 +1212,6 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) =
else:
tok.literal = $c
tok.tokType = tkInvalid
lexMessage(L, errInvalidToken, c & " (\\" & $(ord(c)) & ')')
lexMessage(L, errGenerated, "invalid token: " & c & " (\\" & $(ord(c)) & ')')
inc(L.bufpos)
atTokenEnd()

View file

@ -52,17 +52,17 @@ proc lookupParam(params, dest: PNode): PSym =
if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id:
return params[i].sym
proc liftLocalsIfRequested*(prc: PSym; n: PNode): PNode =
proc liftLocalsIfRequested*(prc: PSym; n: PNode; conf: ConfigRef): PNode =
let liftDest = getPragmaVal(prc.ast, wLiftLocals)
if liftDest == nil: return n
let partialParam = lookupParam(prc.typ.n, liftDest)
if partialParam.isNil:
localError(liftDest.info, "'$1' is not a parameter of '$2'" %
localError(conf, liftDest.info, "'$1' is not a parameter of '$2'" %
[$liftDest, prc.name.s])
return n
let objType = partialParam.typ.skipTypes(abstractPtrs)
if objType.kind != tyObject or tfPartial notin objType.flags:
localError(liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest)
localError(conf, liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest)
return n
var c = Ctx(partialParam: partialParam, objType: objType)
let w = newTree(nkStmtList, n)

View file

@ -11,23 +11,23 @@
import
intsets, ast, astalgo, idents, semdata, types, msgs, options, rodread,
renderer, wordrecg, idgen, nimfix.prettybase
renderer, wordrecg, idgen, nimfix.prettybase, configuration, strutils
proc ensureNoMissingOrUnusedSymbols(scope: PScope)
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
proc noidentError(n, origin: PNode) =
proc noidentError(conf: ConfigRef; n, origin: PNode) =
var m = ""
if origin != nil:
m.add "in expression '" & origin.renderTree & "': "
m.add "identifier expected, but found '" & n.renderTree & "'"
localError(n.info, m)
localError(conf, n.info, m)
proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent =
proc considerQuotedIdent*(conf: ConfigRef; n: PNode, origin: PNode = nil): PIdent =
## Retrieve a PIdent from a PNode, taking into account accent nodes.
## ``origin`` can be nil. If it is not nil, it is used for a better
## error message.
template handleError(n, origin: PNode) =
noidentError(n, origin)
noidentError(conf, n, origin)
result = getIdent"<Error>"
case n.kind
@ -36,7 +36,7 @@ proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent =
of nkAccQuoted:
case n.len
of 0: handleError(n, origin)
of 1: result = considerQuotedIdent(n.sons[0], origin)
of 1: result = considerQuotedIdent(conf, n.sons[0], origin)
else:
var id = ""
for i in 0..<n.len:
@ -44,6 +44,7 @@ proc considerQuotedIdent*(n: PNode, origin: PNode = nil): PIdent =
case x.kind
of nkIdent: id.add(x.ident.s)
of nkSym: id.add(x.sym.name.s)
of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
else: handleError(n, origin)
result = getIdent(id)
of nkOpenSymChoice, nkClosedSymChoice:
@ -70,7 +71,7 @@ proc rawCloseScope*(c: PContext) =
c.currentScope = c.currentScope.parent
proc closeScope*(c: PContext) =
ensureNoMissingOrUnusedSymbols(c.currentScope)
ensureNoMissingOrUnusedSymbols(c, c.currentScope)
rawCloseScope(c)
iterator walkScopes*(scope: PScope): PScope =
@ -79,15 +80,15 @@ iterator walkScopes*(scope: PScope): PScope =
yield current
current = current.parent
proc skipAlias*(s: PSym; n: PNode): PSym =
proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
if s == nil or s.kind != skAlias:
result = s
else:
result = s.owner
if gCmd == cmdPretty:
if conf.cmd == cmdPretty:
prettybase.replaceDeprecated(n.info, s, result)
else:
message(n.info, warnDeprecated, "use " & result.name.s & " instead; " &
message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
s.name.s)
proc localSearchInScope*(c: PContext, s: PIdent): PSym =
@ -124,14 +125,14 @@ proc errorSym*(c: PContext, n: PNode): PSym =
# ensure that 'considerQuotedIdent' can't fail:
if m.kind == nkDotExpr: m = m.sons[1]
let ident = if m.kind in {nkIdent, nkSym, nkAccQuoted}:
considerQuotedIdent(m)
considerQuotedIdent(c.config, m)
else:
getIdent("err:" & renderTree(m))
result = newSym(skError, ident, getCurrOwner(c), n.info)
result = newSym(skError, ident, getCurrOwner(c), n.info, {})
result.typ = errorType(c)
incl(result.flags, sfDiscardable)
# pretend it's imported from some unknown module to prevent cascading errors:
if gCmd != cmdInteractive and c.compilesContextId == 0:
if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
c.importTable.addSym(result)
type
@ -153,7 +154,7 @@ proc getSymRepr*(s: PSym): string =
else:
result = s.name.s
proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
# check if all symbols have been used and defined:
var it: TTabIter
var s = initTabIter(it, scope.symbols)
@ -163,54 +164,53 @@ proc ensureNoMissingOrUnusedSymbols(scope: PScope) =
# too many 'implementation of X' errors are annoying
# and slow 'suggest' down:
if missingImpls == 0:
localError(s.info, errImplOfXexpected, getSymRepr(s))
localError(c.config, s.info, "implementation of '$1' expected" % getSymRepr(s))
inc missingImpls
elif {sfUsed, sfExported} * s.flags == {} and optHints in s.options:
# BUGFIX: check options in s!
if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam}:
# XXX: implicit type params are currently skTypes
# maybe they can be made skGenericParam as well.
if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and
s.typ.kind != tyGenericParam:
message(s.info, hintXDeclaredButNotUsed, getSymRepr(s))
message(c.config, s.info, hintXDeclaredButNotUsed, getSymRepr(s))
s = nextIter(it, scope.symbols)
proc wrongRedefinition*(info: TLineInfo, s: string) =
if gCmd != cmdInteractive:
localError(info, errAttemptToRedefine, s)
proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string) =
if c.config.cmd != cmdInteractive:
localError(c.config, info, "redefinition of '$1'" % s)
proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) =
if not c.currentScope.addUniqueSym(sym):
wrongRedefinition(info, sym.name.s)
wrongRedefinition(c, info, sym.name.s)
proc addDecl*(c: PContext, sym: PSym) =
if not c.currentScope.addUniqueSym(sym):
wrongRedefinition(sym.info, sym.name.s)
wrongRedefinition(c, sym.info, sym.name.s)
proc addPrelimDecl*(c: PContext, sym: PSym) =
discard c.currentScope.addUniqueSym(sym)
proc addDeclAt*(scope: PScope, sym: PSym) =
proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) =
if not scope.addUniqueSym(sym):
wrongRedefinition(sym.info, sym.name.s)
wrongRedefinition(c, sym.info, sym.name.s)
proc addInterfaceDeclAux(c: PContext, sym: PSym) =
if sfExported in sym.flags:
# add to interface:
if c.module != nil: strTableAdd(c.module.tab, sym)
else: internalError(sym.info, "addInterfaceDeclAux")
else: internalError(c.config, sym.info, "addInterfaceDeclAux")
proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) =
addDeclAt(scope, sym)
addDeclAt(c, scope, sym)
addInterfaceDeclAux(c, sym)
proc addOverloadableSymAt*(scope: PScope, fn: PSym) =
proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) =
if fn.kind notin OverloadableSyms:
internalError(fn.info, "addOverloadableSymAt")
internalError(c.config, fn.info, "addOverloadableSymAt")
return
let check = strTableGet(scope.symbols, fn.name)
if check != nil and check.kind notin OverloadableSyms:
wrongRedefinition(fn.info, fn.name.s)
wrongRedefinition(c, fn.info, fn.name.s)
else:
scope.addSym(fn)
@ -221,12 +221,10 @@ proc addInterfaceDecl*(c: PContext, sym: PSym) =
proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) =
# it adds the symbol to the interface if appropriate
addOverloadableSymAt(scope, sym)
addOverloadableSymAt(c, scope, sym)
addInterfaceDeclAux(c, sym)
when defined(nimfix):
import strutils
# when we cannot find the identifier, retry with a changed identifer:
proc altSpelling(x: PIdent): PIdent =
case x.s[0]
@ -254,7 +252,7 @@ proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
err.add candidate.owner.name.s & "." & candidate.name.s
candidate = nextIdentIter(ti, c.importTable.symbols)
inc i
localError(info, errGenerated, err)
localError(c.config, info, errGenerated, err)
proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
var err = "undeclared identifier: '" & name & "'"
@ -263,13 +261,13 @@ proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string) =
err.add c.recursiveDep
# prevent excessive errors for 'nim check'
c.recursiveDep = nil
localError(info, errGenerated, err)
localError(c.config, info, errGenerated, err)
proc lookUp*(c: PContext, n: PNode): PSym =
# Looks up a symbol. Generates an error in case of nil.
case n.kind
of nkIdent:
result = searchInScopes(c, n.ident).skipAlias(n)
result = searchInScopes(c, n.ident).skipAlias(n, c.config)
if result == nil:
fixSpelling(n, n.ident, searchInScopes)
errorUndeclaredIdentifier(c, n.info, n.ident.s)
@ -277,14 +275,14 @@ proc lookUp*(c: PContext, n: PNode): PSym =
of nkSym:
result = n.sym
of nkAccQuoted:
var ident = considerQuotedIdent(n)
result = searchInScopes(c, ident).skipAlias(n)
var ident = considerQuotedIdent(c.config, n)
result = searchInScopes(c, ident).skipAlias(n, c.config)
if result == nil:
fixSpelling(n, ident, searchInScopes)
errorUndeclaredIdentifier(c, n.info, ident.s)
result = errorSym(c, n)
else:
internalError(n.info, "lookUp")
internalError(c.config, n.info, "lookUp")
return
if contains(c.ambiguousSymbols, result.id):
errorUseQualifier(c, n.info, result)
@ -298,11 +296,11 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
const allExceptModule = {low(TSymKind)..high(TSymKind)}-{skModule,skPackage}
case n.kind
of nkIdent, nkAccQuoted:
var ident = considerQuotedIdent(n)
var ident = considerQuotedIdent(c.config, n)
if checkModule in flags:
result = searchInScopes(c, ident).skipAlias(n)
result = searchInScopes(c, ident).skipAlias(n, c.config)
else:
result = searchInScopes(c, ident, allExceptModule).skipAlias(n)
result = searchInScopes(c, ident, allExceptModule).skipAlias(n, c.config)
if result == nil and checkPureEnumFields in flags:
result = strTableGet(c.pureEnumFields, ident)
if result == nil and checkUndeclared in flags:
@ -324,12 +322,12 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
if n.sons[1].kind == nkIdent:
ident = n.sons[1].ident
elif n.sons[1].kind == nkAccQuoted:
ident = considerQuotedIdent(n.sons[1])
ident = considerQuotedIdent(c.config, n.sons[1])
if ident != nil:
if m == c.module:
result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n)
result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
else:
result = strTableGet(m.tab, ident).skipAlias(n)
result = strTableGet(m.tab, ident).skipAlias(n, c.config)
if result == nil and checkUndeclared in flags:
fixSpelling(n.sons[1], ident, searchInScopes)
errorUndeclaredIdentifier(c, n.sons[1].info, ident.s)
@ -338,7 +336,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
result = n.sons[1].sym
elif checkUndeclared in flags and
n.sons[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
localError(n.sons[1].info, errIdentifierExpected,
localError(c.config, n.sons[1].info, "identifier expected, but got: " &
renderTree(n.sons[1]))
result = errorSym(c, n.sons[1])
else:
@ -348,11 +346,11 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
case n.kind
of nkIdent, nkAccQuoted:
var ident = considerQuotedIdent(n)
var ident = considerQuotedIdent(c.config, n)
o.scope = c.currentScope
o.mode = oimNoQualifier
while true:
result = initIdentIter(o.it, o.scope.symbols, ident).skipAlias(n)
result = initIdentIter(o.it, o.scope.symbols, ident).skipAlias(n, c.config)
if result != nil:
break
else:
@ -369,17 +367,17 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
if n.sons[1].kind == nkIdent:
ident = n.sons[1].ident
elif n.sons[1].kind == nkAccQuoted:
ident = considerQuotedIdent(n.sons[1], n)
ident = considerQuotedIdent(c.config, n.sons[1], n)
if ident != nil:
if o.m == c.module:
# a module may access its private members:
result = initIdentIter(o.it, c.topLevelScope.symbols,
ident).skipAlias(n)
ident).skipAlias(n, c.config)
o.mode = oimSelfModule
else:
result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n)
result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n, c.config)
else:
noidentError(n.sons[1], n)
noidentError(c.config, n.sons[1], n)
result = errorSym(c, n.sons[1])
of nkClosedSymChoice, nkOpenSymChoice:
o.mode = oimSymChoice
@ -407,18 +405,18 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
result = nil
of oimNoQualifier:
if o.scope != nil:
result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n)
result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n, c.config)
while result == nil:
o.scope = o.scope.parent
if o.scope == nil: break
result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n)
result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n, c.config)
# BUGFIX: o.it.name <-> n.ident
else:
result = nil
of oimSelfModule:
result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n)
result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config)
of oimOtherModule:
result = nextIdentIter(o.it, o.m.tab).skipAlias(n)
result = nextIdentIter(o.it, o.m.tab).skipAlias(n, c.config)
of oimSymChoice:
if o.symChoiceIndex < sonsLen(n):
result = n.sons[o.symChoiceIndex].sym
@ -429,19 +427,19 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
o.mode = oimSymChoiceLocalLookup
o.scope = c.currentScope
result = firstIdentExcluding(o.it, o.scope.symbols,
n.sons[0].sym.name, o.inSymChoice).skipAlias(n)
n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
while result == nil:
o.scope = o.scope.parent
if o.scope == nil: break
result = firstIdentExcluding(o.it, o.scope.symbols,
n.sons[0].sym.name, o.inSymChoice).skipAlias(n)
n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
of oimSymChoiceLocalLookup:
result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice).skipAlias(n)
result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice).skipAlias(n, c.config)
while result == nil:
o.scope = o.scope.parent
if o.scope == nil: break
result = firstIdentExcluding(o.it, o.scope.symbols,
n.sons[0].sym.name, o.inSymChoice).skipAlias(n)
n.sons[0].sym.name, o.inSymChoice).skipAlias(n, c.config)
if result != nil and result.kind == skStub: loadStub(result)
@ -455,5 +453,3 @@ proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind];
else: return nil # ambiguous
a = nextOverloadIter(o, c, n)
proc isInfixAs*(n: PNode): bool =
return n.kind == nkInfix and considerQuotedIdent(n[0]).s == "as"

View file

@ -12,18 +12,18 @@
const
genPrefix* = ":tmp" # prefix for generated names
import ast, astalgo, types, idents, magicsys, msgs, options
import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs
from trees import getMagic
proc newDeref*(n: PNode): PNode {.inline.} =
result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
addSon(result, n)
proc newTupleAccess*(tup: PNode, i: int): PNode =
proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
abstractInst).sons[i])
addSon(result, copyTree(tup))
var lit = newNodeIT(nkIntLit, tup.info, getSysType(tyInt))
var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt))
lit.intVal = i
addSon(result, lit)
@ -44,12 +44,12 @@ proc newFastAsgnStmt(le, ri: PNode): PNode =
result.sons[0] = le
result.sons[1] = ri
proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; owner: PSym): PNode =
assert n.kind == nkVarTuple
let value = n.lastSon
result = newNodeI(nkStmtList, n.info)
var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info)
var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info, g.config.options)
temp.typ = skipTypes(value.typ, abstractInst)
incl(temp.flags, sfFromGeneric)
@ -61,7 +61,7 @@ proc lowerTupleUnpacking*(n: PNode; owner: PSym): PNode =
result.add newAsgnStmt(tempAsNode, value)
for i in 0 .. n.len-3:
if n.sons[i].kind == nkSym: v.addVar(n.sons[i])
result.add newAsgnStmt(n.sons[i], newTupleAccess(tempAsNode, i))
result.add newAsgnStmt(n.sons[i], newTupleAccess(g, tempAsNode, i))
proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
result = newNodeI(nkBracketExpr, tup.info)
@ -77,7 +77,7 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
let value = n.lastSon
result = newNodeI(nkStmtList, n.info)
var temp = newSym(skLet, getIdent("_"), owner, value.info)
var temp = newSym(skLet, getIdent("_"), owner, value.info, owner.options)
var v = newNodeI(nkLetSection, value.info)
let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
@ -95,7 +95,7 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
proc lowerSwap*(n: PNode; owner: PSym): PNode =
result = newNodeI(nkStmtList, n.info)
# note: cannot use 'skTemp' here cause we really need the copy for the VM :-(
var temp = newSym(skVar, getIdent(genPrefix), owner, n.info)
var temp = newSym(skVar, getIdent(genPrefix), owner, n.info, owner.options)
temp.typ = n.sons[1].typ
incl(temp.flags, sfFromGeneric)
@ -112,16 +112,16 @@ proc lowerSwap*(n: PNode; owner: PSym): PNode =
result.add newFastAsgnStmt(n[1], n[2])
result.add newFastAsgnStmt(n[2], tempAsNode)
proc createObj*(owner: PSym, info: TLineInfo; final=true): PType =
proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType =
result = newType(tyObject, owner)
if final:
rawAddSon(result, nil)
incl result.flags, tfFinal
else:
rawAddSon(result, getCompilerProc("RootObj").typ)
rawAddSon(result, getCompilerProc(g, "RootObj").typ)
result.n = newNodeI(nkRecList, info)
let s = newSym(skType, getIdent("Env_" & info.toFilename),
owner, info)
owner, info, owner.options)
incl s.flags, sfAnon
s.typ = result
result.sym = s
@ -174,7 +174,8 @@ proc lookupInRecord(n: PNode, id: int): PSym =
proc addField*(obj: PType; s: PSym) =
# because of 'gensym' support, we have to mangle the name with its ID.
# This is hacky but the clean solution is much more complex than it looks.
var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info)
var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info,
s.options)
field.id = -s.id
let t = skipIntLit(s.typ)
field.typ = t
@ -185,7 +186,8 @@ proc addField*(obj: PType; s: PSym) =
proc addUniqueField*(obj: PType; s: PSym): PSym {.discardable.} =
result = lookupInRecord(obj.n, s.id)
if result == nil:
var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info)
var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info,
s.options)
field.id = -s.id
let t = skipIntLit(s.typ)
field.typ = t
@ -218,7 +220,7 @@ proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
#if field == nil:
# echo "FIELD ", b
# debug deref.typ
internalAssert field != nil
assert field != nil
addSon(deref, a)
result = newNodeI(nkDotExpr, info)
addSon(result, deref)
@ -242,7 +244,7 @@ proc indirectAccess(a: PNode, b: string, info: TLineInfo): PNode =
#if field == nil:
# echo "FIELD ", b
# debug deref.typ
internalAssert field != nil
assert field != nil
addSon(deref, a)
result = newNodeI(nkDotExpr, info)
addSon(result, deref)
@ -278,12 +280,12 @@ proc genDeref*(n: PNode): PNode =
n.typ.skipTypes(abstractInst).sons[0])
result.add n
proc callCodegenProc*(name: string, arg1: PNode;
proc callCodegenProc*(g: ModuleGraph; name: string, arg1: PNode;
arg2, arg3, optionalArgs: PNode = nil): PNode =
result = newNodeI(nkCall, arg1.info)
let sym = magicsys.getCompilerProc(name)
let sym = magicsys.getCompilerProc(g, name)
if sym == nil:
localError(arg1.info, errSystemNeeds, name)
localError(g.config, arg1.info, "system module needs: " & name)
else:
result.add newSymNode(sym)
result.add arg1
@ -330,12 +332,13 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
# note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
# for the stricter check and likewise we can skip 'seq' for a less
# strict check:
if t.kind in {tyVar, tySequence}: t = t.sons[0]
if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
result = not containsGarbageCollectedRef(t)
proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
v: PNode; useShallowCopy=false): PSym =
result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info)
result = newSym(skTemp, getIdent(genPrefix), owner, varSection.info,
owner.options)
result.typ = typ
incl(result.flags, sfFromGeneric)
@ -349,7 +352,7 @@ proc addLocalVar(varSection, varInit: PNode; owner: PSym; typ: PType;
varInit.add newFastAsgnStmt(newSymNode(result), v)
else:
let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
deepCopyCall.sons[0] = newSymNode(getSysMagic("deepCopy", mDeepCopy))
deepCopyCall.sons[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))
deepCopyCall.sons[1] = newSymNode(result)
deepCopyCall.sons[2] = v
varInit.add deepCopyCall
@ -384,23 +387,23 @@ stmtList:
"""
proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
varSection, varInit, call, barrier, fv: PNode;
spawnKind: TSpawnResult): PSym =
var body = newNodeI(nkStmtList, f.info)
var threadLocalBarrier: PSym
if barrier != nil:
var varSection2 = newNodeI(nkVarSection, barrier.info)
threadLocalBarrier = addLocalVar(varSection2, nil, argsParam.owner,
threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner,
barrier.typ, barrier)
body.add varSection2
body.add callCodegenProc("barrierEnter", threadLocalBarrier.newSymNode)
body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.newSymNode)
var threadLocalProm: PSym
if spawnKind == srByVar:
threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
elif fv != nil:
internalAssert fv.typ.kind == tyGenericInst
threadLocalProm = addLocalVar(varSection, nil, argsParam.owner, fv.typ, fv)
internalAssert g.config, fv.typ.kind == tyGenericInst
threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
body.add varSection
body.add varInit
if fv != nil and spawnKind != srByVar:
@ -409,30 +412,30 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
"owner", fv.info), threadParam.newSymNode)
body.add callCodegenProc("nimArgsPassingDone", threadParam.newSymNode)
body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.newSymNode)
if spawnKind == srByVar:
body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
elif fv != nil:
let fk = fv.typ.sons[1].flowVarKind
if fk == fvInvalid:
localError(f.info, "cannot create a flowVar of type: " &
localError(g.config, f.info, "cannot create a flowVar of type: " &
typeToString(fv.typ.sons[1]))
body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
if fk == fvGC: "data" else: "blob", fv.info), call)
if fk == fvGC:
let incRefCall = newNodeI(nkCall, fv.info, 2)
incRefCall.sons[0] = newSymNode(getSysMagic("GCref", mGCref))
incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))
incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
"data", fv.info)
body.add incRefCall
if barrier == nil:
# by now 'fv' is shared and thus might have beeen overwritten! we need
# to use the thread-local view instead:
body.add callCodegenProc("nimFlowVarSignal", threadLocalProm.newSymNode)
body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.newSymNode)
else:
body.add call
if barrier != nil:
body.add callCodegenProc("barrierLeave", threadLocalBarrier.newSymNode)
body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.newSymNode)
var params = newNodeI(nkFormalParams, f.info)
params.add emptyNode
@ -449,7 +452,8 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
t.n.add argsParam.newSymNode
let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
result = newSym(skProc, getIdent(name), argsParam.owner, f.info)
result = newSym(skProc, getIdent(name), argsParam.owner, f.info,
argsParam.options)
result.ast = newProcNode(nkProcDef, f.info, body, params, newSymNode(result))
result.typ = t
@ -460,7 +464,7 @@ proc createCastExpr(argsParam: PSym; objType: PType): PNode =
result.typ = newType(tyPtr, objType.owner)
result.typ.rawAddSon(objType)
proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym,
castExpr, call,
varSection, varInit, result: PNode) =
let formals = n[0].typ.n
@ -469,18 +473,18 @@ proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
# we pick n's type here, which hopefully is 'tyArray' and not
# 'tyOpenArray':
var argType = n[i].typ.skipTypes(abstractInst)
if i < formals.len and formals[i].typ.kind == tyVar:
localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter")
if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}:
localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
#elif containsTyRef(argType):
# localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
var field = newSym(skField, fieldname, objType.owner, n.info)
var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
field.typ = argType
objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
let temp = addLocalVar(varSection, varInit, objType.owner, argType,
let temp = addLocalVar(g, varSection, varInit, objType.owner, argType,
indirectAccess(castExpr, field, n.info))
call.add(newSymNode(temp))
@ -501,20 +505,20 @@ proc getRoot*(n: PNode): PSym =
if getMagic(n) == mSlice: result = getRoot(n.sons[1])
else: discard
proc newIntLit*(value: BiggestInt): PNode =
proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
result = nkIntLit.newIntNode(value)
result.typ = getSysType(tyInt)
result.typ = getSysType(g, info, tyInt)
proc genHigh*(n: PNode): PNode =
proc genHigh*(g: ModuleGraph; n: PNode): PNode =
if skipTypes(n.typ, abstractVar).kind == tyArray:
result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
result = newIntLit(g, n.info, lastOrd(skipTypes(n.typ, abstractVar)))
else:
result = newNodeI(nkCall, n.info, 2)
result.typ = getSysType(tyInt)
result.sons[0] = newSymNode(getSysMagic("high", mHigh))
result.typ = getSysType(g, n.info, tyInt)
result.sons[0] = newSymNode(getSysMagic(g, n.info, "high", mHigh))
result.sons[1] = n
proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym;
castExpr, call,
varSection, varInit, result: PNode) =
let formals = n[0].typ.n
@ -529,16 +533,16 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
# localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
var field = newSym(skField, fieldname, objType.owner, n.info)
var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
if argType.kind in {tyVarargs, tyOpenArray}:
# important special case: we always create a zero-copy slice:
let slice = newNodeI(nkCall, n.info, 4)
slice.typ = n.typ
slice.sons[0] = newSymNode(createMagic("slice", mSlice))
slice.sons[0].typ = getSysType(tyInt) # fake type
var fieldB = newSym(skField, tmpName, objType.owner, n.info)
fieldB.typ = getSysType(tyInt)
slice.sons[0] = newSymNode(createMagic(g, "slice", mSlice))
slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type
var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
fieldB.typ = getSysType(g, n.info, tyInt)
objType.addField(fieldB)
if getMagic(n) == mSlice:
@ -547,13 +551,13 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
var fieldA = newSym(skField, tmpName, objType.owner, n.info)
fieldA.typ = getSysType(tyInt)
var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
fieldA.typ = getSysType(g, n.info, tyInt)
objType.addField(fieldA)
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldA.typ,
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldA.typ,
indirectAccess(castExpr, fieldA, n.info),
useShallowCopy=true)
slice.sons[2] = threadLocal.newSymNode
@ -562,13 +566,13 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
field.typ = a.typ
objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(n))
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
slice.sons[2] = newIntLit(0)
slice.sons[2] = newIntLit(g, n.info, 0)
# the array itself does not need to go through a thread local variable:
slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
let threadLocal = addLocalVar(varSection,nil, objType.owner, fieldB.typ,
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldB.typ,
indirectAccess(castExpr, fieldB, n.info),
useShallowCopy=true)
slice.sons[3] = threadLocal.newSymNode
@ -580,7 +584,7 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
field.typ = a.typ
objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
let threadLocal = addLocalVar(varSection,nil, objType.owner, field.typ,
let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ,
indirectAccess(castExpr, field, n.info),
useShallowCopy=true)
call.add(genDeref(threadLocal.newSymNode))
@ -589,13 +593,13 @@ proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
field.typ = argType
objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
let threadLocal = addLocalVar(varSection, varInit,
let threadLocal = addLocalVar(g, varSection, varInit,
objType.owner, field.typ,
indirectAccess(castExpr, field, n.info),
useShallowCopy=true)
call.add(threadLocal.newSymNode)
proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType;
barrier, dest: PNode = nil): PNode =
# if 'barrier' != nil, then it is in a 'parallel' section and we
# generate quite different code
@ -603,35 +607,35 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
let spawnKind = spawnResult(retType, barrier!=nil)
case spawnKind
of srVoid:
internalAssert dest == nil
internalAssert g.config, dest == nil
result = newNodeI(nkStmtList, n.info)
of srFlowVar:
internalAssert dest == nil
internalAssert g.config, dest == nil
result = newNodeIT(nkStmtListExpr, n.info, retType)
of srByVar:
if dest == nil: localError(n.info, "'spawn' must not be discarded")
if dest == nil: localError(g.config, n.info, "'spawn' must not be discarded")
result = newNodeI(nkStmtList, n.info)
if n.kind notin nkCallKinds:
localError(n.info, "'spawn' takes a call expression")
localError(g.config, n.info, "'spawn' takes a call expression")
return
if optThreadAnalysis in gGlobalOptions:
if optThreadAnalysis in g.config.globalOptions:
if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
localError(n.info, "'spawn' takes a GC safe call expression")
localError(g.config, n.info, "'spawn' takes a GC safe call expression")
var
threadParam = newSym(skParam, getIdent"thread", owner, n.info)
argsParam = newSym(skParam, getIdent"args", owner, n.info)
threadParam = newSym(skParam, getIdent"thread", owner, n.info, g.config.options)
argsParam = newSym(skParam, getIdent"args", owner, n.info, g.config.options)
block:
let ptrType = getSysType(tyPointer)
let ptrType = getSysType(g, n.info, tyPointer)
threadParam.typ = ptrType
argsParam.typ = ptrType
argsParam.position = 1
var objType = createObj(owner, n.info)
var objType = createObj(g, owner, n.info)
incl(objType.flags, tfFinal)
let castExpr = createCastExpr(argsParam, objType)
var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info)
var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info, g.config.options)
block:
scratchObj.typ = objType
incl(scratchObj.flags, sfFromGeneric)
@ -644,36 +648,36 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
# templates and macros are in fact valid here due to the nature of
# the transformation:
if fn.kind == nkClosure:
localError(n.info, "closure in spawn environment is not allowed")
localError(g.config, n.info, "closure in spawn environment is not allowed")
if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
skFunc, skMethod, skConverter}):
# for indirect calls we pass the function pointer in the scratchObj
var argType = n[0].typ.skipTypes(abstractInst)
var field = newSym(skField, getIdent"fn", owner, n.info)
var field = newSym(skField, getIdent"fn", owner, n.info, g.config.options)
field.typ = argType
objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
fn = indirectAccess(castExpr, field, n.info)
elif fn.kind == nkSym and fn.sym.kind == skIterator:
localError(n.info, "iterator in spawn environment is not allowed")
localError(g.config, n.info, "iterator in spawn environment is not allowed")
elif fn.typ.callConv == ccClosure:
localError(n.info, "closure in spawn environment is not allowed")
localError(g.config, n.info, "closure in spawn environment is not allowed")
call.add(fn)
var varSection = newNodeI(nkVarSection, n.info)
var varInit = newNodeI(nkStmtList, n.info)
if barrier.isNil:
setupArgsForConcurrency(n, objType, scratchObj, castExpr, call,
setupArgsForConcurrency(g, n, objType, scratchObj, castExpr, call,
varSection, varInit, result)
else:
setupArgsForParallelism(n, objType, scratchObj, castExpr, call,
setupArgsForParallelism(g, n, objType, scratchObj, castExpr, call,
varSection, varInit, result)
var barrierAsExpr: PNode = nil
if barrier != nil:
let typ = newType(tyPtr, owner)
typ.rawAddSon(magicsys.getCompilerProc("Barrier").typ)
var field = newSym(skField, getIdent"barrier", owner, n.info)
typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
var field = newSym(skField, getIdent"barrier", owner, n.info, g.config.options)
field.typ = typ
objType.addField(field)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
@ -681,7 +685,7 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
var fvField, fvAsExpr: PNode = nil
if spawnKind == srFlowVar:
var field = newSym(skField, getIdent"fv", owner, n.info)
var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options)
field.typ = retType
objType.addField(field)
fvField = newDotExpr(scratchObj, field)
@ -689,20 +693,20 @@ proc wrapProcForSpawn*(owner: PSym; spawnExpr: PNode; retType: PType;
# create flowVar:
result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
if barrier == nil:
result.add callCodegenProc("nimFlowVarCreateSemaphore", fvField)
result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField)
elif spawnKind == srByVar:
var field = newSym(skField, getIdent"fv", owner, n.info)
var field = newSym(skField, getIdent"fv", owner, n.info, g.config.options)
field.typ = newType(tyPtr, objType.owner)
field.typ.rawAddSon(retType)
objType.addField(field)
fvAsExpr = indirectAccess(castExpr, field, n.info)
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
let wrapper = createWrapperProc(fn, threadParam, argsParam,
let wrapper = createWrapperProc(g, fn, threadParam, argsParam,
varSection, varInit, call,
barrierAsExpr, fvAsExpr, spawnKind)
result.add callCodegenProc("nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.newSymNode,
genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
if spawnKind == srFlowVar: result.add fvField

View file

@ -10,63 +10,62 @@
# Built-in types and compilerprocs are registered here.
import
ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread
ast, astalgo, hashes, msgs, platform, nversion, times, idents, rodread,
modulegraphs
var systemModule*: PSym
export createMagic
var
gSysTypes: array[TTypeKind, PType]
compilerprocs: TStrTable
exposed: TStrTable
proc nilOrSysInt*(g: ModuleGraph): PType = g.sysTypes[tyInt]
proc nilOrSysInt*: PType = gSysTypes[tyInt]
proc registerSysType*(g: ModuleGraph; t: PType) =
if g.sysTypes[t.kind] == nil: g.sysTypes[t.kind] = t
proc registerSysType*(t: PType) =
if gSysTypes[t.kind] == nil: gSysTypes[t.kind] = t
proc newSysType(kind: TTypeKind, size: int): PType =
result = newType(kind, systemModule)
proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType =
result = newType(kind, g.systemModule)
result.size = size
result.align = size.int16
proc getSysSym*(name: string): PSym =
result = strTableGet(systemModule.tab, getIdent(name))
proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
result = strTableGet(g.systemModule.tab, getIdent(name))
if result == nil:
rawMessage(errSystemNeeds, name)
result = newSym(skError, getIdent(name), systemModule, systemModule.info)
result.typ = newType(tyError, systemModule)
localError(g.config, info, "system module needs: " & name)
result = newSym(skError, getIdent(name), g.systemModule, g.systemModule.info, {})
result.typ = newType(tyError, g.systemModule)
if result.kind == skStub: loadStub(result)
if result.kind == skAlias: result = result.owner
proc createMagic*(name: string, m: TMagic): PSym =
result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
result.magic = m
when false:
proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
result = newSym(skProc, getIdent(name), nil, unknownLineInfo())
result.magic = m
let
opNot* = createMagic("not", mNot)
opContains* = createMagic("contains", mInSet)
when false:
let
opNot* = createMagic("not", mNot)
opContains* = createMagic("contains", mInSet)
proc getSysMagic*(name: string, m: TMagic): PSym =
proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
var ti: TIdentIter
let id = getIdent(name)
var r = initIdentIter(ti, systemModule.tab, id)
var r = initIdentIter(ti, g.systemModule.tab, id)
while r != nil:
if r.kind == skStub: loadStub(r)
if r.magic == m:
# prefer the tyInt variant:
if r.typ.sons[0] != nil and r.typ.sons[0].kind == tyInt: return r
result = r
r = nextIdentIter(ti, systemModule.tab)
r = nextIdentIter(ti, g.systemModule.tab)
if result != nil: return result
rawMessage(errSystemNeeds, name)
result = newSym(skError, id, systemModule, systemModule.info)
result.typ = newType(tyError, systemModule)
localError(g.config, info, "system module needs: " & name)
result = newSym(skError, id, g.systemModule, g.systemModule.info, {})
result.typ = newType(tyError, g.systemModule)
proc sysTypeFromName*(name: string): PType =
result = getSysSym(name).typ
proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType =
result = getSysSym(g, info, name).typ
proc getSysType*(kind: TTypeKind): PType =
result = gSysTypes[kind]
proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType =
template sysTypeFromName(s: string): untyped = sysTypeFromName(g, info, s)
result = g.sysTypes[kind]
if result == nil:
case kind
of tyInt: result = sysTypeFromName("int")
@ -88,51 +87,49 @@ proc getSysType*(kind: TTypeKind): PType =
of tyString: result = sysTypeFromName("string")
of tyCString: result = sysTypeFromName("cstring")
of tyPointer: result = sysTypeFromName("pointer")
of tyNil: result = newSysType(tyNil, ptrSize)
else: internalError("request for typekind: " & $kind)
gSysTypes[kind] = result
of tyNil: result = newSysType(g, tyNil, ptrSize)
else: internalError(g.config, "request for typekind: " & $kind)
g.sysTypes[kind] = result
if result.kind != kind:
internalError("wanted: " & $kind & " got: " & $result.kind)
if result == nil: internalError("type not found: " & $kind)
internalError(g.config, "wanted: " & $kind & " got: " & $result.kind)
if result == nil: internalError(g.config, "type not found: " & $kind)
var
intTypeCache: array[-5..64, PType]
proc resetSysTypes*(g: ModuleGraph) =
g.systemModule = nil
initStrTable(g.compilerprocs)
initStrTable(g.exposed)
for i in low(g.sysTypes)..high(g.sysTypes):
g.sysTypes[i] = nil
proc resetSysTypes* =
systemModule = nil
initStrTable(compilerprocs)
initStrTable(exposed)
for i in low(gSysTypes)..high(gSysTypes):
gSysTypes[i] = nil
for i in low(g.intTypeCache)..high(g.intTypeCache):
g.intTypeCache[i] = nil
for i in low(intTypeCache)..high(intTypeCache):
intTypeCache[i] = nil
proc getIntLitType*(literal: PNode): PType =
proc getIntLitType*(g: ModuleGraph; literal: PNode): PType =
# we cache some common integer literal types for performance:
let value = literal.intVal
if value >= low(intTypeCache) and value <= high(intTypeCache):
result = intTypeCache[value.int]
if value >= low(g.intTypeCache) and value <= high(g.intTypeCache):
result = g.intTypeCache[value.int]
if result == nil:
let ti = getSysType(tyInt)
let ti = getSysType(g, literal.info, tyInt)
result = copyType(ti, ti.owner, false)
result.n = literal
intTypeCache[value.int] = result
g.intTypeCache[value.int] = result
else:
let ti = getSysType(tyInt)
let ti = getSysType(g, literal.info, tyInt)
result = copyType(ti, ti.owner, false)
result.n = literal
proc getFloatLitType*(literal: PNode): PType =
proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType =
# for now we do not cache these:
result = newSysType(tyFloat, size=8)
result = newSysType(g, tyFloat, size=8)
result.n = literal
proc skipIntLit*(t: PType): PType {.inline.} =
if t.n != nil:
if t.kind in {tyInt, tyFloat}:
return getSysType(t.kind)
result = t
if t.n != nil and t.kind in {tyInt, tyFloat}:
result = copyType(t, t.owner, false)
result.n = nil
else:
result = t
proc addSonSkipIntLit*(father, son: PType) =
if isNil(father.sons): father.sons = @[]
@ -140,60 +137,59 @@ proc addSonSkipIntLit*(father, son: PType) =
add(father.sons, s)
propagateToOwner(father, s)
proc setIntLitType*(result: PNode) =
proc setIntLitType*(g: ModuleGraph; result: PNode) =
let i = result.intVal
case platform.intSize
of 8: result.typ = getIntLitType(result)
of 8: result.typ = getIntLitType(g, result)
of 4:
if i >= low(int32) and i <= high(int32):
result.typ = getIntLitType(result)
result.typ = getIntLitType(g, result)
else:
result.typ = getSysType(tyInt64)
result.typ = getSysType(g, result.info, tyInt64)
of 2:
if i >= low(int16) and i <= high(int16):
result.typ = getIntLitType(result)
result.typ = getIntLitType(g, result)
elif i >= low(int32) and i <= high(int32):
result.typ = getSysType(tyInt32)
result.typ = getSysType(g, result.info, tyInt32)
else:
result.typ = getSysType(tyInt64)
result.typ = getSysType(g, result.info, tyInt64)
of 1:
# 8 bit CPUs are insane ...
if i >= low(int8) and i <= high(int8):
result.typ = getIntLitType(result)
result.typ = getIntLitType(g, result)
elif i >= low(int16) and i <= high(int16):
result.typ = getSysType(tyInt16)
result.typ = getSysType(g, result.info, tyInt16)
elif i >= low(int32) and i <= high(int32):
result.typ = getSysType(tyInt32)
result.typ = getSysType(g, result.info, tyInt32)
else:
result.typ = getSysType(tyInt64)
else: internalError(result.info, "invalid int size")
proc getCompilerProc*(name: string): PSym =
let ident = getIdent(name)
result = strTableGet(compilerprocs, ident)
if result == nil:
result = strTableGet(rodCompilerprocs, ident)
if result != nil:
strTableAdd(compilerprocs, result)
if result.kind == skStub: loadStub(result)
if result.kind == skAlias: result = result.owner
proc registerCompilerProc*(s: PSym) =
strTableAdd(compilerprocs, s)
proc registerNimScriptSymbol*(s: PSym) =
# Nimscript symbols must be al unique:
let conflict = strTableGet(exposed, s.name)
if conflict == nil:
strTableAdd(exposed, s)
result.typ = getSysType(g, result.info, tyInt64)
else:
localError(s.info, "symbol conflicts with other .exportNims symbol at: " &
$conflict.info)
internalError(g.config, result.info, "invalid int size")
proc getNimScriptSymbol*(name: string): PSym =
strTableGet(exposed, getIdent(name))
proc getCompilerProc*(g: ModuleGraph; name: string): PSym =
let ident = getIdent(name)
result = strTableGet(g.compilerprocs, ident)
when false:
if result == nil:
result = strTableGet(g.rodCompilerprocs, ident)
if result != nil:
strTableAdd(g.compilerprocs, result)
if result.kind == skStub: loadStub(result)
if result.kind == skAlias: result = result.owner
proc resetNimScriptSymbols*() = initStrTable(exposed)
proc registerCompilerProc*(g: ModuleGraph; s: PSym) =
strTableAdd(g.compilerprocs, s)
initStrTable(compilerprocs)
initStrTable(exposed)
proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) =
# Nimscript symbols must be al unique:
let conflict = strTableGet(g.exposed, s.name)
if conflict == nil:
strTableAdd(g.exposed, s)
else:
localError(g.config, s.info,
"symbol conflicts with other .exportNims symbol at: " & $conflict.info)
proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
strTableGet(g.exposed, getIdent(name))
proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed)

View file

@ -16,12 +16,12 @@ import
cgen, jsgen, json, nversion,
platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
docgen2, service, parser, modules, ccgutils, sigmatch, ropes,
modulegraphs, tables
modulegraphs, tables, rod, configuration
from magicsys import systemModule, resetSysTypes
from magicsys import resetSysTypes
proc rodPass =
if gSymbolFiles in {enabledSf, writeOnlySf}:
proc rodPass(g: ModuleGraph) =
if g.config.symbolFiles in {enabledSf, writeOnlySf}:
registerPass(rodwritePass)
proc codegenPass =
@ -35,7 +35,7 @@ proc writeDepsFile(g: ModuleGraph; project: string) =
let f = open(changeFileExt(project, "deps"), fmWrite)
for m in g.modules:
if m != nil:
f.writeLine(toFullPath(m.position.int32))
f.writeLine(toFullPath(m.position.FileIndex))
for k in g.inclToMod.keys:
if g.getModule(k).isNil: # don't repeat includes which are also modules
f.writeLine(k.toFullPath)
@ -46,55 +46,55 @@ proc commandGenDepend(graph: ModuleGraph; cache: IdentCache) =
registerPass(gendependPass)
#registerPass(cleanupPass)
compileProject(graph, cache)
writeDepsFile(graph, gProjectFull)
generateDot(gProjectFull)
execExternalProgram("dot -Tpng -o" & changeFileExt(gProjectFull, "png") &
' ' & changeFileExt(gProjectFull, "dot"))
let project = graph.config.projectFull
writeDepsFile(graph, project)
generateDot(project)
execExternalProgram(graph.config, "dot -Tpng -o" & changeFileExt(project, "png") &
' ' & changeFileExt(project, "dot"))
proc commandCheck(graph: ModuleGraph; cache: IdentCache) =
msgs.gErrorMax = high(int) # do not stop after first error
defineSymbol("nimcheck")
graph.config.errorMax = high(int) # do not stop after first error
defineSymbol(graph.config.symbols, "nimcheck")
semanticPasses() # use an empty backend for semantic checking only
rodPass()
rodPass(graph)
compileProject(graph, cache)
proc commandDoc2(graph: ModuleGraph; cache: IdentCache; json: bool) =
msgs.gErrorMax = high(int) # do not stop after first error
graph.config.errorMax = high(int) # do not stop after first error
semanticPasses()
if json: registerPass(docgen2JsonPass)
else: registerPass(docgen2Pass)
#registerPass(cleanupPass())
compileProject(graph, cache)
finishDoc2Pass(gProjectName)
finishDoc2Pass(graph.config.projectName)
proc commandCompileToC(graph: ModuleGraph; cache: IdentCache) =
extccomp.initVars()
let conf = graph.config
extccomp.initVars(conf)
semanticPasses()
registerPass(cgenPass)
rodPass()
rodPass(graph)
#registerPass(cleanupPass())
compileProject(graph, cache)
cgenWriteModules(graph.backend, graph.config)
if gCmd != cmdRun:
let proj = changeFileExt(gProjectFull, "")
extccomp.callCCompiler(proj)
extccomp.writeJsonBuildInstructions(proj)
if optGenScript in gGlobalOptions:
writeDepsFile(graph, toGeneratedFile(proj, ""))
cgenWriteModules(graph.backend, conf)
if conf.cmd != cmdRun:
let proj = changeFileExt(conf.projectFull, "")
extccomp.callCCompiler(conf, proj)
extccomp.writeJsonBuildInstructions(conf, proj)
if optGenScript in graph.config.globalOptions:
writeDepsFile(graph, toGeneratedFile(conf, proj, ""))
proc commandJsonScript(graph: ModuleGraph; cache: IdentCache) =
let proj = changeFileExt(gProjectFull, "")
extccomp.runJsonBuildInstructions(proj)
let proj = changeFileExt(graph.config.projectFull, "")
extccomp.runJsonBuildInstructions(graph.config, proj)
proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
#incl(gGlobalOptions, optSafeCode)
setTarget(osJS, cpuJS)
#initDefines()
defineSymbol("nimrod") # 'nimrod' is always defined
defineSymbol("ecmascript") # For backward compatibility
defineSymbol("js")
if gCmd == cmdCompileToPHP: defineSymbol("nimphp")
defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility
defineSymbol(graph.config.symbols, "js")
semanticPasses()
registerPass(JSgenPass)
compileProject(graph, cache)
@ -102,19 +102,19 @@ proc commandCompileToJS(graph: ModuleGraph; cache: IdentCache) =
proc interactivePasses(graph: ModuleGraph; cache: IdentCache) =
#incl(gGlobalOptions, optSafeCode)
#setTarget(osNimrodVM, cpuNimrodVM)
initDefines()
defineSymbol("nimscript")
when hasFFI: defineSymbol("nimffi")
initDefines(graph.config.symbols)
defineSymbol(graph.config.symbols, "nimscript")
when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
registerPass(verbosePass)
registerPass(semPass)
registerPass(evalPass)
proc commandInteractive(graph: ModuleGraph; cache: IdentCache) =
msgs.gErrorMax = high(int) # do not stop after first error
graph.config.errorMax = high(int) # do not stop after first error
interactivePasses(graph, cache)
compileSystemModule(graph, cache)
if commandArgs.len > 0:
discard graph.compileModule(fileInfoIdx(gProjectFull), cache, {})
if graph.config.commandArgs.len > 0:
discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), cache, {})
else:
var m = graph.makeStdinModule()
incl(m.flags, sfMainModule)
@ -126,178 +126,171 @@ proc evalNim(graph: ModuleGraph; nodes: PNode, module: PSym; cache: IdentCache)
carryPasses(graph, nodes, module, cache, evalPasses)
proc commandEval(graph: ModuleGraph; cache: IdentCache; exp: string) =
if systemModule == nil:
if graph.systemModule == nil:
interactivePasses(graph, cache)
compileSystemModule(graph, cache)
let echoExp = "echo \"eval\\t\", " & "repr(" & exp & ")"
evalNim(graph, echoExp.parseString(cache), makeStdinModule(graph), cache)
evalNim(graph, echoExp.parseString(cache, graph.config),
makeStdinModule(graph), cache)
proc commandScan(cache: IdentCache) =
var f = addFileExt(mainCommandArg(), NimExt)
proc commandScan(cache: IdentCache, config: ConfigRef) =
var f = addFileExt(mainCommandArg(config), NimExt)
var stream = llStreamOpen(f, fmRead)
if stream != nil:
var
L: TLexer
tok: TToken
initToken(tok)
openLexer(L, f, stream, cache)
openLexer(L, f, stream, cache, config)
while true:
rawGetTok(L, tok)
printTok(tok)
printTok(config, tok)
if tok.tokType == tkEof: break
closeLexer(L)
else:
rawMessage(errCannotOpenFile, f)
rawMessage(config, errGenerated, "cannot open file: " & f)
const
SimulateCaasMemReset = false
PrintRopeCacheStats = false
proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
when SimulateCaasMemReset:
gGlobalOptions.incl(optCaasEnabled)
let conf = graph.config
setupModuleCache()
# In "nim serve" scenario, each command must reset the registered passes
clearPasses()
gLastCmdTime = epochTime()
searchPaths.add(options.libpath)
when false: # gProjectFull.len != 0:
# current path is always looked first for modules
prependStr(searchPaths, gProjectPath)
conf.lastCmdTime = epochTime()
conf.searchPaths.add(conf.libpath)
setId(100)
case command.normalize
case conf.command.normalize
of "c", "cc", "compile", "compiletoc":
# compile means compileToC currently
gCmd = cmdCompileToC
conf.cmd = cmdCompileToC
commandCompileToC(graph, cache)
of "cpp", "compiletocpp":
gCmd = cmdCompileToCpp
defineSymbol("cpp")
conf.cmd = cmdCompileToCpp
defineSymbol(graph.config.symbols, "cpp")
commandCompileToC(graph, cache)
of "objc", "compiletooc":
gCmd = cmdCompileToOC
defineSymbol("objc")
conf.cmd = cmdCompileToOC
defineSymbol(graph.config.symbols, "objc")
commandCompileToC(graph, cache)
of "run":
gCmd = cmdRun
conf.cmd = cmdRun
when hasTinyCBackend:
extccomp.setCC("tcc")
commandCompileToC(graph, cache)
else:
rawMessage(errInvalidCommandX, command)
rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc")
of "js", "compiletojs":
gCmd = cmdCompileToJS
commandCompileToJS(graph, cache)
of "php":
gCmd = cmdCompileToPHP
conf.cmd = cmdCompileToJS
commandCompileToJS(graph, cache)
of "doc0":
wantMainModule()
gCmd = cmdDoc
loadConfigs(DocConfig, cache)
commandDoc()
wantMainModule(conf)
conf.cmd = cmdDoc
loadConfigs(DocConfig, cache, conf)
commandDoc(conf)
of "doc2", "doc":
gCmd = cmdDoc
loadConfigs(DocConfig, cache)
defineSymbol("nimdoc")
conf.cmd = cmdDoc
loadConfigs(DocConfig, cache, conf)
defineSymbol(conf.symbols, "nimdoc")
commandDoc2(graph, cache, false)
of "rst2html":
gCmd = cmdRst2html
loadConfigs(DocConfig, cache)
commandRst2Html()
conf.cmd = cmdRst2html
loadConfigs(DocConfig, cache, conf)
commandRst2Html(conf)
of "rst2tex":
gCmd = cmdRst2tex
loadConfigs(DocTexConfig, cache)
commandRst2TeX()
of "jsondoc":
wantMainModule()
gCmd = cmdDoc
loadConfigs(DocConfig, cache)
wantMainModule()
defineSymbol("nimdoc")
commandJson()
of "jsondoc2":
gCmd = cmdDoc
loadConfigs(DocConfig, cache)
wantMainModule()
defineSymbol("nimdoc")
conf.cmd = cmdRst2tex
loadConfigs(DocTexConfig, cache, conf)
commandRst2TeX(conf)
of "jsondoc0":
wantMainModule(conf)
conf.cmd = cmdDoc
loadConfigs(DocConfig, cache, conf)
wantMainModule(conf)
defineSymbol(conf.symbols, "nimdoc")
commandJson(conf)
of "jsondoc2", "jsondoc":
conf.cmd = cmdDoc
loadConfigs(DocConfig, cache, conf)
wantMainModule(conf)
defineSymbol(conf.symbols, "nimdoc")
commandDoc2(graph, cache, true)
of "ctags":
wantMainModule()
gCmd = cmdDoc
loadConfigs(DocConfig, cache)
wantMainModule()
defineSymbol("nimdoc")
commandTags()
wantMainModule(conf)
conf.cmd = cmdDoc
loadConfigs(DocConfig, cache, conf)
defineSymbol(conf.symbols, "nimdoc")
commandTags(conf)
of "buildindex":
gCmd = cmdDoc
loadConfigs(DocConfig, cache)
commandBuildIndex()
conf.cmd = cmdDoc
loadConfigs(DocConfig, cache, conf)
commandBuildIndex(conf)
of "gendepend":
gCmd = cmdGenDepend
conf.cmd = cmdGenDepend
commandGenDepend(graph, cache)
of "dump":
gCmd = cmdDump
if getConfigVar("dump.format") == "json":
wantMainModule()
conf.cmd = cmdDump
if getConfigVar(conf, "dump.format") == "json":
wantMainModule(conf)
var definedSymbols = newJArray()
for s in definedSymbolNames(): definedSymbols.elems.add(%s)
for s in definedSymbolNames(conf.symbols): definedSymbols.elems.add(%s)
var libpaths = newJArray()
for dir in searchPaths: libpaths.elems.add(%dir)
for dir in conf.searchPaths: libpaths.elems.add(%dir)
var dumpdata = % [
(key: "version", val: %VersionAsString),
(key: "project_path", val: %gProjectFull),
(key: "project_path", val: %conf.projectFull),
(key: "defined_symbols", val: definedSymbols),
(key: "lib_paths", val: libpaths)
]
msgWriteln($dumpdata, {msgStdout, msgSkipHook})
msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook})
else:
msgWriteln("-- list of currently defined symbols --",
msgWriteln(conf, "-- list of currently defined symbols --",
{msgStdout, msgSkipHook})
for s in definedSymbolNames(): msgWriteln(s, {msgStdout, msgSkipHook})
msgWriteln("-- end of list --", {msgStdout, msgSkipHook})
for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook})
msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook})
for it in searchPaths: msgWriteln(it)
for it in conf.searchPaths: msgWriteln(conf, it)
of "check":
gCmd = cmdCheck
conf.cmd = cmdCheck
commandCheck(graph, cache)
of "parse":
gCmd = cmdParse
wantMainModule()
discard parseFile(gProjectMainIdx, cache)
conf.cmd = cmdParse
wantMainModule(conf)
discard parseFile(FileIndex conf.projectMainIdx, cache, conf)
of "scan":
gCmd = cmdScan
wantMainModule()
commandScan(cache)
msgWriteln("Beware: Indentation tokens depend on the parser's state!")
conf.cmd = cmdScan
wantMainModule(conf)
commandScan(cache, conf)
msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!")
of "secret":
gCmd = cmdInteractive
conf.cmd = cmdInteractive
commandInteractive(graph, cache)
of "e":
commandEval(graph, cache, mainCommandArg())
commandEval(graph, cache, mainCommandArg(conf))
of "nop", "help":
# prevent the "success" message:
gCmd = cmdDump
conf.cmd = cmdDump
of "jsonscript":
gCmd = cmdJsonScript
conf.cmd = cmdJsonScript
commandJsonScript(graph, cache)
else:
rawMessage(errInvalidCommandX, command)
rawMessage(conf, errGenerated, "invalid command: " & conf.command)
if msgs.gErrorCounter == 0 and
gCmd notin {cmdInterpret, cmdRun, cmdDump}:
if conf.errorCounter == 0 and
conf.cmd notin {cmdInterpret, cmdRun, cmdDump}:
when declared(system.getMaxMem):
let usedMem = formatSize(getMaxMem()) & " peakmem"
else:
let usedMem = formatSize(getTotalMem())
rawMessage(hintSuccessX, [$gLinesCompiled,
formatFloat(epochTime() - gLastCmdTime, ffDecimal, 3),
rawMessage(conf, hintSuccessX, [$conf.linesCompiled,
formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3),
usedMem,
if condSyms.isDefined("release"): "Release Build"
if isDefined(conf, "release"): "Release Build"
else: "Debug Build"])
when PrintRopeCacheStats:
@ -308,9 +301,6 @@ proc mainCommand*(graph: ModuleGraph; cache: IdentCache) =
echo " efficiency: ", formatFloat(1-(gCacheMisses.float/gCacheTries.float),
ffDecimal, 3)
when SimulateCaasMemReset:
resetMemory()
resetAttributes(conf)
resetAttributes()
proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache())
#proc mainCommand*() = mainCommand(newModuleGraph(newConfigRef()), newIdentCache())

View file

@ -25,7 +25,7 @@
## - Its dependent module stays the same.
##
import ast, intsets, tables, options
import ast, intsets, tables, options, rod, msgs, hashes, idents
type
ModuleGraph* = ref object
@ -34,67 +34,88 @@ type
deps*: IntSet # the dependency graph or potentially its transitive closure.
suggestMode*: bool # whether we are in nimsuggest mode or not.
invalidTransitiveClosure: bool
inclToMod*: Table[int32, int32] # mapping of include file to the
# first module that included it
importStack*: seq[int32] # The current import stack. Used for detecting recursive
# module dependencies.
inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the
# first module that included it
importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive
# module dependencies.
backend*: RootRef # minor hack so that a backend can extend this easily
config*: ConfigRef
doStopCompile*: proc(): bool {.closure.}
usageSym*: PSym # for nimsuggest
owners*: seq[PSym]
methods*: seq[tuple[methods: TSymSeq, dispatcher: PSym]]
systemModule*: PSym
sysTypes*: array[TTypeKind, PType]
compilerprocs*: TStrTable
exposed*: TStrTable
intTypeCache*: array[-5..64, PType]
opContains*, opNot*: PSym
proc hash*(x: FileIndex): Hash {.borrow.}
{.this: g.}
proc stopCompile*(g: ModuleGraph): bool {.inline.} =
result = doStopCompile != nil and doStopCompile()
proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym =
result = newSym(skProc, getIdent(name), nil, unknownLineInfo(), {})
result.magic = m
proc newModuleGraph*(config: ConfigRef = nil): ModuleGraph =
result = ModuleGraph()
initStrTable(result.packageSyms)
result.deps = initIntSet()
result.modules = @[]
result.importStack = @[]
result.inclToMod = initTable[int32, int32]()
result.inclToMod = initTable[FileIndex, FileIndex]()
if config.isNil:
result.config = newConfigRef()
else:
result.config = config
result.owners = @[]
result.methods = @[]
initStrTable(result.compilerprocs)
initStrTable(result.exposed)
result.opNot = createMagic(result, "not", mNot)
result.opContains = createMagic(result, "contains", mInSet)
proc resetAllModules*(g: ModuleGraph) =
initStrTable(packageSyms)
deps = initIntSet()
modules = @[]
importStack = @[]
inclToMod = initTable[int32, int32]()
inclToMod = initTable[FileIndex, FileIndex]()
usageSym = nil
owners = @[]
methods = @[]
initStrTable(compilerprocs)
initStrTable(exposed)
proc getModule*(g: ModuleGraph; fileIdx: int32): PSym =
if fileIdx >= 0 and fileIdx < modules.len:
result = modules[fileIdx]
proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len:
result = modules[fileIdx.int32]
proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b
proc addDep*(g: ModuleGraph; m: PSym, dep: int32) =
proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) =
assert m.position == m.info.fileIndex.int32
addModuleDep(m.info.fileIndex, dep, isIncludeFile = false)
if suggestMode:
deps.incl m.position.dependsOn(dep)
deps.incl m.position.dependsOn(dep.int)
# we compute the transitive closure later when quering the graph lazily.
# this improve efficiency quite a lot:
#invalidTransitiveClosure = true
proc addIncludeDep*(g: ModuleGraph; module, includeFile: int32) =
proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) =
addModuleDep(module, includeFile, isIncludeFile = true)
discard hasKeyOrPut(inclToMod, includeFile, module)
proc parentModule*(g: ModuleGraph; fileIdx: int32): int32 =
proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex =
## returns 'fileIdx' if the file belonging to this index is
## directly used as a module or else the module that first
## references this include file.
if fileIdx >= 0 and fileIdx < modules.len and modules[fileIdx] != nil:
if fileIdx.int32 >= 0 and fileIdx.int32 < modules.len and modules[fileIdx.int32] != nil:
result = fileIdx
else:
result = inclToMod.getOrDefault(fileIdx)
@ -108,11 +129,11 @@ proc transitiveClosure(g: var IntSet; n: int) =
if g.contains(i.dependsOn(k)) and g.contains(k.dependsOn(j)):
g.incl i.dependsOn(j)
proc markDirty*(g: ModuleGraph; fileIdx: int32) =
proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) =
let m = getModule fileIdx
if m != nil: incl m.flags, sfDirty
proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) =
proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
# we need to mark its dependent modules D as dirty right away because after
# nimsuggest is done with this module, the module's dirty flag will be
# cleared but D still needs to be remembered as 'dirty'.
@ -123,7 +144,7 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: int32) =
# every module that *depends* on this file is also dirty:
for i in 0i32..<modules.len.int32:
let m = modules[i]
if m != nil and deps.contains(i.dependsOn(fileIdx)):
if m != nil and deps.contains(i.dependsOn(fileIdx.int)):
incl m.flags, sfDirty
proc isDirty*(g: ModuleGraph; m: PSym): bool =

View file

@ -11,38 +11,39 @@ import ast, renderer, strutils, msgs, options, idents, os
import nimblecmd
const
considerParentDirs = not defined(noParentProjects)
considerNimbleDirs = not defined(noNimbleDirs)
when false:
const
considerParentDirs = not defined(noParentProjects)
considerNimbleDirs = not defined(noNimbleDirs)
proc findInNimbleDir(pkg, subdir, dir: string): string =
var best = ""
var bestv = ""
for k, p in os.walkDir(dir, relative=true):
if k == pcDir and p.len > pkg.len+1 and
p[pkg.len] == '-' and p.startsWith(pkg):
let (_, a) = getPathVersion(p)
if bestv.len == 0 or bestv < a:
bestv = a
best = dir / p
proc findInNimbleDir(pkg, subdir, dir: string): string =
var best = ""
var bestv = ""
for k, p in os.walkDir(dir, relative=true):
if k == pcDir and p.len > pkg.len+1 and
p[pkg.len] == '-' and p.startsWith(pkg):
let (_, a) = getPathVersion(p)
if bestv.len == 0 or bestv < a:
bestv = a
best = dir / p
if best.len > 0:
var f: File
if open(f, best / changeFileExt(pkg, ".nimble-link")):
# the second line contains what we're interested in, see:
# https://github.com/nim-lang/nimble#nimble-link
var override = ""
discard readLine(f, override)
discard readLine(f, override)
close(f)
if not override.isAbsolute():
best = best / override
else:
best = override
let f = if subdir.len == 0: pkg else: subdir
let res = addFileExt(best / f, "nim")
if best.len > 0 and fileExists(res):
result = res
if best.len > 0:
var f: File
if open(f, best / changeFileExt(pkg, ".nimble-link")):
# the second line contains what we're interested in, see:
# https://github.com/nim-lang/nimble#nimble-link
var override = ""
discard readLine(f, override)
discard readLine(f, override)
close(f)
if not override.isAbsolute():
best = best / override
else:
best = override
let f = if subdir.len == 0: pkg else: subdir
let res = addFileExt(best / f, "nim")
if best.len > 0 and fileExists(res):
result = res
const stdlibDirs = [
"pure", "core", "arch",
@ -51,76 +52,77 @@ const stdlibDirs = [
"wrappers", "wrappers/linenoise",
"windows", "posix", "js"]
proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string =
template attempt(a) =
let x = addFileExt(a, "nim")
if fileExists(x): return x
when false:
proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string =
template attempt(a) =
let x = addFileExt(a, "nim")
if fileExists(x): return x
case pkg
of "stdlib":
if subdir.len == 0:
return options.libpath
case pkg
of "stdlib":
if subdir.len == 0:
return options.libpath
else:
for candidate in stdlibDirs:
attempt(options.libpath / candidate / subdir)
of "root":
let root = project.splitFile.dir
if subdir.len == 0:
return root
else:
attempt(root / subdir)
else:
for candidate in stdlibDirs:
attempt(options.libpath / candidate / subdir)
of "root":
let root = project.splitFile.dir
if subdir.len == 0:
return root
else:
attempt(root / subdir)
else:
when considerParentDirs:
var p = parentDir(source.splitFile.dir)
# support 'import $karax':
let f = if subdir.len == 0: pkg else: subdir
when considerParentDirs:
var p = parentDir(source.splitFile.dir)
# support 'import $karax':
let f = if subdir.len == 0: pkg else: subdir
while p.len > 0:
let dir = p / pkg
if dirExists(dir):
attempt(dir / f)
# 2nd attempt: try to use 'karax/karax'
attempt(dir / pkg / f)
# 3rd attempt: try to use 'karax/src/karax'
attempt(dir / "src" / f)
attempt(dir / "src" / pkg / f)
p = parentDir(p)
while p.len > 0:
let dir = p / pkg
if dirExists(dir):
attempt(dir / f)
# 2nd attempt: try to use 'karax/karax'
attempt(dir / pkg / f)
# 3rd attempt: try to use 'karax/src/karax'
attempt(dir / "src" / f)
attempt(dir / "src" / pkg / f)
p = parentDir(p)
when considerNimbleDirs:
if not options.gNoNimblePath:
var nimbleDir = getEnv("NIMBLE_DIR")
if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble"
result = findInNimbleDir(pkg, subdir, nimbleDir / "pkgs")
if result.len > 0: return result
when not defined(windows):
result = findInNimbleDir(pkg, subdir, "/opt/nimble/pkgs")
when considerNimbleDirs:
if not options.gNoNimblePath:
var nimbleDir = getEnv("NIMBLE_DIR")
if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble"
result = findInNimbleDir(pkg, subdir, nimbleDir / "pkgs")
if result.len > 0: return result
when not defined(windows):
result = findInNimbleDir(pkg, subdir, "/opt/nimble/pkgs")
if result.len > 0: return result
proc scriptableImport(pkg, sub: string; info: TLineInfo): string =
result = resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info)
if result.isNil: result = ""
proc scriptableImport(pkg, sub: string; info: TLineInfo): string =
result = resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info)
if result.isNil: result = ""
proc lookupPackage(pkg, subdir: PNode): string =
let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
case pkg.kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
result = scriptableImport(pkg.strVal, sub, pkg.info)
of nkIdent:
result = scriptableImport(pkg.ident.s, sub, pkg.info)
else:
localError(pkg.info, "package name must be an identifier or string literal")
result = ""
proc lookupPackage(pkg, subdir: PNode): string =
let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: ""
case pkg.kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
result = scriptableImport(pkg.strVal, sub, pkg.info)
of nkIdent:
result = scriptableImport(pkg.ident.s, sub, pkg.info)
else:
localError(pkg.info, "package name must be an identifier or string literal")
result = ""
proc getModuleName*(n: PNode): string =
proc getModuleName*(conf: ConfigRef; n: PNode): string =
# This returns a short relative module name without the nim extension
# e.g. like "system", "importer" or "somepath/module"
# The proc won't perform any checks that the path is actually valid
case n.kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
try:
result = pathSubs(n.strVal, n.info.toFullPath().splitFile().dir)
result = pathSubs(conf, n.strVal, n.info.toFullPath().splitFile().dir)
except ValueError:
localError(n.info, "invalid path: " & n.strVal)
localError(conf, n.info, "invalid path: " & n.strVal)
result = n.strVal
of nkIdent:
result = n.ident.s
@ -135,40 +137,51 @@ proc getModuleName*(n: PNode): string =
n.sons[0] = n.sons[1]
n.sons[1] = n.sons[2]
n.sons.setLen(2)
return getModuleName(n.sons[0])
if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
if n0.kind == nkIdent and n0.ident.s == "/":
result = lookupPackage(n1[1], n[2])
else:
localError(n.info, "only '/' supported with $package notation")
result = ""
return getModuleName(conf, n.sons[0])
when false:
if n1.kind == nkPrefix and n1[0].kind == nkIdent and n1[0].ident.s == "$":
if n0.kind == nkIdent and n0.ident.s == "/":
result = lookupPackage(n1[1], n[2])
else:
localError(n.info, "only '/' supported with $package notation")
result = ""
else:
let modname = getModuleName(conf, n[2])
if $n1 == "std":
template attempt(a) =
let x = addFileExt(a, "nim")
if fileExists(x): return x
for candidate in stdlibDirs:
attempt(conf.libpath / candidate / modname)
# hacky way to implement 'x / y /../ z':
result = getModuleName(n1)
result = getModuleName(conf, n1)
result.add renderTree(n0, {renderNoComments})
result.add getModuleName(n[2])
result.add modname
of nkPrefix:
if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$":
result = lookupPackage(n[1], nil)
else:
# hacky way to implement 'x / y /../ z':
result = renderTree(n, {renderNoComments}).replace(" ")
when false:
if n.sons[0].kind == nkIdent and n.sons[0].ident.s == "$":
result = lookupPackage(n[1], nil)
else:
discard
# hacky way to implement 'x / y /../ z':
result = renderTree(n, {renderNoComments}).replace(" ")
of nkDotExpr:
result = renderTree(n, {renderNoComments}).replace(".", "/")
of nkImportAs:
result = getModuleName(n.sons[0])
result = getModuleName(conf, n.sons[0])
else:
localError(n.info, errGenerated, "invalid module name: '$1'" % n.renderTree)
localError(conf, n.info, "invalid module name: '$1'" % n.renderTree)
result = ""
proc checkModuleName*(n: PNode; doLocalError=true): int32 =
proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex =
# This returns the full canonical path for a given module import
let modulename = n.getModuleName
let fullPath = findModule(modulename, n.info.toFullPath)
let modulename = getModuleName(conf, n)
let fullPath = findModule(conf, modulename, n.info.toFullPath)
if fullPath.len == 0:
if doLocalError:
let m = if modulename.len > 0: modulename else: $n
localError(n.info, errCannotOpenFile, m)
localError(conf, n.info, "cannot open file: " & m)
result = InvalidFileIDX
else:
result = fullPath.fileInfoIdx
result = fileInfoIdx(conf, fullPath)

View file

@ -10,131 +10,27 @@
## Implements the module handling, including the caching of modules.
import
ast, astalgo, magicsys, securehash, rodread, msgs, cgendata, sigmatch, options,
idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs
ast, astalgo, magicsys, std / sha1, rodread, msgs, cgendata, sigmatch, options,
idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod,
configuration
when false:
type
TNeedRecompile* = enum Maybe, No, Yes, Probing, Recompiled
THashStatus* = enum hashNotTaken, hashCached, hashHasChanged, hashNotChanged
proc resetSystemArtifacts*(g: ModuleGraph) =
magicsys.resetSysTypes(g)
TModuleInMemory* = object
hash*: SecureHash
deps*: seq[int32] ## XXX: slurped files are currently not tracked
needsRecompile*: TNeedRecompile
hashStatus*: THashStatus
var
gCompiledModules: seq[PSym] = @[]
gMemCacheData*: seq[TModuleInMemory] = @[]
## XXX: we should implement recycling of file IDs
## if the user keeps renaming modules, the file IDs will keep growing
gFuzzyGraphChecking*: bool # nimsuggest uses this. XXX figure out why.
proc hashChanged(fileIdx: int32): bool =
internalAssert fileIdx >= 0 and fileIdx < gMemCacheData.len
template updateStatus =
gMemCacheData[fileIdx].hashStatus = if result: hashHasChanged
else: hashNotChanged
# echo "TESTING Hash: ", fileIdx.toFilename, " ", result
case gMemCacheData[fileIdx].hashStatus
of hashHasChanged:
result = true
of hashNotChanged:
result = false
of hashCached:
let newHash = secureHashFile(fileIdx.toFullPath)
result = newHash != gMemCacheData[fileIdx].hash
gMemCacheData[fileIdx].hash = newHash
updateStatus()
of hashNotTaken:
gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
result = true
updateStatus()
proc doHash(fileIdx: int32) =
if gMemCacheData[fileIdx].hashStatus == hashNotTaken:
# echo "FIRST Hash: ", fileIdx.ToFilename
gMemCacheData[fileIdx].hash = secureHashFile(fileIdx.toFullPath)
proc resetModule*(fileIdx: int32) =
# echo "HARD RESETTING ", fileIdx.toFilename
if fileIdx <% gMemCacheData.len:
gMemCacheData[fileIdx].needsRecompile = Yes
if fileIdx <% gCompiledModules.len:
gCompiledModules[fileIdx] = nil
if fileIdx <% cgendata.gModules.len:
cgendata.gModules[fileIdx] = nil
proc resetModule*(module: PSym) =
let conflict = getModule(module.position.int32)
if conflict == nil: return
doAssert conflict == module
resetModule(module.position.int32)
initStrTable(module.tab)
proc resetAllModules* =
for i in 0..gCompiledModules.high:
if gCompiledModules[i] != nil:
resetModule(i.int32)
resetPackageCache()
# for m in cgenModules(): echo "CGEN MODULE FOUND"
proc resetAllModulesHard* =
resetPackageCache()
gCompiledModules.setLen 0
gMemCacheData.setLen 0
magicsys.resetSysTypes()
# XXX
#gOwners = @[]
proc checkDepMem(fileIdx: int32): TNeedRecompile =
template markDirty =
resetModule(fileIdx)
return Yes
if gFuzzyGraphChecking:
if gMemCacheData[fileIdx].needsRecompile != Maybe:
return gMemCacheData[fileIdx].needsRecompile
else:
# cycle detection: We claim that a cycle does no harm.
if gMemCacheData[fileIdx].needsRecompile == Probing:
return No
if optForceFullMake in gGlobalOptions or hashChanged(fileIdx):
markDirty()
if gMemCacheData[fileIdx].deps != nil:
gMemCacheData[fileIdx].needsRecompile = Probing
for dep in gMemCacheData[fileIdx].deps:
let d = checkDepMem(dep)
if d in {Yes, Recompiled}:
# echo fileIdx.toFilename, " depends on ", dep.toFilename, " ", d
markDirty()
gMemCacheData[fileIdx].needsRecompile = No
return No
proc resetSystemArtifacts*() =
magicsys.resetSysTypes()
proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
# We cannot call ``newSym`` here, because we have to circumvent the ID
# mechanism, which we do in order to assign each module a persistent ID.
new(result)
result.id = - 1 # for better error checking
result.id = -1 # for better error checking
result.kind = skModule
let filename = fileIdx.toFullPath
result.name = getIdent(splitFile(filename).name)
if not isNimIdentifier(result.name.s):
rawMessage(errInvalidModuleName, result.name.s)
rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
result.info = newLineInfo(fileIdx, 1, 1)
let
pck = getPackageName(filename)
pck = getPackageName(graph.config, filename)
pck2 = if pck.len > 0: pck else: "unknown"
pack = getIdent(pck2)
var packSym = graph.packageSyms.strTableGet(pack)
@ -144,9 +40,9 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
graph.packageSyms.strTableAdd(packSym)
result.owner = packSym
result.position = fileIdx
result.position = int fileIdx
growCache graph.modules, fileIdx
growCache graph.modules, int fileIdx
graph.modules[result.position] = result
incl(result.flags, sfUsed)
@ -154,11 +50,11 @@ proc newModule(graph: ModuleGraph; fileIdx: int32): PSym =
strTableAdd(result.tab, result) # a module knows itself
let existing = strTableGet(packSym.tab, result.name)
if existing != nil and existing.info.fileIndex != result.info.fileIndex:
localError(result.info, "module names need to be unique per Nimble package; module clashes with " & existing.info.fileIndex.toFullPath)
localError(graph.config, result.info, "module names need to be unique per Nimble package; module clashes with " & existing.info.fileIndex.toFullPath)
# strTableIncl() for error corrections:
discard strTableIncl(packSym.tab, result)
proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags: TSymFlags): PSym =
proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; cache: IdentCache, flags: TSymFlags): PSym =
result = graph.getModule(fileIdx)
if result == nil:
#growCache gMemCacheData, fileIdx
@ -169,15 +65,17 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags
if sfMainModule in result.flags:
gMainPackageId = result.owner.id
if gCmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
rd = handleSymbolFile(result, cache)
if result.id < 0:
internalError("handleSymbolFile should have set the module's ID")
return
else:
result.id = getID()
when false:
if conf.cmd in {cmdCompileToC, cmdCompileToCpp, cmdCheck, cmdIdeTools}:
rd = handleSymbolFile(result, cache)
if result.id < 0:
internalError("handleSymbolFile should have set the module's ID")
return
else:
discard
result.id = getModuleId(fileIdx, toFullPath(fileIdx))
discard processModule(graph, result,
if sfMainModule in flags and gProjectIsStdin: stdin.llStreamOpen else: nil,
if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil,
rd, cache)
#if optCaasEnabled in gGlobalOptions:
# gMemCacheData[fileIdx].needsRecompile = Recompiled
@ -188,7 +86,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags
initStrTable(result.tab)
result.ast = nil
discard processModule(graph, result,
if sfMainModule in flags and gProjectIsStdin: stdin.llStreamOpen else: nil,
if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil,
nil, cache)
graph.markClientsDirty(fileIdx)
when false:
@ -197,41 +95,44 @@ proc compileModule*(graph: ModuleGraph; fileIdx: int32; cache: IdentCache, flags
else:
result = gCompiledModules[fileIdx]
proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
cache: IdentCache): PSym {.procvar.} =
# this is called by the semantic checking phase
assert graph.config != nil
result = compileModule(graph, fileIdx, cache, {})
graph.addDep(s, fileIdx)
#if sfSystemModule in result.flags:
# localError(result.info, errAttemptToRedefine, result.name.s)
# restore the notes for outer module:
gNotes = if s.owner.id == gMainPackageId: gMainPackageNotes
else: ForeignPackageNotes
graph.config.notes =
if s.owner.id == gMainPackageId: graph.config.mainPackageNotes
else: graph.config.foreignPackageNotes
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
cache: IdentCache): PNode {.procvar.} =
result = syntaxes.parseFile(fileIdx, cache)
result = syntaxes.parseFile(fileIdx, cache, graph.config)
graph.addDep(s, fileIdx)
graph.addIncludeDep(s.position.int32, fileIdx)
graph.addIncludeDep(s.position.FileIndex, fileIdx)
proc compileSystemModule*(graph: ModuleGraph; cache: IdentCache) =
if magicsys.systemModule == nil:
systemFileIdx = fileInfoIdx(options.libpath/"system.nim")
if graph.systemModule == nil:
systemFileIdx = fileInfoIdx(graph.config, graph.config.libpath / "system.nim")
discard graph.compileModule(systemFileIdx, cache, {sfSystemModule})
proc wantMainModule* =
if gProjectFull.len == 0:
fatal(gCmdLineInfo, errCommandExpectsFilename)
gProjectMainIdx = addFileExt(gProjectFull, NimExt).fileInfoIdx
proc wantMainModule*(conf: ConfigRef) =
if conf.projectFull.len == 0:
fatal(conf, newLineInfo(conf, "command line", 1, 1), errGenerated, "command expects a filename")
conf.projectMainIdx = int32 fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
passes.gIncludeFile = includeModule
passes.gImportModule = importModule
proc compileProject*(graph: ModuleGraph; cache: IdentCache;
projectFileIdx = -1'i32) =
wantMainModule()
let systemFileIdx = fileInfoIdx(options.libpath / "system.nim")
let projectFile = if projectFileIdx < 0: gProjectMainIdx else: projectFileIdx
projectFileIdx = InvalidFileIDX) =
let conf = graph.config
wantMainModule(conf)
let systemFileIdx = fileInfoIdx(conf, conf.libpath / "system.nim")
let projectFile = if projectFileIdx == InvalidFileIDX: FileIndex(conf.projectMainIdx) else: projectFileIdx
graph.importStack.add projectFile
if projectFile == systemFileIdx:
discard graph.compileModule(projectFile, cache, {sfMainModule, sfSystemModule})
@ -240,7 +141,7 @@ proc compileProject*(graph: ModuleGraph; cache: IdentCache;
discard graph.compileModule(projectFile, cache, {sfMainModule})
proc makeModule*(graph: ModuleGraph; filename: string): PSym =
result = graph.newModule(fileInfoIdx filename)
result = graph.newModule(fileInfoIdx(graph.config, filename))
result.id = getID()
proc makeStdinModule*(graph: ModuleGraph): PSym = graph.makeModule"stdin"

File diff suppressed because it is too large Load diff

View file

@ -21,7 +21,7 @@ when defined(i386) and defined(windows) and defined(vcc):
import
commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
extccomp, strutils, os, osproc, platform, main, parseopt, service,
nodejs, scriptconfig, idents, modulegraphs
nodejs, scriptconfig, idents, modulegraphs, configuration
when hasTinyCBackend:
import tccgen
@ -37,77 +37,70 @@ proc prependCurDir(f: string): string =
else:
result = f
proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
condsyms.initDefines(conf.symbols)
if paramCount() == 0:
writeCommandLineUsage()
writeCommandLineUsage(conf, conf.helpWritten)
else:
# Process command line arguments:
processCmdLine(passCmd1, "")
if gProjectName == "-":
gProjectName = "stdinfile"
gProjectFull = "stdinfile"
gProjectPath = canonicalizePath getCurrentDir()
gProjectIsStdin = true
elif gProjectName != "":
processCmdLine(passCmd1, "", conf)
if conf.projectName == "-":
conf.projectName = "stdinfile"
conf.projectFull = "stdinfile"
conf.projectPath = canonicalizePath(conf, getCurrentDir())
conf.projectIsStdin = true
elif conf.projectName != "":
try:
gProjectFull = canonicalizePath(gProjectName)
conf.projectFull = canonicalizePath(conf, conf.projectName)
except OSError:
gProjectFull = gProjectName
let p = splitFile(gProjectFull)
conf.projectFull = conf.projectName
let p = splitFile(conf.projectFull)
let dir = if p.dir.len > 0: p.dir else: getCurrentDir()
gProjectPath = canonicalizePath dir
gProjectName = p.name
conf.projectPath = canonicalizePath(conf, dir)
conf.projectName = p.name
else:
gProjectPath = canonicalizePath getCurrentDir()
loadConfigs(DefaultConfig, config) # load all config files
let scriptFile = gProjectFull.changeFileExt("nims")
conf.projectPath = canonicalizePath(conf, getCurrentDir())
loadConfigs(DefaultConfig, conf) # load all config files
let scriptFile = conf.projectFull.changeFileExt("nims")
if fileExists(scriptFile):
runNimScript(cache, scriptFile, freshDefines=false, config)
runNimScript(cache, scriptFile, freshDefines=false, conf)
# 'nim foo.nims' means to just run the NimScript file and do nothing more:
if scriptFile == gProjectFull: return
elif fileExists(gProjectPath / "config.nims"):
if scriptFile == conf.projectFull: return
elif fileExists(conf.projectPath / "config.nims"):
# directory wide NimScript file
runNimScript(cache, gProjectPath / "config.nims", freshDefines=false, config)
runNimScript(cache, conf.projectPath / "config.nims", freshDefines=false, conf)
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars()
processCmdLine(passCmd2, "")
if options.command == "":
rawMessage(errNoCommand, command)
mainCommand(newModuleGraph(config), cache)
if optHints in gOptions and hintGCStats in gNotes: echo(GC_getStatistics())
extccomp.initVars(conf)
processCmdLine(passCmd2, "", conf)
if conf.command == "":
rawMessage(conf, errGenerated, "command missing")
mainCommand(newModuleGraph(conf), cache)
if optHints in conf.options and hintGCStats in conf.notes: echo(GC_getStatistics())
#echo(GC_getStatistics())
if msgs.gErrorCounter == 0:
if conf.errorCounter == 0:
when hasTinyCBackend:
if gCmd == cmdRun:
tccgen.run(commands.arguments)
if optRun in gGlobalOptions:
if gCmd == cmdCompileToJS:
if conf.cmd == cmdRun:
tccgen.run(conf.arguments)
if optRun in conf.globalOptions:
if conf.cmd == cmdCompileToJS:
var ex: string
if options.outFile.len > 0:
ex = options.outFile.prependCurDir.quoteShell
if conf.outFile.len > 0:
ex = conf.outFile.prependCurDir.quoteShell
else:
ex = quoteShell(
completeCFilePath(changeFileExt(gProjectFull, "js").prependCurDir))
execExternalProgram(findNodeJs() & " " & ex & ' ' & commands.arguments)
elif gCmd == cmdCompileToPHP:
var ex: string
if options.outFile.len > 0:
ex = options.outFile.prependCurDir.quoteShell
else:
ex = quoteShell(
completeCFilePath(changeFileExt(gProjectFull, "php").prependCurDir))
execExternalProgram("php " & ex & ' ' & commands.arguments)
completeCFilePath(conf, changeFileExt(conf.projectFull, "js").prependCurDir))
execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments)
else:
var binPath: string
if options.outFile.len > 0:
if conf.outFile.len > 0:
# If the user specified an outFile path, use that directly.
binPath = options.outFile.prependCurDir
binPath = conf.outFile.prependCurDir
else:
# Figure out ourselves a valid binary name.
binPath = changeFileExt(gProjectFull, ExeExt).prependCurDir
binPath = changeFileExt(conf.projectFull, ExeExt).prependCurDir
var ex = quoteShell(binPath)
execExternalProgram(ex & ' ' & commands.arguments)
execExternalProgram(conf, ex & ' ' & conf.arguments)
when declared(GC_setMaxPause):
GC_setMaxPause 2_000
@ -115,10 +108,10 @@ when declared(GC_setMaxPause):
when compileOption("gc", "v2") or compileOption("gc", "refc"):
# the new correct mark&sweet collector is too slow :-/
GC_disableMarkAndSweep()
condsyms.initDefines()
when not defined(selftest):
handleCmdLine(newIdentCache(), newConfigRef())
let conf = newConfigRef()
handleCmdLine(newIdentCache(), conf)
when declared(GC_setMaxPause):
echo GC_getStatistics()
msgQuit(int8(msgs.gErrorCounter > 0))
msgQuit(int8(conf.errorCounter > 0))

View file

@ -9,11 +9,12 @@
## Implements some helper procs for Nimble (Nim's package manager) support.
import parseutils, strutils, strtabs, os, options, msgs, sequtils
import parseutils, strutils, strtabs, os, options, msgs, sequtils,
configuration
proc addPath*(path: string, info: TLineInfo) =
if not options.searchPaths.contains(path):
options.searchPaths.insert(path, 0)
proc addPath*(conf: ConfigRef; path: string, info: TLineInfo) =
if not conf.searchPaths.contains(path):
conf.searchPaths.insert(path, 0)
type
Version* = distinct string
@ -84,7 +85,7 @@ proc getPathVersion*(p: string): tuple[name, version: string] =
result.name = p[0 .. sepIdx - 1]
result.version = p.substr(sepIdx + 1)
proc addPackage(packages: StringTableRef, p: string; info: TLineInfo) =
proc addPackage(conf: ConfigRef; packages: StringTableRef, p: string; info: TLineInfo) =
let (name, ver) = getPathVersion(p)
if isValidVersion(ver):
let version = newVersion(ver)
@ -92,14 +93,14 @@ proc addPackage(packages: StringTableRef, p: string; info: TLineInfo) =
(not packages.hasKey(name)):
packages[name] = $version
else:
localError(info, "invalid package name: " & p)
localError(conf, info, "invalid package name: " & p)
iterator chosen(packages: StringTableRef): string =
for key, val in pairs(packages):
let res = if val.len == 0: key else: key & '-' & val
yield res
proc addNimblePath(p: string, info: TLineInfo) =
proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) =
var path = p
let nimbleLinks = toSeq(walkPattern(p / "*.nimble-link"))
if nimbleLinks.len > 0:
@ -111,23 +112,23 @@ proc addNimblePath(p: string, info: TLineInfo) =
if not path.isAbsolute():
path = p / path
if not contains(options.searchPaths, path):
message(info, hintPath, path)
options.lazyPaths.insert(path, 0)
if not contains(conf.searchPaths, path):
message(conf, info, hintPath, path)
conf.lazyPaths.insert(path, 0)
proc addPathRec(dir: string, info: TLineInfo) =
proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) =
var packages = newStringTable(modeStyleInsensitive)
var pos = dir.len-1
if dir[pos] in {DirSep, AltSep}: inc(pos)
for k,p in os.walkDir(dir):
if k == pcDir and p[pos] != '.':
addPackage(packages, p, info)
addPackage(conf, packages, p, info)
for p in packages.chosen:
addNimblePath(p, info)
addNimblePath(conf, p, info)
proc nimblePath*(path: string, info: TLineInfo) =
addPathRec(path, info)
addNimblePath(path, info)
proc nimblePath*(conf: ConfigRef; path: string, info: TLineInfo) =
addPathRec(conf, path, info)
addNimblePath(conf, path, info)
when isMainModule:
proc v(s: string): Version = s.newVersion

View file

@ -11,7 +11,7 @@
import
llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
options, idents, wordrecg, strtabs
options, idents, wordrecg, strtabs, configuration
# ---------------- configuration file parser -----------------------------
# we use Nim's scanner here to save space and work
@ -27,12 +27,12 @@ proc parseAtom(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
ppGetTok(L, tok)
result = parseExpr(L, tok, config)
if tok.tokType == tkParRi: ppGetTok(L, tok)
else: lexMessage(L, errTokenExpected, "\')\'")
else: lexMessage(L, errGenerated, "expected closing ')'")
elif tok.ident.id == ord(wNot):
ppGetTok(L, tok)
result = not parseAtom(L, tok, config)
else:
result = isDefined(tok.ident)
result = isDefined(config, tok.ident.s)
ppGetTok(L, tok)
proc parseAndExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
@ -53,12 +53,12 @@ proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool =
ppGetTok(L, tok) # skip 'if' or 'elif'
result = parseExpr(L, tok, config)
if tok.tokType == tkColon: ppGetTok(L, tok)
else: lexMessage(L, errTokenExpected, "\':\'")
else: lexMessage(L, errGenerated, "expected ':'")
var condStack: seq[bool] = @[]
#var condStack: seq[bool] = @[]
proc doEnd(L: var TLexer, tok: var TToken) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
proc doEnd(L: var TLexer, tok: var TToken; condStack: var seq[bool]) =
if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
ppGetTok(L, tok) # skip 'end'
setLen(condStack, high(condStack))
@ -66,20 +66,22 @@ type
TJumpDest = enum
jdEndif, jdElseEndif
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef)
proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef;
condStack: var seq[bool])
proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
ppGetTok(L, tok)
if tok.tokType == tkColon: ppGetTok(L, tok)
if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config)
if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config, condStack)
proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef) =
if high(condStack) < 0: lexMessage(L, errTokenExpected, "@if")
proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if")
var res = evalppIf(L, tok, config)
if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config)
if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config, condStack)
else: condStack[high(condStack)] = true
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef) =
proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef;
condStack: var seq[bool]) =
var nestedIfs = 0
while true:
if tok.ident != nil and tok.ident.s == "@":
@ -89,39 +91,39 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: Co
inc(nestedIfs)
of wElse:
if dest == jdElseEndif and nestedIfs == 0:
doElse(L, tok, config)
doElse(L, tok, config, condStack)
break
of wElif:
if dest == jdElseEndif and nestedIfs == 0:
doElif(L, tok, config)
doElif(L, tok, config, condStack)
break
of wEnd:
if nestedIfs == 0:
doEnd(L, tok)
doEnd(L, tok, condStack)
break
if nestedIfs > 0: dec(nestedIfs)
else:
discard
ppGetTok(L, tok)
elif tok.tokType == tkEof:
lexMessage(L, errTokenExpected, "@end")
lexMessage(L, errGenerated, "expected @end")
else:
ppGetTok(L, tok)
proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) =
proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
ppGetTok(L, tok) # skip @
case whichKeyword(tok.ident)
of wIf:
setLen(condStack, len(condStack) + 1)
let res = evalppIf(L, tok, config)
condStack[high(condStack)] = res
if not res: jumpToDirective(L, tok, jdElseEndif, config)
of wElif: doElif(L, tok, config)
of wElse: doElse(L, tok, config)
of wEnd: doEnd(L, tok)
if not res: jumpToDirective(L, tok, jdElseEndif, config, condStack)
of wElif: doElif(L, tok, config, condStack)
of wElse: doElse(L, tok, config, condStack)
of wEnd: doEnd(L, tok, condStack)
of wWrite:
ppGetTok(L, tok)
msgs.msgWriteln(strtabs.`%`(tokToStr(tok), options.gConfigVars,
msgs.msgWriteln(config, strtabs.`%`(tokToStr(tok), config.configVars,
{useEnvironment, useKey}))
ppGetTok(L, tok)
else:
@ -144,60 +146,63 @@ proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef) =
ppGetTok(L, tok)
os.putEnv(key, os.getEnv(key) & tokToStr(tok))
ppGetTok(L, tok)
else: lexMessage(L, errInvalidDirectiveX, tokToStr(tok))
else:
lexMessage(L, errGenerated, "invalid directive: '$1'" % tokToStr(tok))
proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef) =
proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) =
ppGetTok(L, tok)
while tok.ident != nil and tok.ident.s == "@":
parseDirective(L, tok, config) # else: give the token to the parser
parseDirective(L, tok, config, condStack) # else: give the token to the parser
proc checkSymbol(L: TLexer, tok: TToken) =
if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}:
lexMessage(L, errIdentifierExpected, tokToStr(tok))
lexMessage(L, errGenerated, "expected identifier, but got: " & tokToStr(tok))
proc parseAssignment(L: var TLexer, tok: var TToken; config: ConfigRef) =
proc parseAssignment(L: var TLexer, tok: var TToken;
config: ConfigRef; condStack: var seq[bool]) =
if tok.ident.s == "-" or tok.ident.s == "--":
confTok(L, tok, config) # skip unnecessary prefix
confTok(L, tok, config, condStack) # skip unnecessary prefix
var info = getLineInfo(L, tok) # save for later in case of an error
checkSymbol(L, tok)
var s = tokToStr(tok)
confTok(L, tok, config) # skip symbol
confTok(L, tok, config, condStack) # skip symbol
var val = ""
while tok.tokType == tkDot:
add(s, '.')
confTok(L, tok, config)
confTok(L, tok, config, condStack)
checkSymbol(L, tok)
add(s, tokToStr(tok))
confTok(L, tok, config)
confTok(L, tok, config, condStack)
if tok.tokType == tkBracketLe:
# BUGFIX: val, not s!
# BUGFIX: do not copy '['!
confTok(L, tok, config)
confTok(L, tok, config, condStack)
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok, config)
if tok.tokType == tkBracketRi: confTok(L, tok, config)
else: lexMessage(L, errTokenExpected, "']'")
confTok(L, tok, config, condStack)
if tok.tokType == tkBracketRi: confTok(L, tok, config, condStack)
else: lexMessage(L, errGenerated, "expected closing ']'")
add(val, ']')
let percent = tok.ident != nil and tok.ident.s == "%="
if tok.tokType in {tkColon, tkEquals} or percent:
if len(val) > 0: add(val, ':')
confTok(L, tok, config) # skip ':' or '=' or '%'
confTok(L, tok, config, condStack) # skip ':' or '=' or '%'
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok, config) # skip symbol
confTok(L, tok, config, condStack) # skip symbol
while tok.ident != nil and tok.ident.s == "&":
confTok(L, tok, config)
confTok(L, tok, config, condStack)
checkSymbol(L, tok)
add(val, tokToStr(tok))
confTok(L, tok, config)
confTok(L, tok, config, condStack)
if percent:
processSwitch(s, strtabs.`%`(val, options.gConfigVars,
processSwitch(s, strtabs.`%`(val, config.configVars,
{useEnvironment, useEmpty}), passPP, info, config)
else:
processSwitch(s, val, passPP, info, config)
proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) =
proc readConfigFile(
filename: string; cache: IdentCache; config: ConfigRef): bool =
var
L: TLexer
tok: TToken
@ -205,54 +210,61 @@ proc readConfigFile(filename: string; cache: IdentCache; config: ConfigRef) =
stream = llStreamOpen(filename, fmRead)
if stream != nil:
initToken(tok)
openLexer(L, filename, stream, cache)
openLexer(L, filename, stream, cache, config)
tok.tokType = tkEof # to avoid a pointless warning
confTok(L, tok, config) # read in the first token
while tok.tokType != tkEof: parseAssignment(L, tok, config)
if len(condStack) > 0: lexMessage(L, errTokenExpected, "@end")
var condStack: seq[bool] = @[]
confTok(L, tok, config, condStack) # read in the first token
while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack)
if len(condStack) > 0: lexMessage(L, errGenerated, "expected @end")
closeLexer(L)
rawMessage(hintConf, filename)
return true
proc getUserConfigPath(filename: string): string =
result = joinPath(getConfigDir(), filename)
proc getSystemConfigPath(filename: string): string =
proc getSystemConfigPath(conf: ConfigRef; filename: string): string =
# try standard configuration file (installation did not distribute files
# the UNIX way)
let p = getPrefixDir()
let p = getPrefixDir(conf)
result = joinPath([p, "config", filename])
when defined(unix):
if not existsFile(result): result = joinPath([p, "etc", filename])
if not existsFile(result): result = "/etc/" & filename
proc loadConfigs*(cfg: string; cache: IdentCache; config: ConfigRef = nil) =
setDefaultLibpath()
proc loadConfigs*(cfg: string; cache: IdentCache; conf: ConfigRef) =
setDefaultLibpath(conf)
var configFiles = newSeq[string]()
if optSkipConfigFile notin gGlobalOptions:
readConfigFile(getSystemConfigPath(cfg), cache, config)
template readConfigFile(path: string) =
let configPath = path
if readConfigFile(configPath, cache, conf):
add(configFiles, configPath)
if optSkipUserConfigFile notin gGlobalOptions:
readConfigFile(getUserConfigPath(cfg), cache, config)
if optSkipConfigFile notin conf.globalOptions:
readConfigFile(getSystemConfigPath(conf, cfg))
var pd = if gProjectPath.len > 0: gProjectPath else: getCurrentDir()
if optSkipParentConfigFiles notin gGlobalOptions:
if optSkipUserConfigFile notin conf.globalOptions:
readConfigFile(getUserConfigPath(cfg))
let pd = if conf.projectPath.len > 0: conf.projectPath else: getCurrentDir()
if optSkipParentConfigFiles notin conf.globalOptions:
for dir in parentDirs(pd, fromRoot=true, inclusive=false):
readConfigFile(dir / cfg, cache, config)
readConfigFile(dir / cfg)
if optSkipProjConfigFile notin gGlobalOptions:
readConfigFile(pd / cfg, cache, config)
if optSkipProjConfigFile notin conf.globalOptions:
readConfigFile(pd / cfg)
if gProjectName.len != 0:
if conf.projectName.len != 0:
# new project wide config file:
var projectConfig = changeFileExt(gProjectFull, "nimcfg")
var projectConfig = changeFileExt(conf.projectFull, "nimcfg")
if not fileExists(projectConfig):
projectConfig = changeFileExt(gProjectFull, "nim.cfg")
if not fileExists(projectConfig):
projectConfig = changeFileExt(gProjectFull, "nimrod.cfg")
if fileExists(projectConfig):
rawMessage(warnDeprecated, projectConfig)
readConfigFile(projectConfig, cache, config)
projectConfig = changeFileExt(conf.projectFull, "nim.cfg")
readConfigFile(projectConfig)
proc loadConfigs*(cfg: string; config: ConfigRef = nil) =
for filename in configFiles:
rawMessage(conf, hintConf, filename)
proc loadConfigs*(cfg: string; conf: ConfigRef) =
# for backwards compatibility only.
loadConfigs(cfg, newIdentCache(), config)
loadConfigs(cfg, newIdentCache(), conf)

View file

@ -20,6 +20,7 @@ proc execute*(program: string) =
initDefines()
defineSymbol("nimrodvm")
defineSymbol("nimscript")
when hasFFI: defineSymbol("nimffi")
registerPass(verbosePass)
registerPass(semPass)

View file

@ -38,7 +38,7 @@ In addition, all command line options of Nim are supported.
proc mainCommand =
registerPass verbosePass
registerPass semPass
gCmd = cmdPretty
conf.cmd = cmdPretty
searchPaths.add options.libpath
if gProjectFull.len != 0:
# current path is always looked first for modules
@ -47,7 +47,7 @@ proc mainCommand =
compileProject(newModuleGraph(), newIdentCache())
pretty.overwriteFiles()
proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) =
var p = parseopt.initOptParser(cmd)
var argsCount = 0
gOnlyMainfile = true
@ -76,16 +76,16 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) =
of "wholeproject": gOnlyMainfile = false
of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error
else:
processSwitch(pass, p)
processSwitch(pass, p, config)
of cmdArgument:
options.gProjectName = unixToNativePath(p.key)
# if processArgument(pass, p, argsCount): break
proc handleCmdLine() =
proc handleCmdLine(config: ConfigRef) =
if paramCount() == 0:
stdout.writeLine(Usage)
else:
processCmdLine(passCmd1, "")
processCmdLine(passCmd1, "", config)
if gProjectName != "":
try:
gProjectFull = canonicalizePath(gProjectName)
@ -96,11 +96,11 @@ proc handleCmdLine() =
gProjectName = p.name
else:
gProjectPath = getCurrentDir()
loadConfigs(DefaultConfig) # load all config files
loadConfigs(DefaultConfig, config) # load all config files
# now process command line arguments again, because some options in the
# command line can overwite the config file's settings
extccomp.initVars()
processCmdLine(passCmd2, "")
processCmdLine(passCmd2, "", config)
mainCommand()
when compileOption("gc", "v2") or compileOption("gc", "refc"):
@ -108,4 +108,4 @@ when compileOption("gc", "v2") or compileOption("gc", "refc"):
condsyms.initDefines()
defineSymbol "nimfix"
handleCmdline()
handleCmdline newConfigRef()

View file

@ -13,8 +13,9 @@
import
strutils, os, intsets, strtabs
import compiler/options, compiler/ast, compiler/astalgo, compiler/msgs,
compiler/semdata, compiler/nimfix/prettybase, compiler/ropes, compiler/idents
import ".." / [options, ast, astalgo, msgs, semdata, ropes, idents,
configuration]
import prettybase
type
StyleCheck* {.pure.} = enum None, Warn, Auto
@ -24,11 +25,11 @@ var
gStyleCheck*: StyleCheck
gCheckExtern*, gOnlyMainfile*: bool
proc overwriteFiles*() =
let doStrip = options.getConfigVar("pretty.strip").normalize == "on"
proc overwriteFiles*(conf: ConfigRef) =
let doStrip = options.getConfigVar(conf, "pretty.strip").normalize == "on"
for i in 0 .. high(gSourceFiles):
if gSourceFiles[i].dirty and not gSourceFiles[i].isNimfixFile and
(not gOnlyMainfile or gSourceFiles[i].fileIdx == gProjectMainIdx):
(not gOnlyMainfile or gSourceFiles[i].fileIdx == conf.projectMainIdx.FileIndex):
let newFile = if gOverWrite: gSourceFiles[i].fullpath
else: gSourceFiles[i].fullpath.changeFileExt(".pretty.nim")
try:
@ -41,7 +42,7 @@ proc overwriteFiles*() =
f.write(gSourceFiles[i].newline)
f.close
except IOError:
rawMessage(errCannotOpenFile, newFile)
rawMessage(conf, errGenerated, "cannot open file: " & newFile)
proc `=~`(s: string, a: openArray[string]): bool =
for x in a:
@ -95,7 +96,7 @@ proc beautifyName(s: string, k: TSymKind): string =
proc replaceInFile(info: TLineInfo; newName: string) =
loadFile(info)
let line = gSourceFiles[info.fileIndex].lines[info.line-1]
let line = gSourceFiles[info.fileIndex.int].lines[info.line.int-1]
var first = min(info.col.int, line.len)
if first < 0: return
#inc first, skipIgnoreCase(line, "proc ", first)
@ -107,28 +108,28 @@ proc replaceInFile(info: TLineInfo; newName: string) =
if differ(line, first, last, newName):
# last-first+1 != newName.len or
var x = line.substr(0, first-1) & newName & line.substr(last+1)
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
gSourceFiles[info.fileIndex].dirty = true
system.shallowCopy(gSourceFiles[info.fileIndex.int].lines[info.line.int-1], x)
gSourceFiles[info.fileIndex.int].dirty = true
proc checkStyle(info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
proc checkStyle(conf: ConfigRef; info: TLineInfo, s: string, k: TSymKind; sym: PSym) =
let beau = beautifyName(s, k)
if s != beau:
if gStyleCheck == StyleCheck.Auto:
sym.name = getIdent(beau)
replaceInFile(info, beau)
else:
message(info, hintName, beau)
message(conf, info, hintName, beau)
proc styleCheckDefImpl(info: TLineInfo; s: PSym; k: TSymKind) =
proc styleCheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) =
# operators stay as they are:
if k in {skResult, skTemp} or s.name.s[0] notin prettybase.Letters: return
if k in {skType, skGenericParam} and sfAnon in s.flags: return
if {sfImportc, sfExportc} * s.flags == {} or gCheckExtern:
checkStyle(info, s.name.s, k, s)
checkStyle(conf, info, s.name.s, k, s)
template styleCheckDef*(info: TLineInfo; s: PSym; k: TSymKind) =
when defined(nimfix):
if gStyleCheck != StyleCheck.None: styleCheckDefImpl(info, s, k)
if gStyleCheck != StyleCheck.None: styleCheckDefImpl(conf, info, s, k)
template styleCheckDef*(info: TLineInfo; s: PSym) =
styleCheckDef(info, s, s.kind)
@ -136,7 +137,7 @@ template styleCheckDef*(s: PSym) =
styleCheckDef(s.info, s, s.kind)
proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
if info.fileIndex < 0: return
if info.fileIndex.int < 0: return
# we simply convert it to what it looks like in the definition
# for consistency
@ -151,4 +152,4 @@ proc styleCheckUseImpl(info: TLineInfo; s: PSym) =
template styleCheckUse*(info: TLineInfo; s: PSym) =
when defined(nimfix):
if gStyleCheck != StyleCheck.None: styleCheckUseImpl(info, s)
if gStyleCheck != StyleCheck.None: styleCheckUseImpl(conf, info, s)

View file

@ -8,7 +8,7 @@
#
import strutils, lexbase, streams
import compiler/ast, compiler/msgs, compiler/idents
import ".." / [ast, msgs, idents]
from os import splitFile
type
@ -16,13 +16,13 @@ type
lines*: seq[string]
dirty*, isNimfixFile*: bool
fullpath*, newline*: string
fileIdx*: int32
fileIdx*: FileIndex
var
gSourceFiles*: seq[TSourceFile] = @[]
proc loadFile*(info: TLineInfo) =
let i = info.fileIndex
let i = info.fileIndex.int
if i >= gSourceFiles.len:
gSourceFiles.setLen(i+1)
if gSourceFiles[i].lines.isNil:
@ -64,7 +64,7 @@ proc differ*(line: string, a, b: int, x: string): bool =
proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
loadFile(info)
let line = gSourceFiles[info.fileIndex].lines[info.line-1]
let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
var first = min(info.col.int, line.len)
if first < 0: return
#inc first, skipIgnoreCase(line, "proc ", first)
@ -75,8 +75,8 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PIdent) =
let last = first+identLen(line, first)-1
if cmpIgnoreStyle(line[first..last], oldSym.s) == 0:
var x = line.substr(0, first-1) & newSym.s & line.substr(last+1)
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
gSourceFiles[info.fileIndex].dirty = true
system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
gSourceFiles[info.fileIndex.int32].dirty = true
#if newSym.s == "File": writeStackTrace()
proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) =
@ -85,10 +85,10 @@ proc replaceDeprecated*(info: TLineInfo; oldSym, newSym: PSym) =
proc replaceComment*(info: TLineInfo) =
loadFile(info)
let line = gSourceFiles[info.fileIndex].lines[info.line-1]
let line = gSourceFiles[info.fileIndex.int32].lines[info.line.int-1]
var first = info.col.int
if line[first] != '#': inc first
var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape
system.shallowCopy(gSourceFiles[info.fileIndex].lines[info.line-1], x)
gSourceFiles[info.fileIndex].dirty = true
system.shallowCopy(gSourceFiles[info.fileIndex.int32].lines[info.line.int-1], x)
gSourceFiles[info.fileIndex.int32].dirty = true

View file

@ -12,27 +12,10 @@
import
ast, astalgo, trees, nversion, msgs, platform, bitsets, types, renderer
proc toBitSet*(s: PNode, b: var TBitSet)
# this function is used for case statement checking:
proc overlap*(a, b: PNode): bool
proc inSet*(s: PNode, elem: PNode): bool
proc someInSet*(s: PNode, a, b: PNode): bool
proc emptyRange*(a, b: PNode): bool
proc setHasRange*(s: PNode): bool
# returns true if set contains a range (needed by the code generator)
# these are used for constant folding:
proc unionSets*(a, b: PNode): PNode
proc diffSets*(a, b: PNode): PNode
proc intersectSets*(a, b: PNode): PNode
proc symdiffSets*(a, b: PNode): PNode
proc containsSets*(a, b: PNode): bool
proc equalSets*(a, b: PNode): bool
proc cardSet*(s: PNode): BiggestInt
# implementation
proc inSet(s: PNode, elem: PNode): bool =
proc inSet*(s: PNode, elem: PNode): bool =
assert s.kind == nkCurly
if s.kind != nkCurly:
internalError(s.info, "inSet")
#internalError(s.info, "inSet")
return false
for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange:
@ -44,7 +27,7 @@ proc inSet(s: PNode, elem: PNode): bool =
return true
result = false
proc overlap(a, b: PNode): bool =
proc overlap*(a, b: PNode): bool =
if a.kind == nkRange:
if b.kind == nkRange:
# X..Y and C..D overlap iff (X <= D and C <= Y)
@ -58,10 +41,11 @@ proc overlap(a, b: PNode): bool =
else:
result = sameValue(a, b)
proc someInSet(s: PNode, a, b: PNode): bool =
proc someInSet*(s: PNode, a, b: PNode): bool =
# checks if some element of a..b is in the set s
assert s.kind == nkCurly
if s.kind != nkCurly:
internalError(s.info, "SomeInSet")
#internalError(s.info, "SomeInSet")
return false
for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange:
@ -74,7 +58,7 @@ proc someInSet(s: PNode, a, b: PNode): bool =
return true
result = false
proc toBitSet(s: PNode, b: var TBitSet) =
proc toBitSet*(s: PNode, b: var TBitSet) =
var first, j: BiggestInt
first = firstOrd(s.typ.sons[0])
bitSetInit(b, int(getSize(s.typ)))
@ -87,7 +71,7 @@ proc toBitSet(s: PNode, b: var TBitSet) =
else:
bitSetIncl(b, getOrdValue(s.sons[i]) - first)
proc toTreeSet(s: TBitSet, settype: PType, info: TLineInfo): PNode =
proc toTreeSet*(s: TBitSet, settype: PType, info: TLineInfo): PNode =
var
a, b, e, first: BiggestInt # a, b are interval borders
elemType: PType
@ -128,18 +112,18 @@ template nodeSetOp(a, b: PNode, op: untyped) {.dirty.} =
op(x, y)
result = toTreeSet(x, a.typ, a.info)
proc unionSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion)
proc diffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
proc intersectSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
proc symdiffSets(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
proc unionSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetUnion)
proc diffSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetDiff)
proc intersectSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetIntersect)
proc symdiffSets*(a, b: PNode): PNode = nodeSetOp(a, b, bitSetSymDiff)
proc containsSets(a, b: PNode): bool =
proc containsSets*(a, b: PNode): bool =
var x, y: TBitSet
toBitSet(a, x)
toBitSet(b, y)
result = bitSetContains(x, y)
proc equalSets(a, b: PNode): bool =
proc equalSets*(a, b: PNode): bool =
var x, y: TBitSet
toBitSet(a, x)
toBitSet(b, y)
@ -151,26 +135,24 @@ proc complement*(a: PNode): PNode =
for i in countup(0, high(x)): x[i] = not x[i]
result = toTreeSet(x, a.typ, a.info)
proc cardSet(s: PNode): BiggestInt =
# here we can do better than converting it into a compact set
# we just count the elements directly
result = 0
for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange:
result = result + getOrdValue(s.sons[i].sons[1]) -
getOrdValue(s.sons[i].sons[0]) + 1
else:
inc(result)
proc deduplicate*(a: PNode): PNode =
var x: TBitSet
toBitSet(a, x)
result = toTreeSet(x, a.typ, a.info)
proc setHasRange(s: PNode): bool =
proc cardSet*(a: PNode): BiggestInt =
var x: TBitSet
toBitSet(a, x)
result = bitSetCard(x)
proc setHasRange*(s: PNode): bool =
assert s.kind == nkCurly
if s.kind != nkCurly:
internalError(s.info, "SetHasRange")
return false
for i in countup(0, sonsLen(s) - 1):
if s.sons[i].kind == nkRange:
return true
result = false
proc emptyRange(a, b: PNode): bool =
proc emptyRange*(a, b: PNode): bool =
result = not leValue(a, b) # a > b iff not (a <= b)

View file

@ -15,3 +15,6 @@ const
VersionAsString* = system.NimVersion
RodFileVersion* = "1223" # modify this if the rod-format changes!
NimCompilerApiVersion* = 1 ## Check for the existance of this before accessing it
## as older versions of the compiler API do not
## declare this.

View file

@ -8,7 +8,9 @@
#
import
os, strutils, strtabs, osproc, sets
os, strutils, strtabs, osproc, sets, configuration, platform
from terminal import isatty
const
hasTinyCBackend* = defined(tinyc)
@ -17,14 +19,14 @@ const
hasFFI* = defined(useFFI)
newScopeForIf* = true
useCaas* = not defined(noCaas)
noTimeMachine* = defined(avoidTimeMachine) and defined(macosx)
copyrightYear* = "2018"
type # please make sure we have under 32 options
# (improves code efficiency a lot!)
TOption* = enum # **keep binary compatible**
optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
optOverflowCheck, optNilCheck,
optNaNCheck, optInfCheck,
optNaNCheck, optInfCheck, optMoveCheck,
optAssert, optLineDir, optWarns, optHints,
optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
optLineTrace, # line tracing support (includes stack tracing)
@ -35,11 +37,14 @@ type # please make sure we have under 32 options
optImplicitStatic, # optimization: implicit at compile time
# evaluation
optPatterns, # en/disable pattern matching
optMemTracker
optMemTracker,
optHotCodeReloading,
optLaxStrings
TOptions* = set[TOption]
TGlobalOption* = enum # **keep binary compatible**
gloptNone, optForceFullMake, optDeadCodeElim,
gloptNone, optForceFullMake,
optDeadCodeElimUnused, # deprecated, always on
optListCmd, optCompileOnly, optNoLinking,
optCDebug, # turn on debugging information
optGenDynLib, # generate a dynamic library
@ -67,6 +72,11 @@ type # please make sure we have under 32 options
optIdeTerse # idetools: use terse descriptions
optNoCppExceptions # use C exception handling even with CPP
optExcessiveStackTrace # fully qualified module filenames
optWholeProject # for 'doc2': output any dependency
optListFullPaths
optNoNimblePath
optDynlibOverrideAll
optUseNimNamespace
TGlobalOptions* = set[TGlobalOption]
@ -79,7 +89,6 @@ type
# **keep binary compatible**
cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
cmdCompileToJS,
cmdCompileToPHP,
cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc,
cmdGenDepend, cmdDump,
cmdCheck, # semantic checking for whole project
@ -101,67 +110,187 @@ type
ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideMod,
ideHighlight, ideOutline, ideKnown, ideMsg
Feature* = enum ## experimental features
implicitDeref,
dotOperators,
callOperator,
parallel,
destructor,
notnil,
oldIterTransf
SymbolFilesOption* = enum
disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf
ConfigRef* = ref object ## eventually all global configuration should be moved here
linesCompiled*: int # all lines that have been compiled
options*: TOptions
globalOptions*: TGlobalOptions
exitcode*: int8
cmd*: TCommands # the command
selectedGC*: TGCMode # the selected GC
verbosity*: int # how verbose the compiler is
numberOfProcessors*: int # number of processors
evalExpr*: string # expression for idetools --eval
lastCmdTime*: float # when caas is enabled, we measure each command
symbolFiles*: SymbolFilesOption
cppDefines*: HashSet[string]
headerFile*: string
features*: set[Feature]
arguments*: string ## the arguments to be passed to the program that
## should be run
helpWritten*: bool
ideCmd*: IdeCmd
oldNewlines*: bool
enableNotes*: TNoteKinds
disableNotes*: TNoteKinds
foreignPackageNotes*: TNoteKinds
notes*: TNoteKinds
mainPackageNotes*: TNoteKinds
errorCounter*: int
hintCounter*: int
warnCounter*: int
errorMax*: int
configVars*: StringTableRef
symbols*: StringTableRef ## We need to use a StringTableRef here as defined
## symbols are always guaranteed to be style
## insensitive. Otherwise hell would break lose.
packageCache*: StringTableRef
searchPaths*: seq[string]
lazyPaths*: seq[string]
outFile*, prefixDir*, libpath*, nimcacheDir*: string
dllOverrides, moduleOverrides*: StringTableRef
projectName*: string # holds a name like 'nim'
projectPath*: string # holds a path like /home/alice/projects/nim/compiler/
projectFull*: string # projectPath/projectName
projectIsStdin*: bool # whether we're compiling from stdin
projectMainIdx*: int32 # the canonical path id of the main module
command*: string # the main command (e.g. cc, check, scan, etc)
commandArgs*: seq[string] # any arguments after the main command
keepComments*: bool # whether the parser needs to keep comments
implicitImports*: seq[string] # modules that are to be implicitly imported
implicitIncludes*: seq[string] # modules that are to be implicitly included
docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \
# The string uses the formatting variables `path` and `line`.
const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
const
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck,
optMoveCheck}
DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
optBoundsCheck, optOverflowCheck, optAssert, optWarns,
optHints, optStackTrace, optLineTrace,
optPatterns, optNilCheck, optMoveCheck}
DefaultGlobalOptions* = {optThreadAnalysis}
template newPackageCache*(): untyped =
newStringTable(when FileSystemCaseSensitive:
modeCaseInsensitive
else:
modeCaseSensitive)
proc newConfigRef*(): ConfigRef =
result = ConfigRef(cppDefines: initSet[string](),
headerFile: "")
result = ConfigRef(
selectedGC: gcRefc,
verbosity: 1,
options: DefaultOptions,
globalOptions: DefaultGlobalOptions,
evalExpr: "",
cppDefines: initSet[string](),
headerFile: "", features: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic,
hintQuitCalled, hintExecuting},
notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1],
configVars: newStringTable(modeStyleInsensitive),
symbols: newStringTable(modeStyleInsensitive),
packageCache: newPackageCache(),
searchPaths: @[],
lazyPaths: @[],
outFile: "", prefixDir: "", libpath: "", nimcacheDir: "",
dllOverrides: newStringTable(modeCaseInsensitive),
moduleOverrides: newStringTable(modeStyleInsensitive),
projectName: "", # holds a name like 'nim'
projectPath: "", # holds a path like /home/alice/projects/nim/compiler/
projectFull: "", # projectPath/projectName
projectIsStdin: false, # whether we're compiling from stdin
projectMainIdx: 0'i32, # the canonical path id of the main module
command: "", # the main command (e.g. cc, check, scan, etc)
commandArgs: @[], # any arguments after the main command
keepComments: true, # whether the parser needs to keep comments
implicitImports: @[], # modules that are to be implicitly imported
implicitIncludes: @[], # modules that are to be implicitly included
docSeeSrcUrl: "",
arguments: ""
)
# enable colors by default on terminals
if terminal.isatty(stderr):
incl(result.globalOptions, optUseColors)
proc newPartialConfigRef*(): ConfigRef =
## create a new ConfigRef that is only good enough for error reporting.
result = ConfigRef(
selectedGC: gcRefc,
verbosity: 1,
options: DefaultOptions,
globalOptions: DefaultGlobalOptions,
foreignPackageNotes: {hintProcessing, warnUnknownMagic,
hintQuitCalled, hintExecuting},
notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1])
proc cppDefine*(c: ConfigRef; define: string) =
c.cppDefines.incl define
var
gIdeCmd*: IdeCmd
proc isDefined*(conf: ConfigRef; symbol: string): bool =
if conf.symbols.hasKey(symbol):
result = conf.symbols[symbol] != "false"
elif cmpIgnoreStyle(symbol, CPU[targetCPU].name) == 0:
result = true
elif cmpIgnoreStyle(symbol, platform.OS[targetOS].name) == 0:
result = true
else:
case symbol.normalize
of "x86": result = targetCPU == cpuI386
of "itanium": result = targetCPU == cpuIa64
of "x8664": result = targetCPU == cpuAmd64
of "posix", "unix":
result = targetOS in {osLinux, osMorphos, osSkyos, osIrix, osPalmos,
osQnx, osAtari, osAix,
osHaiku, osVxWorks, osSolaris, osNetbsd,
osFreebsd, osOpenbsd, osDragonfly, osMacosx,
osAndroid}
of "linux":
result = targetOS in {osLinux, osAndroid}
of "bsd":
result = targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly}
of "emulatedthreadvars":
result = platform.OS[targetOS].props.contains(ospLacksThreadVars)
of "msdos": result = targetOS == osDos
of "mswindows", "win32": result = targetOS == osWindows
of "macintosh": result = targetOS in {osMacos, osMacosx}
of "sunos": result = targetOS == osSolaris
of "littleendian": result = CPU[targetCPU].endian == platform.littleEndian
of "bigendian": result = CPU[targetCPU].endian == platform.bigEndian
of "cpu8": result = CPU[targetCPU].bit == 8
of "cpu16": result = CPU[targetCPU].bit == 16
of "cpu32": result = CPU[targetCPU].bit == 32
of "cpu64": result = CPU[targetCPU].bit == 64
of "nimrawsetjmp":
result = targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
osDragonfly, osMacosx}
else: discard
const
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck}
proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools}
proc usesNativeGC*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc
var
gOptions*: TOptions = {optObjCheck, optFieldCheck, optRangeCheck,
optBoundsCheck, optOverflowCheck, optAssert, optWarns,
optHints, optStackTrace, optLineTrace,
optPatterns, optNilCheck}
gGlobalOptions*: TGlobalOptions = {optThreadAnalysis}
gExitcode*: int8
gCmd*: TCommands = cmdNone # the command
gSelectedGC* = gcRefc # the selected GC
searchPaths*: seq[string] = @[]
lazyPaths*: seq[string] = @[]
outFile*: string = ""
docSeeSrcUrl*: string = "" # if empty, no seeSrc will be generated. \
# The string uses the formatting variables `path` and `line`.
#headerFile*: string = ""
gVerbosity* = 1 # how verbose the compiler is
gNumberOfProcessors*: int # number of processors
gWholeProject*: bool # for 'doc2': output any dependency
gEvalExpr* = "" # expression for idetools --eval
gLastCmdTime*: float # when caas is enabled, we measure each command
gListFullPaths*: bool
gPreciseStack*: bool = false
gNoNimblePath* = false
gExperimentalMode*: bool
newDestructors*: bool
gDynlibOverrideAll*: bool
type
SymbolFilesOption* = enum
disabledSf, enabledSf, writeOnlySf, readOnlySf
var gSymbolFiles*: SymbolFilesOption
proc importantComments*(): bool {.inline.} = gCmd in {cmdDoc, cmdIdeTools}
proc usesNativeGC*(): bool {.inline.} = gSelectedGC >= gcRefc
template preciseStack*(): bool = gPreciseStack
template compilationCachePresent*: untyped =
gSymbolFiles in {enabledSf, writeOnlySf}
template compilationCachePresent*(conf: ConfigRef): untyped =
conf.symbolFiles in {enabledSf, writeOnlySf}
# {optCaasEnabled, optSymbolFiles} * gGlobalOptions != {}
template optPreserveOrigSource*: untyped =
optEmbedOrigSrc in gGlobalOptions
template optPreserveOrigSource*(conf: ConfigRef): untyped =
optEmbedOrigSrc in conf.globalOptions
const
genSubDir* = "nimcache"
@ -176,82 +305,62 @@ const
DocConfig* = "nimdoc.cfg"
DocTexConfig* = "nimdoc.tex.cfg"
# additional configuration variables:
var
gConfigVars* = newStringTable(modeStyleInsensitive)
gDllOverrides = newStringTable(modeCaseInsensitive)
gModuleOverrides* = newStringTable(modeStyleInsensitive)
gPrefixDir* = "" # Overrides the default prefix dir in getPrefixDir proc.
libpath* = ""
gProjectName* = "" # holds a name like 'nim'
gProjectPath* = "" # holds a path like /home/alice/projects/nim/compiler/
gProjectFull* = "" # projectPath/projectName
gProjectIsStdin* = false # whether we're compiling from stdin
gProjectMainIdx*: int32 # the canonical path id of the main module
nimcacheDir* = ""
command* = "" # the main command (e.g. cc, check, scan, etc)
commandArgs*: seq[string] = @[] # any arguments after the main command
gKeepComments*: bool = true # whether the parser needs to keep comments
implicitImports*: seq[string] = @[] # modules that are to be implicitly imported
implicitIncludes*: seq[string] = @[] # modules that are to be implicitly included
const oKeepVariableNames* = true
template compilingLib*: bool =
template compilingLib*(conf: ConfigRef): bool =
gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}
proc mainCommandArg*: string =
proc mainCommandArg*(conf: ConfigRef): string =
## This is intended for commands like check or parse
## which will work on the main project file unless
## explicitly given a specific file argument
if commandArgs.len > 0:
result = commandArgs[0]
if conf.commandArgs.len > 0:
result = conf.commandArgs[0]
else:
result = gProjectName
result = conf.projectName
proc existsConfigVar*(key: string): bool =
result = hasKey(gConfigVars, key)
proc existsConfigVar*(conf: ConfigRef; key: string): bool =
result = hasKey(conf.configVars, key)
proc getConfigVar*(key: string): string =
result = gConfigVars.getOrDefault key
proc getConfigVar*(conf: ConfigRef; key: string): string =
result = conf.configVars.getOrDefault key
proc setConfigVar*(key, val: string) =
gConfigVars[key] = val
proc setConfigVar*(conf: ConfigRef; key, val: string) =
conf.configVars[key] = val
proc getOutFile*(filename, ext: string): string =
if options.outFile != "": result = options.outFile
proc getOutFile*(conf: ConfigRef; filename, ext: string): string =
if conf.outFile != "": result = conf.outFile
else: result = changeFileExt(filename, ext)
proc getPrefixDir*(): string =
proc getPrefixDir*(conf: ConfigRef): string =
## Gets the prefix dir, usually the parent directory where the binary resides.
##
## This is overridden by some tools (namely nimsuggest) via the ``gPrefixDir``
## This is overridden by some tools (namely nimsuggest) via the ``conf.prefixDir``
## global.
if gPrefixDir != "": result = gPrefixDir
else:
result = splitPath(getAppDir()).head
if conf.prefixDir != "": result = conf.prefixDir
else: result = splitPath(getAppDir()).head
proc setDefaultLibpath*() =
proc setDefaultLibpath*(conf: ConfigRef) =
# set default value (can be overwritten):
if libpath == "":
if conf.libpath == "":
# choose default libpath:
var prefix = getPrefixDir()
var prefix = getPrefixDir(conf)
when defined(posix):
if prefix == "/usr": libpath = "/usr/lib/nim"
elif prefix == "/usr/local": libpath = "/usr/local/lib/nim"
else: libpath = joinPath(prefix, "lib")
else: libpath = joinPath(prefix, "lib")
if prefix == "/usr": conf.libpath = "/usr/lib/nim"
elif prefix == "/usr/local": conf.libpath = "/usr/local/lib/nim"
else: conf.libpath = joinPath(prefix, "lib")
else: conf.libpath = joinPath(prefix, "lib")
# Special rule to support other tools (nimble) which import the compiler
# modules and make use of them.
let realNimPath = findExe("nim")
# Find out if $nim/../../lib/system.nim exists.
let parentNimLibPath = realNimPath.parentDir().parentDir() / "lib"
if not fileExists(libpath / "system.nim") and
let parentNimLibPath = realNimPath.parentDir.parentDir / "lib"
if not fileExists(conf.libpath / "system.nim") and
fileExists(parentNimlibPath / "system.nim"):
libpath = parentNimLibPath
conf.libpath = parentNimLibPath
proc canonicalizePath*(path: string): string =
proc canonicalizePath*(conf: ConfigRef; path: string): string =
# on Windows, 'expandFilename' calls getFullPathName which doesn't do
# case corrections, so we have to use this convoluted way of retrieving
# the true filename (see tests/modules and Nimble uses 'import Uri' instead
@ -263,12 +372,12 @@ proc canonicalizePath*(path: string): string =
else:
result = path.expandFilename
proc shortenDir*(dir: string): string =
proc shortenDir*(conf: ConfigRef; dir: string): string =
## returns the interesting part of a dir
var prefix = gProjectPath & DirSep
var prefix = conf.projectPath & DirSep
if startsWith(dir, prefix):
return substr(dir, len(prefix))
prefix = getPrefixDir() & DirSep
prefix = getPrefixDir(conf) & DirSep
if startsWith(dir, prefix):
return substr(dir, len(prefix))
result = dir
@ -279,114 +388,89 @@ proc removeTrailingDirSep*(path: string): string =
else:
result = path
proc disableNimblePath*() =
gNoNimblePath = true
lazyPaths.setLen(0)
proc disableNimblePath*(conf: ConfigRef) =
incl conf.globalOptions, optNoNimblePath
conf.lazyPaths.setLen(0)
include packagehandling
proc getNimcacheDir*: string =
result = if nimcacheDir.len > 0: nimcacheDir else: gProjectPath.shortenDir /
genSubDir
proc getNimcacheDir*(conf: ConfigRef): string =
result = if conf.nimcacheDir.len > 0: conf.nimcacheDir
else: shortenDir(conf, conf.projectPath) / genSubDir
proc pathSubs*(p, config: string): string =
proc pathSubs*(conf: ConfigRef; p, config: string): string =
let home = removeTrailingDirSep(os.getHomeDir())
result = unixToNativePath(p % [
"nim", getPrefixDir(),
"lib", libpath,
"nim", getPrefixDir(conf),
"lib", conf.libpath,
"home", home,
"config", config,
"projectname", options.gProjectName,
"projectpath", options.gProjectPath,
"projectdir", options.gProjectPath,
"nimcache", getNimcacheDir()])
"projectname", conf.projectName,
"projectpath", conf.projectPath,
"projectdir", conf.projectPath,
"nimcache", getNimcacheDir(conf)])
if "~/" in result:
result = result.replace("~/", home & '/')
proc toGeneratedFile*(path, ext: string): string =
proc toGeneratedFile*(conf: ConfigRef; path, ext: string): string =
## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod"
var (head, tail) = splitPath(path)
#if len(head) > 0: head = shortenDir(head & dirSep)
result = joinPath([getNimcacheDir(), changeFileExt(tail, ext)])
result = joinPath([getNimcacheDir(conf), changeFileExt(tail, ext)])
#echo "toGeneratedFile(", path, ", ", ext, ") = ", result
when noTimeMachine:
var alreadyExcludedDirs = initSet[string]()
proc excludeDirFromTimeMachine(dir: string) {.raises: [].} =
## Calls a macosx command on the directory to exclude it from backups.
##
## The macosx tmutil command is invoked to mark the specified path as an
## item to be excluded from time machine backups. If a path already exists
## with files before excluding it, newer files won't be added to the
## directory, but previous files won't be removed from the backup until the
## user deletes that directory.
##
## The whole proc is optional and will ignore all kinds of errors. The only
## way to be sure that it works is to call ``tmutil isexcluded path``.
if alreadyExcludedDirs.contains(dir): return
alreadyExcludedDirs.incl(dir)
try:
var p = startProcess("/usr/bin/tmutil", args = ["addexclusion", dir])
discard p.waitForExit
p.close
except Exception:
discard
proc completeGeneratedFilePath*(f: string, createSubDir: bool = true): string =
proc completeGeneratedFilePath*(conf: ConfigRef; f: string, createSubDir: bool = true): string =
var (head, tail) = splitPath(f)
#if len(head) > 0: head = removeTrailingDirSep(shortenDir(head & dirSep))
var subdir = getNimcacheDir() # / head
var subdir = getNimcacheDir(conf) # / head
if createSubDir:
try:
createDir(subdir)
when noTimeMachine:
excludeDirFromTimeMachine(subdir)
except OSError:
writeLine(stdout, "cannot create directory: " & subdir)
quit(1)
result = joinPath(subdir, tail)
#echo "completeGeneratedFilePath(", f, ") = ", result
proc rawFindFile(f: string): string =
for it in searchPaths:
proc rawFindFile(conf: ConfigRef; f: string): string =
for it in conf.searchPaths:
result = joinPath(it, f)
if existsFile(result):
return result.canonicalizePath
return canonicalizePath(conf, result)
result = ""
proc rawFindFile2(f: string): string =
for i, it in lazyPaths:
proc rawFindFile2(conf: ConfigRef; f: string): string =
for i, it in conf.lazyPaths:
result = joinPath(it, f)
if existsFile(result):
# bring to front
for j in countDown(i,1):
swap(lazyPaths[j], lazyPaths[j-1])
swap(conf.lazyPaths[j], conf.lazyPaths[j-1])
return result.canonicalizePath
return canonicalizePath(conf, result)
result = ""
template patchModule() {.dirty.} =
if result.len > 0 and gModuleOverrides.len > 0:
let key = getPackageName(result) & "_" & splitFile(result).name
if gModuleOverrides.hasKey(key):
let ov = gModuleOverrides[key]
template patchModule(conf: ConfigRef) {.dirty.} =
if result.len > 0 and conf.moduleOverrides.len > 0:
let key = getPackageName(conf, result) & "_" & splitFile(result).name
if conf.moduleOverrides.hasKey(key):
let ov = conf.moduleOverrides[key]
if ov.len > 0: result = ov
proc findFile*(f: string): string {.procvar.} =
proc findFile*(conf: ConfigRef; f: string): string {.procvar.} =
if f.isAbsolute:
result = if f.existsFile: f else: ""
else:
result = f.rawFindFile
result = rawFindFile(conf, f)
if result.len == 0:
result = f.toLowerAscii.rawFindFile
result = rawFindFile(conf, f.toLowerAscii)
if result.len == 0:
result = f.rawFindFile2
result = rawFindFile2(conf, f)
if result.len == 0:
result = f.toLowerAscii.rawFindFile2
patchModule()
result = rawFindFile2(conf, f.toLowerAscii)
patchModule(conf)
proc findModule*(modulename, currentModule: string): string =
proc findModule*(conf: ConfigRef; modulename, currentModule: string): string =
# returns path to module
when defined(nimfix):
# '.nimfix' modules are preferred over '.nim' modules so that specialized
@ -396,16 +480,16 @@ proc findModule*(modulename, currentModule: string): string =
let currentPath = currentModule.splitFile.dir
result = currentPath / m
if not existsFile(result):
result = findFile(m)
result = findFile(conf, m)
if existsFile(result): return result
let m = addFileExt(modulename, NimExt)
let currentPath = currentModule.splitFile.dir
result = currentPath / m
if not existsFile(result):
result = findFile(m)
patchModule()
result = findFile(conf, m)
patchModule(conf)
proc findProjectNimFile*(pkg: string): string =
proc findProjectNimFile*(conf: ConfigRef; pkg: string): string =
const extensions = [".nims", ".cfg", ".nimcfg", ".nimble"]
var candidates: seq[string] = @[]
for k, f in os.walkDir(pkg, relative=true):
@ -430,25 +514,12 @@ proc canonDynlibName(s: string): string =
else:
result = s.substr(start)
proc inclDynlibOverride*(lib: string) =
gDllOverrides[lib.canonDynlibName] = "true"
proc inclDynlibOverride*(conf: ConfigRef; lib: string) =
conf.dllOverrides[lib.canonDynlibName] = "true"
proc isDynlibOverride*(lib: string): bool =
result = gDynlibOverrideAll or gDllOverrides.hasKey(lib.canonDynlibName)
proc binaryStrSearch*(x: openArray[string], y: string): int =
var a = 0
var b = len(x) - 1
while a <= b:
var mid = (a + b) div 2
var c = cmpIgnoreCase(x[mid], y)
if c < 0:
a = mid + 1
elif c > 0:
b = mid - 1
else:
return mid
result = - 1
proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
result = optDynlibOverrideAll in conf.globalOptions or
conf.dllOverrides.hasKey(lib.canonDynlibName)
proc parseIdeCmd*(s: string): IdeCmd =
case s:

View file

@ -15,23 +15,16 @@ iterator myParentDirs(p: string): string =
if current.len == 0: break
yield current
template newPackageCache(): untyped =
newStringTable(when FileSystemCaseSensitive:
modeCaseInsensitive
else:
modeCaseSensitive)
proc resetPackageCache*(conf: ConfigRef) =
conf.packageCache = newPackageCache()
var packageCache = newPackageCache()
proc resetPackageCache*() = packageCache = newPackageCache()
proc getPackageName*(path: string): string =
proc getPackageName*(conf: ConfigRef; path: string): string =
var parents = 0
block packageSearch:
for d in myParentDirs(path):
if packageCache.hasKey(d):
if conf.packageCache.hasKey(d):
#echo "from cache ", d, " |", packageCache[d], "|", path.splitFile.name
return packageCache[d]
return conf.packageCache[d]
inc parents
for file in walkFiles(d / "*.nimble"):
result = file.splitFile.name
@ -43,12 +36,12 @@ proc getPackageName*(path: string): string =
if result.isNil: result = ""
for d in myParentDirs(path):
#echo "set cache ", d, " |", result, "|", parents
packageCache[d] = result
conf.packageCache[d] = result
dec parents
if parents <= 0: break
proc withPackageName*(path: string): string =
let x = path.getPackageName
proc withPackageName*(conf: ConfigRef; path: string): string =
let x = getPackageName(conf, path)
if x.len == 0:
result = path
else:

View file

@ -10,7 +10,8 @@
## This module implements the pattern matching features for term rewriting
## macro support.
import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees
import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees,
options
# we precompile the pattern here for efficiency into some internal
# stack based VM :-) Why? Because it's fun; I did no benchmarks to see if that
@ -41,8 +42,8 @@ type
const
MaxStackSize* = 64 ## max required stack size by the VM
proc patternError(n: PNode) =
localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc patternError(n: PNode; conf: ConfigRef) =
localError(conf, n.info, "illformed AST: " & renderTree(n, {renderNoComments}))
proc add(code: var TPatternCode, op: TOpcode) {.inline.} =
add(code, chr(ord(op)))
@ -53,42 +54,42 @@ proc whichAlias*(p: PSym): TAliasRequest =
else:
result = aqNone
proc compileConstraints(p: PNode, result: var TPatternCode) =
proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) =
case p.kind
of nkCallKinds:
if p.sons[0].kind != nkIdent:
patternError(p.sons[0])
patternError(p.sons[0], conf)
return
let op = p.sons[0].ident
if p.len == 3:
if op.s == "|" or op.id == ord(wOr):
compileConstraints(p.sons[1], result)
compileConstraints(p.sons[2], result)
compileConstraints(p.sons[1], result, conf)
compileConstraints(p.sons[2], result, conf)
result.add(ppOr)
elif op.s == "&" or op.id == ord(wAnd):
compileConstraints(p.sons[1], result)
compileConstraints(p.sons[2], result)
compileConstraints(p.sons[1], result, conf)
compileConstraints(p.sons[2], result, conf)
result.add(ppAnd)
else:
patternError(p)
patternError(p, conf)
elif p.len == 2 and (op.s == "~" or op.id == ord(wNot)):
compileConstraints(p.sons[1], result)
compileConstraints(p.sons[1], result, conf)
result.add(ppNot)
else:
patternError(p)
patternError(p, conf)
of nkAccQuoted, nkPar:
if p.len == 1:
compileConstraints(p.sons[0], result)
compileConstraints(p.sons[0], result, conf)
else:
patternError(p)
patternError(p, conf)
of nkIdent:
let spec = p.ident.s.normalize
case spec
of "atom": result.add(ppAtom)
of "lit": result.add(ppLit)
of "sym": result.add(ppSym)
of "atom": result.add(ppAtom)
of "lit": result.add(ppLit)
of "sym": result.add(ppSym)
of "ident": result.add(ppIdent)
of "call": result.add(ppCall)
of "call": result.add(ppCall)
of "alias": result[0] = chr(aqShouldAlias.ord)
of "noalias": result[0] = chr(aqNoAlias.ord)
of "lvalue": result.add(ppLValue)
@ -97,24 +98,24 @@ proc compileConstraints(p: PNode, result: var TPatternCode) =
of "nosideeffect": result.add(ppNoSideEffect)
else:
# check all symkinds:
internalAssert int(high(TSymKind)) < 255
internalAssert conf, int(high(TSymKind)) < 255
for i in low(TSymKind)..high(TSymKind):
if cmpIgnoreStyle(($i).substr(2), spec) == 0:
result.add(ppSymKind)
result.add(chr(i.ord))
return
# check all nodekinds:
internalAssert int(high(TNodeKind)) < 255
internalAssert conf, int(high(TNodeKind)) < 255
for i in low(TNodeKind)..high(TNodeKind):
if cmpIgnoreStyle($i, spec) == 0:
result.add(ppNodeKind)
result.add(chr(i.ord))
return
patternError(p)
patternError(p, conf)
else:
patternError(p)
patternError(p, conf)
proc semNodeKindConstraints*(p: PNode): PNode =
proc semNodeKindConstraints*(p: PNode; conf: ConfigRef): PNode =
## does semantic checking for a node kind pattern and compiles it into an
## efficient internal format.
assert p.kind == nkCurlyExpr
@ -123,11 +124,11 @@ proc semNodeKindConstraints*(p: PNode): PNode =
result.strVal.add(chr(aqNone.ord))
if p.len >= 2:
for i in 1..<p.len:
compileConstraints(p.sons[i], result.strVal)
compileConstraints(p.sons[i], result.strVal, conf)
if result.strVal.len > MaxStackSize-1:
internalError(p.info, "parameter pattern too complex")
internalError(conf, p.info, "parameter pattern too complex")
else:
patternError(p)
patternError(p, conf)
result.strVal.add(ppEof)
type
@ -178,6 +179,36 @@ type
arDiscriminant, # is a discriminant
arStrange # it is a strange beast like 'typedesc[var T]'
proc exprRoot*(n: PNode): PSym =
var it = n
while true:
case it.kind
of nkSym: return it.sym
of nkHiddenDeref, nkDerefExpr:
if it[0].typ.skipTypes(abstractInst).kind in {tyPtr, tyRef}:
# 'ptr' is unsafe anyway and 'ref' is always on the heap,
# so allow these derefs:
break
else:
it = it[0]
of nkDotExpr, nkBracketExpr, nkHiddenAddr,
nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
it = it[0]
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
it = it[1]
of nkStmtList, nkStmtListExpr:
if it.len > 0 and it.typ != nil: it = it.lastSon
else: break
of nkCallKinds:
if it.typ != nil and it.typ.kind == tyVar and it.len > 1:
# See RFC #7373, calls returning 'var T' are assumed to
# return a view into the first argument (if there is one):
it = it[1]
else:
break
else:
break
proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult =
## 'owner' can be nil!
result = arNone
@ -189,7 +220,7 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
else: {skVar, skResult, skTemp}
if n.sym.kind in kinds:
if owner != nil and owner.id == n.sym.owner.id and
if owner != nil and owner == n.sym.owner and
sfGlobal notin n.sym.flags:
result = arLocalLValue
else:
@ -205,7 +236,8 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
result = arLValue
else:
result = isAssignable(owner, n.sons[0], isUnsafeAddr)
if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
if result != arNone and n[1].kind == nkSym and
sfDiscriminant in n[1].sym.flags:
result = arDiscriminant
of nkBracketExpr:
if skipTypes(n.sons[0].typ, abstractInst-{tyTypeDesc}).kind in
@ -222,7 +254,10 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult
elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
# types that are equal modulo distinction preserve l-value:
result = isAssignable(owner, n.sons[1], isUnsafeAddr)
of nkHiddenDeref, nkDerefExpr, nkHiddenAddr:
of nkHiddenDeref:
if n[0].typ.kind == tyLent: result = arDiscriminant
else: result = arLValue
of nkDerefExpr, nkHiddenAddr:
result = arLValue
of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
result = isAssignable(owner, n.sons[0], isUnsafeAddr)

View file

@ -17,6 +17,8 @@
# In fact the grammar is generated from this file:
when isMainModule:
# Leave a note in grammar.txt that it is generated:
#| # This file is generated by compiler/parser.nim.
import pegs
var outp = open("doc/grammar.txt", fmWrite)
for line in lines("compiler/parser.nim"):
@ -25,7 +27,7 @@ when isMainModule:
outp.close
import
llstream, lexer, idents, strutils, ast, astalgo, msgs
llstream, lexer, idents, strutils, ast, astalgo, msgs, options, configuration
type
TParser* = object # A TParser object represents a file that
@ -81,21 +83,21 @@ proc getTok(p: var TParser) =
rawGetTok(p.lex, p.tok)
p.hasProgress = true
proc openParser*(p: var TParser, fileIdx: int32, inputStream: PLLStream,
cache: IdentCache;
proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream,
cache: IdentCache; config: ConfigRef;
strongSpaces=false) =
## Open a parser, using the given arguments to set up its internal state.
##
initToken(p.tok)
openLexer(p.lex, fileIdx, inputStream, cache)
openLexer(p.lex, fileIdx, inputStream, cache, config)
getTok(p) # read the first token
p.firstTok = true
p.strongSpaces = strongSpaces
proc openParser*(p: var TParser, filename: string, inputStream: PLLStream,
cache: IdentCache;
cache: IdentCache; config: ConfigRef;
strongSpaces=false) =
openParser(p, filename.fileInfoIdx, inputStream, cache, strongSpaces)
openParser(p, fileInfoIdx(config, filename), inputStream, cache, config, strongSpaces)
proc closeParser(p: var TParser) =
## Close a parser, freeing up its resources.
@ -105,9 +107,13 @@ proc parMessage(p: TParser, msg: TMsgKind, arg = "") =
## Produce and emit the parser message `arg` to output.
lexMessageTok(p.lex, msg, p.tok, arg)
proc parMessage(p: TParser, msg: TMsgKind, tok: TToken) =
proc parMessage(p: TParser, msg: string, tok: TToken) =
## Produce and emit a parser message to output about the token `tok`
parMessage(p, msg, prettyTok(tok))
parMessage(p, errGenerated, msg % prettyTok(tok))
proc parMessage(p: TParser, arg: string) =
## Produce and emit the parser message `arg` to output.
lexMessageTok(p.lex, errGenerated, p.tok, arg)
template withInd(p, body: untyped) =
let oldInd = p.currInd
@ -123,7 +129,13 @@ proc rawSkipComment(p: var TParser, node: PNode) =
if p.tok.tokType == tkComment:
if node != nil:
if node.comment == nil: node.comment = ""
add(node.comment, p.tok.literal)
when defined(nimpretty):
if p.tok.commentOffsetB > p.tok.commentOffsetA:
add node.comment, fileSection(p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB)
else:
add node.comment, p.tok.literal
else:
add(node.comment, p.tok.literal)
else:
parMessage(p, errInternal, "skipComment")
getTok(p)
@ -134,6 +146,12 @@ proc skipComment(p: var TParser, node: PNode) =
proc flexComment(p: var TParser, node: PNode) =
if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node)
const
errInvalidIndentation = "invalid indentation"
errIdentifierExpected = "identifier expected, but got '$1'"
errExprExpected = "expression expected, but found '$1'"
errTokenExpected = "'$1' expected"
proc skipInd(p: var TParser) =
if p.tok.indent >= 0:
if not realInd(p): parMessage(p, errInvalidIndentation)
@ -152,11 +170,11 @@ proc getTokNoInd(p: var TParser) =
proc expectIdentOrKeyw(p: TParser) =
if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType):
lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
proc expectIdent(p: TParser) =
if p.tok.tokType != tkSymbol:
lexMessage(p.lex, errIdentifierExpected, prettyTok(p.tok))
lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok))
proc eat(p: var TParser, tokType: TTokType) =
## Move the parser to the next token if the current token is of type
@ -164,7 +182,8 @@ proc eat(p: var TParser, tokType: TTokType) =
if p.tok.tokType == tokType:
getTok(p)
else:
lexMessageTok(p.lex, errTokenExpected, p.tok, TokTypeToStr[tokType])
lexMessage(p.lex, errGenerated,
"expected: '" & TokTypeToStr[tokType] & "', but got: '" & prettyTok(p.tok) & "'")
proc parLineInfo(p: TParser): TLineInfo =
## Retrieve the line information associated with the parser's current state.
@ -260,13 +279,9 @@ proc isUnary(p: TParser): bool =
proc checkBinary(p: TParser) {.inline.} =
## Check if the current parser token is a binary operator.
# we don't check '..' here as that's too annoying
if p.strongSpaces and p.tok.tokType == tkOpr:
if p.tok.tokType == tkOpr:
if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA != p.tok.strongSpaceB:
parMessage(p, errGenerated,
"Number of spaces around '$#' not consistent" %
prettyTok(p.tok))
elif p.tok.strongSpaceA notin {0,1,2,4,8}:
parMessage(p, errGenerated, "Number of spaces must be 0,1,2,4 or 8")
parMessage(p, warnInconsistentSpacing, prettyTok(p.tok))
#| module = stmt ^* (';' / IND{=})
#|
@ -387,20 +402,6 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
getTok(p)
optInd(p, a)
proc dotExpr(p: var TParser, a: PNode): PNode =
#| dotExpr = expr '.' optInd symbol
var info = p.parLineInfo
getTok(p)
result = newNodeI(nkDotExpr, info)
optInd(p, result)
addSon(result, a)
addSon(result, parseSymbol(p, smAfterDot))
proc qualifiedIdent(p: var TParser): PNode =
#| qualifiedIdent = symbol ('.' optInd symbol)?
result = parseSymbol(p)
if p.tok.tokType == tkDot: result = dotExpr(p, result)
proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) =
assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi})
getTok(p)
@ -411,6 +412,9 @@ proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) =
addSon(result, a)
if p.tok.tokType != tkComma: break
getTok(p)
# (1,) produces a tuple expression
if endTok == tkParRi and p.tok.tokType == tkParRi and result.kind == nkPar:
result.kind = nkTupleConstr
skipComment(p, a)
optPar(p)
eat(p, endTok)
@ -421,6 +425,33 @@ proc exprColonEqExprList(p: var TParser, kind: TNodeKind,
result = newNodeP(kind, p)
exprColonEqExprListAux(p, endTok, result)
proc dotExpr(p: var TParser, a: PNode): PNode =
#| dotExpr = expr '.' optInd (symbol | '[:' exprList ']')
#| explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )?
var info = p.parLineInfo
getTok(p)
result = newNodeI(nkDotExpr, info)
optInd(p, result)
addSon(result, a)
addSon(result, parseSymbol(p, smAfterDot))
if p.tok.tokType == tkBracketLeColon and p.tok.strongSpaceA <= 0:
var x = newNodeI(nkBracketExpr, p.parLineInfo)
# rewrite 'x.y[:z]()' to 'y[z](x)'
x.add result[1]
exprList(p, tkBracketRi, x)
eat(p, tkBracketRi)
var y = newNodeI(nkCall, p.parLineInfo)
y.add x
y.add result[0]
if p.tok.tokType == tkParLe and p.tok.strongSpaceA <= 0:
exprColonEqExprListAux(p, tkParRi, y)
result = y
proc qualifiedIdent(p: var TParser): PNode =
#| qualifiedIdent = symbol ('.' optInd symbol)?
result = parseSymbol(p)
if p.tok.tokType == tkDot: result = dotExpr(p, result)
proc setOrTableConstr(p: var TParser): PNode =
#| setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
result = newNodeP(nkCurly, p)
@ -551,6 +582,9 @@ proc parsePar(p: var TParser): PNode =
if p.tok.tokType == tkComma:
getTok(p)
skipComment(p, a)
# (1,) produces a tuple expression:
if p.tok.tokType == tkParRi:
result.kind = nkTupleConstr
# progress guaranteed
while p.tok.tokType != tkParRi and p.tok.tokType != tkEof:
var a = exprColonEqExpr(p)
@ -679,10 +713,17 @@ proc namedParams(p: var TParser, callee: PNode,
# progress guaranteed
exprColonEqExprListAux(p, endTok, result)
proc commandParam(p: var TParser): PNode =
proc commandParam(p: var TParser, isFirstParam: var bool): PNode =
result = parseExpr(p)
if p.tok.tokType == tkDo:
result = postExprBlocks(p, result)
elif p.tok.tokType == tkEquals and not isFirstParam:
let lhs = result
result = newNodeP(nkExprEqExpr, p)
getTok(p)
addSon(result, lhs)
addSon(result, parseExpr(p))
isFirstParam = false
proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
#| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
@ -717,17 +758,19 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
# progress guaranteed
somePar()
result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType:
if p.inPragma == 0:
of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType,
tkOpr, tkDotDot:
if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}):
# actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
# solution, but pragmas.nim can't handle that
let a = result
result = newNodeP(nkCommand, p)
addSon(result, a)
var isFirstParam = true
when true:
# progress NOT guaranteed
p.hasProgress = false
addSon result, commandParam(p)
addSon result, commandParam(p, isFirstParam)
if not p.hasProgress: break
else:
while p.tok.tokType != tkEof:
@ -818,7 +861,6 @@ proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode =
if realInd(p):
p.currInd = p.tok.indent
wasIndented = true
echo result.info, " yes ", p.currInd
addSon(branch, parseExpr(p))
result.add branch
while sameInd(p) or not wasIndented:
@ -855,7 +897,7 @@ proc parsePragma(p: var TParser): PNode =
skipComment(p, a)
optPar(p)
if p.tok.tokType in {tkCurlyDotRi, tkCurlyRi}: getTok(p)
else: parMessage(p, errTokenExpected, ".}")
else: parMessage(p, "expected '.}'")
dec p.inPragma
proc identVis(p: var TParser; allowDot=false): PNode =
@ -914,15 +956,15 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
optInd(p, result)
addSon(result, parseTypeDesc(p))
else:
addSon(result, ast.emptyNode)
addSon(result, newNodeP(nkEmpty, p))
if p.tok.tokType != tkEquals and withBothOptional notin flags:
parMessage(p, errColonOrEqualsExpected, p.tok)
parMessage(p, "':' or '=' expected, but got '$1'", p.tok)
if p.tok.tokType == tkEquals:
getTok(p)
optInd(p, result)
addSon(result, parseExpr(p))
else:
addSon(result, ast.emptyNode)
addSon(result, newNodeP(nkEmpty, p))
proc parseTuple(p: var TParser, indentAllowed = false): PNode =
#| inlTupleDecl = 'tuple'
@ -962,6 +1004,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode =
parMessage(p, errIdentifierExpected, p.tok)
break
if not sameInd(p): break
elif p.tok.tokType == tkParLe:
parMessage(p, errGenerated, "the syntax for tuple types is 'tuple[...]', not 'tuple(...)'")
else:
result = newNodeP(nkTupleClassTy, p)
@ -983,8 +1027,11 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
a = parseIdentColonEquals(p, {withBothOptional, withPragma})
of tkParRi:
break
of tkVar:
parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'")
break
else:
parMessage(p, errTokenExpected, ")")
parMessage(p, "expected closing ')'")
break
addSon(result, a)
if p.tok.tokType notin {tkComma, tkSemiColon}: break
@ -1061,6 +1108,7 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
#| distinct = 'distinct' optInd typeDesc
result = newNodeP(kind, p)
getTok(p)
if p.tok.indent != -1 and p.tok.indent <= p.currInd: return
optInd(p, result)
if not isOperator(p.tok) and isExprStart(p):
addSon(result, primary(p, mode))
@ -1144,7 +1192,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode =
if mode == pmTypeDef:
result = parseTypeClass(p)
else:
parMessage(p, errInvalidToken, p.tok)
parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
of tkStatic:
let info = parLineInfo(p)
getTokNoInd(p)
@ -1254,7 +1302,7 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
if nextBlock.kind == nkElse: break
else:
if openingParams.kind != nkEmpty:
parMessage(p, errTokenExpected, ":")
parMessage(p, "expected ':'")
proc parseExprStmt(p: var TParser): PNode =
#| exprStmt = simpleExpr
@ -1274,17 +1322,18 @@ proc parseExprStmt(p: var TParser): PNode =
addSon(result, b)
else:
# simpleExpr parsed 'p a' from 'p a, b'?
var isFirstParam = false
if p.tok.indent < 0 and p.tok.tokType == tkComma and a.kind == nkCommand:
result = a
while true:
getTok(p)
optInd(p, result)
addSon(result, commandParam(p))
addSon(result, commandParam(p, isFirstParam))
if p.tok.tokType != tkComma: break
elif p.tok.indent < 0 and isExprStart(p):
result = newNode(nkCommand, a.info, @[a])
while true:
addSon(result, commandParam(p))
addSon(result, commandParam(p, isFirstParam))
if p.tok.tokType != tkComma: break
getTok(p)
optInd(p, result)
@ -1489,7 +1538,7 @@ proc parseTry(p: var TParser; isExpr: bool): PNode =
addSon(b, parseStmt(p))
addSon(result, b)
if b.kind == nkFinally: break
if b == nil: parMessage(p, errTokenExpected, "except")
if b == nil: parMessage(p, "expected 'except'")
proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode =
#| exceptBlock = 'except' colcom stmt
@ -1544,7 +1593,7 @@ proc parseAsm(p: var TParser): PNode =
of tkTripleStrLit: addSon(result,
newStrNodeP(nkTripleStrLit, p.tok.literal, p))
else:
parMessage(p, errStringLiteralExpected)
parMessage(p, "the 'asm' statement takes a string literal")
addSon(result, ast.emptyNode)
return
getTok(p)
@ -1675,7 +1724,7 @@ proc parseSection(p: var TParser, kind: TNodeKind,
parMessage(p, errIdentifierExpected, p.tok)
proc parseConstant(p: var TParser): PNode =
#| constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment
#| constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment
result = newNodeP(nkConstDef, p)
addSon(result, identWithPragma(p))
if p.tok.tokType == tkColon:
@ -1723,7 +1772,7 @@ proc parseEnum(p: var TParser): PNode =
p.tok.tokType == tkEof:
break
if result.len <= 1:
lexMessageTok(p.lex, errIdentifierExpected, p.tok, prettyTok(p.tok))
parMessage(p, errIdentifierExpected, p.tok)
proc parseObjectPart(p: var TParser): PNode
proc parseObjectWhen(p: var TParser): PNode =
@ -2086,7 +2135,7 @@ proc parseStmt(p: var TParser): PNode =
case p.tok.tokType
of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc,
tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar:
parMessage(p, errComplexStmtRequiresInd)
parMessage(p, "complex statement requires indentation")
result = ast.emptyNode
else:
if p.inSemiStmtList > 0:
@ -2130,7 +2179,12 @@ proc parseTopLevelStmt(p: var TParser): PNode =
if p.tok.indent != 0:
if p.firstTok and p.tok.indent < 0: discard
elif p.tok.tokType != tkSemiColon:
parMessage(p, errInvalidIndentation)
# special casing for better error messages:
if p.tok.tokType == tkOpr and p.tok.ident.s == "*":
parMessage(p, errGenerated,
"invalid indentation; an export marker '*' follows the declared identifier")
else:
parMessage(p, errInvalidIndentation)
p.firstTok = false
case p.tok.tokType
of tkSemiColon:
@ -2144,8 +2198,8 @@ proc parseTopLevelStmt(p: var TParser): PNode =
if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
break
proc parseString*(s: string; cache: IdentCache; filename: string = "";
line: int = 0;
proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
filename: string = ""; line: int = 0;
errorHandler: TErrorHandler = nil): PNode =
## Parses a string into an AST, returning the top node.
## `filename` and `line`, although optional, provide info so that the
@ -2158,7 +2212,7 @@ proc parseString*(s: string; cache: IdentCache; filename: string = "";
# XXX for now the builtin 'parseStmt/Expr' functions do not know about strong
# spaces...
parser.lex.errorHandler = errorHandler
openParser(parser, filename, stream, cache, false)
openParser(parser, filename, stream, cache, config, false)
result = parser.parseAll
closeParser(parser)

View file

@ -10,22 +10,26 @@
## implements some little helper passes
import
strutils, ast, astalgo, passes, idents, msgs, options, idgen
strutils, ast, astalgo, passes, idents, msgs, options, idgen, configuration
from modulegraphs import ModuleGraph
type
VerboseRef = ref object of TPassContext
config: ConfigRef
proc verboseOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext =
#MessageOut('compiling ' + s.name.s);
result = nil # we don't need a context
rawMessage(hintProcessing, s.name.s)
result = VerboseRef(config: graph.config)
rawMessage(graph.config, hintProcessing, s.name.s)
proc verboseProcess(context: PPassContext, n: PNode): PNode =
result = n
if context != nil: internalError("logpass: context is not nil")
if gVerbosity == 3:
let v = VerboseRef(context)
if v.config.verbosity == 3:
# system.nim deactivates all hints, for verbosity:3 we want the processing
# messages nonetheless, so we activate them again unconditionally:
incl(msgs.gNotes, hintProcessing)
message(n.info, hintProcessing, $idgen.gFrontendId)
incl(v.config.notes, hintProcessing)
message(v.config, n.info, hintProcessing, $idgen.gFrontendId)
const verbosePass* = makePass(open = verboseOpen, process = verboseProcess)

View file

@ -7,13 +7,14 @@
# distribution, for details about the copyright.
#
# This module implements the passes functionality. A pass must implement the
# `TPass` interface.
## This module implements the passes functionality. A pass must implement the
## `TPass` interface.
import
strutils, options, ast, astalgo, llstream, msgs, platform, os,
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder
nimsets, syntaxes, times, rodread, idgen, modulegraphs, reorder, rod,
configuration
type
@ -29,7 +30,8 @@ type
TPassProcess* = proc (p: PPassContext, topLevelStmt: PNode): PNode {.nimcall.}
TPass* = tuple[open: TPassOpen, openCached: TPassOpenCached,
process: TPassProcess, close: TPassClose]
process: TPassProcess, close: TPassClose,
isFrontend: bool]
TPassData* = tuple[input: PNode, closeOutput: PNode]
TPasses* = openArray[TPass]
@ -41,38 +43,26 @@ type
proc makePass*(open: TPassOpen = nil,
openCached: TPassOpenCached = nil,
process: TPassProcess = nil,
close: TPassClose = nil): TPass =
close: TPassClose = nil,
isFrontend = false): TPass =
result.open = open
result.openCached = openCached
result.close = close
result.process = process
result.isFrontend = isFrontend
# the semantic checker needs these:
var
gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PSym {.nimcall.}
gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: int32; cache: IdentCache): PNode {.nimcall.}
gImportModule*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PSym {.nimcall.}
gIncludeFile*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex; cache: IdentCache): PNode {.nimcall.}
# implementation
proc skipCodegen*(n: PNode): bool {.inline.} =
proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} =
# can be used by codegen passes to determine whether they should do
# something with `n`. Currently, this ignores `n` and uses the global
# error count instead.
result = msgs.gErrorCounter > 0
proc astNeeded*(s: PSym): bool =
# The ``rodwrite`` module uses this to determine if the body of a proc
# needs to be stored. The passes manager frees s.sons[codePos] when
# appropriate to free the procedure body's memory. This is important
# to keep memory usage down.
if (s.kind in {skMethod, skProc, skFunc}) and
({sfCompilerProc, sfCompileTime} * s.flags == {}) and
(s.typ.callConv != ccInline) and
(s.ast.sons[genericParamsPos].kind == nkEmpty):
result = false
# XXX this doesn't really make sense with excessive CTFE
else:
result = true
result = config.errorCounter > 0
const
maxPasses = 10
@ -150,20 +140,21 @@ proc closePassesCached(graph: ModuleGraph; a: var TPassContextArray) =
m = gPasses[i].close(graph, a[i], m)
a[i] = nil # free the memory here
proc resolveMod(module, relativeTo: string): int32 =
let fullPath = findModule(module, relativeTo)
proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
let fullPath = findModule(conf, module, relativeTo)
if fullPath.len == 0:
result = InvalidFileIDX
else:
result = fullPath.fileInfoIdx
result = fileInfoIdx(conf, fullPath)
proc processImplicits(implicits: seq[string], nodeKind: TNodeKind,
proc processImplicits(conf: ConfigRef; implicits: seq[string], nodeKind: TNodeKind,
a: var TPassContextArray; m: PSym) =
# XXX fixme this should actually be relative to the config file!
let gCmdLineInfo = newLineInfo(FileIndex(0), 1, 1)
let relativeTo = m.info.toFullPath
for module in items(implicits):
# implicit imports should not lead to a module importing itself
if m.position != resolveMod(module, relativeTo):
if m.position != resolveMod(conf, module, relativeTo).int32:
var importStmt = newNodeI(nodeKind, gCmdLineInfo)
var str = newStrNode(nkStrLit, module)
str.info = gCmdLineInfo
@ -178,26 +169,55 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream,
a: TPassContextArray
s: PLLStream
fileIdx = module.fileIdx
if rd == nil:
if module.id < 0:
# new module caching mechanism:
for i in 0..<gPassesLen:
if not isNil(gPasses[i].open) and not gPasses[i].isFrontend:
a[i] = gPasses[i].open(graph, module, cache)
else:
a[i] = nil
var stmtIndex = 0
var doContinue = true
while doContinue:
let n = loadNode(module, stmtIndex)
if n == nil or graph.stopCompile(): break
#if n.kind == nkImportStmt:
# echo "yes and it's ", n
inc stmtIndex
var m = n
for i in 0..<gPassesLen:
if not isNil(gPasses[i].process) and not gPasses[i].isFrontend:
m = gPasses[i].process(a[i], m)
if isNil(m):
doContinue = false
break
var m: PNode = nil
for i in 0..<gPassesLen:
if not isNil(gPasses[i].close) and not gPasses[i].isFrontend:
m = gPasses[i].close(graph, a[i], m)
a[i] = nil
elif rd == nil:
openPasses(graph, a, module, cache)
if stream == nil:
let filename = fileIdx.toFullPathConsiderDirty
s = llStreamOpen(filename, fmRead)
if s == nil:
rawMessage(errCannotOpenFile, filename)
rawMessage(graph.config, errCannotOpenFile, filename)
return false
else:
s = stream
while true:
openParsers(p, fileIdx, s, cache)
openParsers(p, fileIdx, s, cache, graph.config)
if sfSystemModule notin module.flags:
# XXX what about caching? no processing then? what if I change the
# modules to include between compilation runs? we'd need to track that
# in ROD files. I think we should enable this feature only
# for the interactive mode.
processImplicits implicitImports, nkImportStmt, a, module
processImplicits implicitIncludes, nkIncludeStmt, a, module
processImplicits graph.config, graph.config.implicitImports, nkImportStmt, a, module
processImplicits graph.config, graph.config.implicitIncludes, nkIncludeStmt, a, module
while true:
if graph.stopCompile(): break

View file

@ -150,7 +150,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
of "*": result = matchNested(c, p, n, rpn=false)
of "**": result = matchNested(c, p, n, rpn=true)
of "~": result = not matches(c, p.sons[1], n)
else: internalError(p.info, "invalid pattern")
else: doAssert(false, "invalid pattern")
# template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
# add(a, b)
elif p.kind == nkCurlyExpr:
@ -289,7 +289,7 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
# constraint not fulfilled:
if not ok: return nil
markUsed(n.info, s, c.graph.usageSym)
markUsed(c.config, n.info, s, c.graph.usageSym)
if ctx.subMatch:
assert m.len == 3
m.sons[1] = result

File diff suppressed because it is too large Load diff

View file

@ -176,7 +176,7 @@ type
cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel,
cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR, cpuMSP430, cpuSparc64,
cpuMips64, cpuMips64el
cpuMips64, cpuMips64el, cpuRiscV64
type
TEndian* = enum
@ -207,7 +207,8 @@ const
(name: "msp430", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16),
(name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
(name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64),
(name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)]
(name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
(name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)]
var
targetCPU*, hostCPU*: TSystemCPU

View file

@ -10,4 +10,4 @@
## Include file that imports all plugins that are active.
import
locals.locals, itersgen
locals / locals, itersgen

View file

@ -9,30 +9,29 @@
## Plugin to transform an inline iterator into a data structure.
import compiler/pluginsupport, compiler/ast, compiler/astalgo,
compiler/magicsys, compiler/lookups, compiler/semdata,
compiler/lambdalifting, compiler/rodread, compiler/msgs
import ".." / [pluginsupport, ast, astalgo,
magicsys, lookups, semdata,
lambdalifting, rodread, msgs]
proc iterToProcImpl(c: PContext, n: PNode): PNode =
result = newNodeI(nkStmtList, n.info)
let iter = n[1]
if iter.kind != nkSym or iter.sym.kind != skIterator:
localError(iter.info, "first argument needs to be an iterator")
localError(c.config, iter.info, "first argument needs to be an iterator")
return
if n[2].typ.isNil:
localError(n[2].info, "second argument needs to be a type")
localError(c.config, n[2].info, "second argument needs to be a type")
return
if n[3].kind != nkIdent:
localError(n[3].info, "third argument needs to be an identifier")
localError(c.config, n[3].info, "third argument needs to be an identifier")
return
let t = n[2].typ.skipTypes({tyTypeDesc, tyGenericInst})
if t.kind notin {tyRef, tyPtr} or t.lastSon.kind != tyObject:
localError(n[2].info,
localError(c.config, n[2].info,
"type must be a non-generic ref|ptr to object with state field")
return
let body = liftIterToProc(iter.sym, iter.sym.getBody, t)
let body = liftIterToProc(c.graph, iter.sym, iter.sym.getBody, t)
let prc = newSym(skProc, n[3].ident, iter.sym.owner, iter.sym.info)
prc.typ = copyType(iter.sym.typ, prc, false)

View file

@ -9,8 +9,8 @@
## The builtin 'system.locals' implemented as a plugin.
import compiler/pluginsupport, compiler/ast, compiler/astalgo,
compiler/magicsys, compiler/lookups, compiler/semdata, compiler/lowerings
import "../../" / [pluginsupport, ast, astalgo,
magicsys, lookups, semdata, lowerings]
proc semLocals(c: PContext, n: PNode): PNode =
var counter = 0

File diff suppressed because it is too large Load diff

View file

@ -14,14 +14,12 @@ import
ast, astalgo, msgs, semdata, types, trees, strutils
proc equalGenericParams(procA, procB: PNode): bool =
if sonsLen(procA) != sonsLen(procB): return
if sonsLen(procA) != sonsLen(procB): return false
for i in countup(0, sonsLen(procA) - 1):
if procA.sons[i].kind != nkSym:
internalError(procA.info, "equalGenericParams")
return
return false
if procB.sons[i].kind != nkSym:
internalError(procB.info, "equalGenericParams")
return
return false
let a = procA.sons[i].sym
let b = procB.sons[i].sym
if a.name.id != b.name.id or
@ -57,7 +55,7 @@ proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym =
of paramsEqual:
return
of paramsIncompatible:
localError(fn.info, errNotOverloadable, fn.name.s)
localError(c.config, fn.info, "overloaded '$1' leads to ambiguous calls" % fn.name.s)
return
of paramsNotEqual:
discard
@ -66,30 +64,25 @@ proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym =
proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym =
const flags = {ExactGenericParams, ExactTypeDescValues,
ExactConstraints, IgnoreCC}
var it: TIdentIter
result = initIdentIter(it, scope.symbols, fn.name)
while result != nil:
if result.kind == fn.kind and sameType(result.typ, fn.typ, flags):
if result.kind == fn.kind: #and sameType(result.typ, fn.typ, flags):
case equalParams(result.typ.n, fn.typ.n)
of paramsEqual:
if (sfExported notin result.flags) and (sfExported in fn.flags):
let message = ("public implementation '$1' has non-public " &
"forward declaration in $2") %
[getProcHeader(result), $result.info]
localError(fn.info, errGenerated, message)
localError(c.config, fn.info, message)
return
of paramsIncompatible:
localError(fn.info, errNotOverloadable, fn.name.s)
localError(c.config, fn.info, "overloaded '$1' leads to ambiguous calls" % fn.name.s)
return
of paramsNotEqual:
discard
result = nextIdentIter(it, scope.symbols)
return nil
proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym =
result = searchForProcNew(c, scope, fn)
when false:

View file

@ -10,7 +10,7 @@
# This module implements the renderer of the standard Nim representation.
import
lexer, options, idents, strutils, ast, msgs
lexer, options, idents, strutils, ast, msgs, configuration
type
TRenderFlag* = enum
@ -39,8 +39,8 @@ type
inPragma: int
when defined(nimpretty):
pendingNewlineCount: int
origContent: string
fid*: FileIndex
config*: ConfigRef
# We render the source code in a two phases: The first
# determines how long the subtree will likely be, the second
@ -91,7 +91,7 @@ const
MaxLineLen = 80
LineCommentColumn = 30
proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) =
proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) =
g.comStack = @[]
g.tokens = @[]
g.indent = 0
@ -103,6 +103,7 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags) =
g.pendingNL = -1
g.pendingWhitespace = -1
g.inGenericParams = false
g.config = config
proc addTok(g: var TSrcGen, kind: TTokType, s: string) =
var length = len(g.tokens)
@ -173,34 +174,14 @@ proc put(g: var TSrcGen, kind: TTokType, s: string) =
else:
g.pendingWhitespace = s.len
proc toNimChar(c: char): string =
case c
of '\0': result = "\\x00" # not "\\0" to avoid ambiguous cases like "\\012".
of '\a': result = "\\a" # \x07
of '\b': result = "\\b" # \x08
of '\t': result = "\\t" # \x09
of '\L': result = "\\L" # \x0A
of '\v': result = "\\v" # \x0B
of '\f': result = "\\f" # \x0C
of '\c': result = "\\c" # \x0D
of '\e': result = "\\e" # \x1B
of '\x01'..'\x06', '\x0E'..'\x1A', '\x1C'..'\x1F', '\x80'..'\xFF':
result = "\\x" & strutils.toHex(ord(c), 2)
of '\'', '\"', '\\': result = '\\' & c
else: result = c & ""
proc makeNimString(s: string): string =
result = "\""
for i in countup(0, len(s)-1): add(result, toNimChar(s[i]))
add(result, '\"')
proc putComment(g: var TSrcGen, s: string) =
if s.isNil: return
var i = 0
let hi = len(s) - 1
var isCode = (len(s) >= 2) and (s[1] != ' ')
var ind = g.lineLen
var com = "## "
while true:
while i <= hi:
case s[i]
of '\0':
break
@ -208,7 +189,7 @@ proc putComment(g: var TSrcGen, s: string) =
put(g, tkComment, com)
com = "## "
inc(i)
if s[i] == '\x0A': inc(i)
if i < s.len and s[i] == '\x0A': inc(i)
optNL(g, ind)
of '\x0A':
put(g, tkComment, com)
@ -223,12 +204,12 @@ proc putComment(g: var TSrcGen, s: string) =
# gets too long:
# compute length of the following word:
var j = i
while s[j] > ' ': inc(j)
while j <= hi and s[j] > ' ': inc(j)
if not isCode and (g.lineLen + (j - i) > MaxLineLen):
put(g, tkComment, com)
optNL(g, ind)
com = "## "
while s[i] > ' ':
while i <= hi and s[i] > ' ':
add(com, s[i])
inc(i)
put(g, tkComment, com)
@ -237,8 +218,9 @@ proc putComment(g: var TSrcGen, s: string) =
proc maxLineLength(s: string): int =
if s.isNil: return 0
var i = 0
let hi = len(s) - 1
var lineLen = 0
while true:
while i <= hi:
case s[i]
of '\0':
break
@ -257,7 +239,7 @@ proc maxLineLength(s: string): int =
proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) =
var i = 0
var hi = len(s) - 1
let hi = len(s) - 1
var str = ""
while i <= hi:
case s[i]
@ -325,8 +307,8 @@ proc lsub(g: TSrcGen; n: PNode): int
proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
proc skip(t: PType): PType =
result = t
while result.kind in {tyGenericInst, tyRange, tyVar, tyDistinct,
tyOrdinal, tyAlias}:
while result.kind in {tyGenericInst, tyRange, tyVar, tyLent, tyDistinct,
tyOrdinal, tyAlias, tySink}:
result = lastSon(result)
if n.typ != nil and n.typ.skip.kind in {tyBool, tyEnum}:
let enumfields = n.typ.skip.n
@ -349,22 +331,25 @@ proc ulitAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
proc atom(g: TSrcGen; n: PNode): string =
when defined(nimpretty):
let comment = if n.info.commentOffsetA < n.info.commentOffsetB:
" " & substr(g.origContent, n.info.commentOffsetA, n.info.commentOffsetB)
" " & fileSection(g.fid, n.info.commentOffsetA, n.info.commentOffsetB)
else:
""
if n.info.offsetA <= n.info.offsetB:
# for some constructed tokens this can not be the case and we're better
# off to not mess with the offset then.
return substr(g.origContent, n.info.offsetA, n.info.offsetB) & comment
return fileSection(g.fid, n.info.offsetA, n.info.offsetB) & comment
var f: float32
case n.kind
of nkEmpty: result = ""
of nkIdent: result = n.ident.s
of nkSym: result = n.sym.name.s
of nkStrLit: result = makeNimString(n.strVal)
of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
of nkStrLit: result = ""; result.addQuoted(n.strVal)
of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
of nkCharLit: result = '\'' & toNimChar(chr(int(n.intVal))) & '\''
of nkCharLit:
result = "\'"
result.addEscapedChar(chr(int(n.intVal)));
result.add '\''
of nkIntLit: result = litAux(g, n, n.intVal, 4)
of nkInt8Lit: result = litAux(g, n, n.intVal, 1) & "\'i8"
of nkInt16Lit: result = litAux(g, n, n.intVal, 2) & "\'i16"
@ -394,7 +379,7 @@ proc atom(g: TSrcGen; n: PNode): string =
if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
else: result = "[type node]"
else:
internalError("rnimsyn.atom " & $n.kind)
internalError(g.config, "rnimsyn.atom " & $n.kind)
result = ""
proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
@ -432,6 +417,9 @@ proc lsub(g: TSrcGen; n: PNode): int =
of nkCommand: result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + 1
of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3
of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(g, n) + 2
of nkTupleConstr:
# assume the trailing comma:
result = lcomma(g, n) + 3
of nkArgList: result = lcomma(g, n)
of nkTableConstr:
result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
@ -898,6 +886,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkBracketLe, "[")
gcomma(g, n, 2)
put(g, tkBracketRi, "]")
elif n.len > 1 and n.lastSon.kind == nkStmtList:
gsub(g, n[0])
if n.len > 2:
put(g, tkParLe, "(")
gcomma(g, n, 1, -2)
put(g, tkParRi, ")")
put(g, tkColon, ":")
gsub(g, n, n.len-1)
else:
if sonsLen(n) >= 1: gsub(g, n.sons[0])
put(g, tkParLe, "(")
@ -994,6 +990,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkParLe, "(")
gcomma(g, n, c)
put(g, tkParRi, ")")
of nkTupleConstr:
put(g, tkParLe, "(")
gcomma(g, n, c)
if n.len == 1: put(g, tkComma, ",")
put(g, tkParRi, ")")
of nkCurly:
put(g, tkCurlyLe, "{")
gcomma(g, n, c)
@ -1067,6 +1068,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
if n.len > 1:
let opr = if n[0].kind == nkIdent: n[0].ident
elif n[0].kind == nkSym: n[0].sym.name
elif n[0].kind in {nkOpenSymChoice, nkClosedSymChoice}: n[0][0].sym.name
else: nil
if n[1].kind == nkPrefix or (opr != nil and renderer.isKeyword(opr)):
put(g, tkSpaces, Space)
@ -1411,22 +1413,32 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
put(g, tkParLe, "(ComesFrom|")
gsub(g, n, 0)
put(g, tkParRi, ")")
of nkGotoState, nkState:
of nkGotoState:
var c: TContext
initContext c
putWithSpace g, tkSymbol, if n.kind == nkState: "state" else: "goto"
putWithSpace g, tkSymbol, "goto"
gsons(g, n, c)
of nkState:
var c: TContext
initContext c
putWithSpace g, tkSymbol, "state"
gsub(g, n[0], c)
putWithSpace(g, tkColon, ":")
indentNL(g)
gsons(g, n, c, 1)
dedent(g)
of nkBreakState:
put(g, tkTuple, "breakstate")
of nkTypeClassTy:
gTypeClassTy(g, n)
else:
#nkNone, nkExplicitTypeListCall:
internalError(n.info, "rnimsyn.gsub(" & $n.kind & ')')
internalError(g.config, n.info, "rnimsyn.gsub(" & $n.kind & ')')
proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
var g: TSrcGen
initSrcGen(g, renderFlags)
initSrcGen(g, renderFlags, newPartialConfigRef())
# do not indent the initial statement list so that
# writeFile("file.nim", repr n)
# produces working Nim code:
@ -1439,17 +1451,13 @@ proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
proc `$`*(n: PNode): string = n.renderTree
proc renderModule*(n: PNode, infile, outfile: string,
renderFlags: TRenderFlags = {}) =
renderFlags: TRenderFlags = {};
fid = FileIndex(-1)) =
var
f: File
g: TSrcGen
initSrcGen(g, renderFlags)
when defined(nimpretty):
try:
g.origContent = readFile(infile)
except IOError:
rawMessage(errCannotOpenFile, infile)
initSrcGen(g, renderFlags, newPartialConfigRef())
g.fid = fid
for i in countup(0, sonsLen(n) - 1):
gsub(g, n.sons[i])
optNL(g)
@ -1458,16 +1466,14 @@ proc renderModule*(n: PNode, infile, outfile: string,
nkCommentStmt: putNL(g)
else: discard
gcoms(g)
if optStdout in gGlobalOptions:
write(stdout, g.buf)
elif open(f, outfile, fmWrite):
if open(f, outfile, fmWrite):
write(f, g.buf)
close(f)
else:
rawMessage(errCannotOpenFile, outfile)
rawMessage(g.config, errGenerated, "cannot open file: " & outfile)
proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) =
initSrcGen(r, renderFlags)
initSrcGen(r, renderFlags, newPartialConfigRef())
gsub(r, n)
proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string) =

View file

@ -1,7 +1,8 @@
import
intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils,
sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables
import
intsets, ast, idents, algorithm, renderer, parser, ospaths, strutils,
sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables,
configuration
type
DepN = ref object
@ -135,13 +136,13 @@ proc hasIncludes(n:PNode): bool =
if a.kind == nkIncludeStmt:
return true
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: int32;
proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex;
cache: IdentCache): PNode {.procvar.} =
result = syntaxes.parseFile(fileIdx, cache)
result = syntaxes.parseFile(fileIdx, cache, graph.config)
graph.addDep(s, fileIdx)
graph.addIncludeDep(s.position.int32, fileIdx)
graph.addIncludeDep(FileIndex s.position, fileIdx)
proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
modulePath: string, includedFiles: var IntSet,
cache: IdentCache): PNode =
# Parses includes and injects them in the current tree
@ -151,15 +152,15 @@ proc expandIncludes(graph: ModuleGraph, module: PSym, n: PNode,
for a in n:
if a.kind == nkIncludeStmt:
for i in 0..<a.len:
var f = checkModuleName(a.sons[i])
var f = checkModuleName(graph.config, a.sons[i])
if f != InvalidFileIDX:
if containsOrIncl(includedFiles, f):
localError(a.info, errRecursiveDependencyX, f.toFilename)
if containsOrIncl(includedFiles, f.int):
localError(graph.config, a.info, "recursive dependency: '$1'" % f.toFilename)
else:
let nn = includeModule(graph, module, f, cache)
let nnn = expandIncludes(graph, module, nn, modulePath,
let nnn = expandIncludes(graph, module, nn, modulePath,
includedFiles, cache)
excl(includedFiles, f)
excl(includedFiles, f.int)
for b in nnn:
result.add b
else:
@ -189,7 +190,7 @@ proc haveSameKind(dns: seq[DepN]): bool =
if dn.pnode.kind != kind:
return false
proc mergeSections(comps: seq[seq[DepN]], res: PNode) =
proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) =
# Merges typeSections and ConstSections when they form
# a strong component (ex: circular type definition)
for c in comps:
@ -229,7 +230,7 @@ proc mergeSections(comps: seq[seq[DepN]], res: PNode) =
wmsg &= "line " & $cs[^1].pnode.info.line &
" depends on line " & $cs[j].pnode.info.line &
": " & cs[^1].expls[ci] & "\n"
message(cs[0].pnode.info, warnUser, wmsg)
message(conf, cs[0].pnode.info, warnUser, wmsg)
var i = 0
while i < cs.len:
@ -273,9 +274,9 @@ proc hasCommand(n: PNode): bool =
of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse,
nkStaticStmt, nkLetSection, nkConstSection, nkVarSection,
nkIdentDefs:
for a in n:
if a.hasCommand:
return true
for a in n:
if a.hasCommand:
return true
else:
return false
@ -430,7 +431,7 @@ proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PN
return n
var includedFiles = initIntSet()
let mpath = module.fileIdx.toFullPath
let n = expandIncludes(graph, module, n, mpath,
let n = expandIncludes(graph, module, n, mpath,
includedFiles, cache).splitSections
result = newNodeI(nkStmtList, n.info)
var deps = newSeq[(IntSet, IntSet)](n.len)
@ -441,4 +442,4 @@ proc reorder*(graph: ModuleGraph, n: PNode, module: PSym, cache: IdentCache): PN
var g = buildGraph(n, deps)
let comps = getStrongComponents(g)
mergeSections(comps, result)
mergeSections(graph.config, comps, result)

26
compiler/rod.nim Normal file
View file

@ -0,0 +1,26 @@
#
#
# The Nim Compiler
# (c) Copyright 2017 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the canonalization for the various caching mechanisms.
import ast, idgen, msgs
when not defined(nimSymbolfiles):
template setupModuleCache* = discard
template storeNode*(module: PSym; n: PNode) = discard
template loadNode*(module: PSym; index: var int): PNode = PNode(nil)
template getModuleId*(fileIdx: FileIndex; fullpath: string): int = getID()
template addModuleDep*(module, fileIdx: FileIndex; isIncludeFile: bool) = discard
template storeRemaining*(module: PSym) = discard
else:
include rodimpl

947
compiler/rodimpl.nim Normal file
View file

@ -0,0 +1,947 @@
#
#
# The Nim Compiler
# (c) Copyright 2018 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements the new compilation cache.
import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types,
renderer, rodutils, std / sha1, idents, astalgo, magicsys
## Todo:
## - Implement the 'import' replay logic so that the codegen runs over
## dependent modules.
## - Make conditional symbols and the configuration part of a module's
## dependencies.
## - Test multi methods.
## - Implement the limited VM support based on sets.
## - Depencency computation should use signature hashes in order to
## avoid recompiling dependent modules.
var db: DbConn
proc hashFileCached(fileIdx: int32; fullpath: string): string =
result = msgs.getHash(fileIdx)
if result.len == 0:
result = $secureHashFile(fullpath)
msgs.setHash(fileIdx, result)
proc needsRecompile(fileIdx: int32; fullpath: string; cycleCheck: var IntSet): bool =
let root = db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
fullpath)
if root[0].len == 0: return true
if root[1] != hashFileCached(fileIdx, fullpath):
return true
# cycle detection: assume "not changed" is correct.
if cycleCheck.containsOrIncl(fileIdx):
return false
# check dependencies (recursively):
for row in db.fastRows(sql"select fullpath from filenames where id in (select dependency from deps where module = ?)",
root[0]):
let dep = row[0]
if needsRecompile(dep.fileInfoIdx, dep, cycleCheck):
return true
return false
proc getModuleId*(fileIdx: int32; fullpath: string): int =
if gSymbolFiles != v2Sf: return getID()
let module = db.getRow(
sql"select id, fullHash from modules where fullpath = ?", fullpath)
let currentFullhash = hashFileCached(fileIdx, fullpath)
if module[0].len == 0:
result = int db.insertID(sql"insert into modules(fullpath, interfHash, fullHash) values (?, ?, ?)",
fullpath, "", currentFullhash)
else:
result = parseInt(module[0])
if currentFullhash == module[1]:
# not changed, so use the cached AST (even if it might be wrong
# due to its dependencies):
doAssert(result != 0)
var cycleCheck = initIntSet()
if not needsRecompile(fileIdx, fullpath, cycleCheck):
return -result
db.exec(sql"update modules set fullHash = ? where id = ?", currentFullhash, module[0])
db.exec(sql"delete from deps where module = ?", module[0])
db.exec(sql"delete from types where module = ?", module[0])
db.exec(sql"delete from syms where module = ?", module[0])
db.exec(sql"delete from toplevelstmts where module = ?", module[0])
db.exec(sql"delete from statics where module = ?", module[0])
type
TRodWriter = object
module: PSym
sstack: seq[PSym] # a stack of symbols to process
tstack: seq[PType] # a stack of types to process
tmarks, smarks: IntSet
forwardedSyms: seq[PSym]
PRodWriter = var TRodWriter
proc initRodWriter(module: PSym): TRodWriter =
result = TRodWriter(module: module, sstack: @[], tstack: @[],
tmarks: initIntSet(), smarks: initIntSet(), forwardedSyms: @[])
when false:
proc getDefines(): string =
result = ""
for d in definedSymbolNames():
if result.len != 0: add(result, " ")
add(result, d)
const
rodNL = "\L"
proc pushType(w: PRodWriter, t: PType) =
if not containsOrIncl(w.tmarks, t.id):
w.tstack.add(t)
proc pushSym(w: PRodWriter, s: PSym) =
if not containsOrIncl(w.smarks, s.id):
w.sstack.add(s)
proc toDbFileId(fileIdx: int32): int =
if fileIdx == -1: return -1
let fullpath = fileIdx.toFullPath
let row = db.getRow(sql"select id, fullhash from filenames where fullpath = ?",
fullpath)
let id = row[0]
let fullhash = hashFileCached(fileIdx, fullpath)
if id.len == 0:
result = int db.insertID(sql"insert into filenames(fullpath, fullhash) values (?, ?)",
fullpath, fullhash)
else:
if row[1] != fullhash:
db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath)
result = parseInt(id)
proc fromDbFileId(dbId: int): int32 =
if dbId == -1: return -1
let fullpath = db.getValue(sql"select fullpath from filenames where id = ?", dbId)
doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId
result = fileInfoIdx(fullpath)
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result: var string) =
if n == nil:
# nil nodes have to be stored too:
result.add("()")
return
result.add('(')
encodeVInt(ord(n.kind), result)
# we do not write comments for now
# Line information takes easily 20% or more of the filesize! Therefore we
# omit line information if it is the same as the parent's line information:
if fInfo.fileIndex != n.info.fileIndex:
result.add('?')
encodeVInt(n.info.col, result)
result.add(',')
encodeVInt(n.info.line, result)
result.add(',')
encodeVInt(toDbFileId(n.info.fileIndex), result)
elif fInfo.line != n.info.line:
result.add('?')
encodeVInt(n.info.col, result)
result.add(',')
encodeVInt(n.info.line, result)
elif fInfo.col != n.info.col:
result.add('?')
encodeVInt(n.info.col, result)
# No need to output the file index, as this is the serialization of one
# file.
let f = n.flags * PersistentNodeFlags
if f != {}:
result.add('$')
encodeVInt(cast[int32](f), result)
if n.typ != nil:
result.add('^')
encodeVInt(n.typ.id, result)
pushType(w, n.typ)
case n.kind
of nkCharLit..nkUInt64Lit:
if n.intVal != 0:
result.add('!')
encodeVBiggestInt(n.intVal, result)
of nkFloatLit..nkFloat64Lit:
if n.floatVal != 0.0:
result.add('!')
encodeStr($n.floatVal, result)
of nkStrLit..nkTripleStrLit:
if n.strVal != "":
result.add('!')
encodeStr(n.strVal, result)
of nkIdent:
result.add('!')
encodeStr(n.ident.s, result)
of nkSym:
result.add('!')
encodeVInt(n.sym.id, result)
pushSym(w, n.sym)
else:
for i in countup(0, sonsLen(n) - 1):
encodeNode(w, n.info, n.sons[i], result)
add(result, ')')
proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
var oldLen = result.len
result.add('<')
if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
if loc.storage != low(loc.storage):
add(result, '*')
encodeVInt(ord(loc.storage), result)
if loc.flags != {}:
add(result, '$')
encodeVInt(cast[int32](loc.flags), result)
if loc.lode != nil:
add(result, '^')
encodeNode(w, unknownLineInfo(), loc.lode, result)
#encodeVInt(cast[int32](loc.t.id), result)
#pushType(w, loc.t)
if loc.r != nil:
add(result, '!')
encodeStr($loc.r, result)
if oldLen + 1 == result.len:
# no data was necessary, so remove the '<' again:
setLen(result, oldLen)
else:
add(result, '>')
proc encodeType(w: PRodWriter, t: PType, result: var string) =
if t == nil:
# nil nodes have to be stored too:
result.add("[]")
return
# we need no surrounding [] here because the type is in a line of its own
if t.kind == tyForward: internalError("encodeType: tyForward")
# for the new rodfile viewer we use a preceding [ so that the data section
# can easily be disambiguated:
add(result, '[')
encodeVInt(ord(t.kind), result)
add(result, '+')
encodeVInt(t.id, result)
if t.n != nil:
encodeNode(w, w.module.info, t.n, result)
if t.flags != {}:
add(result, '$')
encodeVInt(cast[int32](t.flags), result)
if t.callConv != low(t.callConv):
add(result, '?')
encodeVInt(ord(t.callConv), result)
if t.owner != nil:
add(result, '*')
encodeVInt(t.owner.id, result)
pushSym(w, t.owner)
if t.sym != nil:
add(result, '&')
encodeVInt(t.sym.id, result)
pushSym(w, t.sym)
if t.size != - 1:
add(result, '/')
encodeVBiggestInt(t.size, result)
if t.align != 2:
add(result, '=')
encodeVInt(t.align, result)
if t.lockLevel.ord != UnspecifiedLockLevel.ord:
add(result, '\14')
encodeVInt(t.lockLevel.int16, result)
if t.destructor != nil and t.destructor.id != 0:
add(result, '\15')
encodeVInt(t.destructor.id, result)
pushSym(w, t.destructor)
if t.deepCopy != nil:
add(result, '\16')
encodeVInt(t.deepcopy.id, result)
pushSym(w, t.deepcopy)
if t.assignment != nil:
add(result, '\17')
encodeVInt(t.assignment.id, result)
pushSym(w, t.assignment)
if t.sink != nil:
add(result, '\18')
encodeVInt(t.sink.id, result)
pushSym(w, t.sink)
for i, s in items(t.methods):
add(result, '\19')
encodeVInt(i, result)
add(result, '\20')
encodeVInt(s.id, result)
pushSym(w, s)
encodeLoc(w, t.loc, result)
for i in countup(0, sonsLen(t) - 1):
if t.sons[i] == nil:
add(result, "^()")
else:
add(result, '^')
encodeVInt(t.sons[i].id, result)
pushType(w, t.sons[i])
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
add(result, '|')
encodeVInt(ord(lib.kind), result)
add(result, '|')
encodeStr($lib.name, result)
add(result, '|')
encodeNode(w, info, lib.path, result)
proc encodeInstantiations(w: PRodWriter; s: seq[PInstantiation];
result: var string) =
for t in s:
result.add('\15')
encodeVInt(t.sym.id, result)
pushSym(w, t.sym)
for tt in t.concreteTypes:
result.add('\17')
encodeVInt(tt.id, result)
pushType(w, tt)
result.add('\20')
encodeVInt(t.compilesId, result)
proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
if s == nil:
# nil nodes have to be stored too:
result.add("{}")
return
# we need no surrounding {} here because the symbol is in a line of its own
encodeVInt(ord(s.kind), result)
result.add('+')
encodeVInt(s.id, result)
result.add('&')
encodeStr(s.name.s, result)
if s.typ != nil:
result.add('^')
encodeVInt(s.typ.id, result)
pushType(w, s.typ)
result.add('?')
if s.info.col != -1'i16: encodeVInt(s.info.col, result)
result.add(',')
if s.info.line != -1'i16: encodeVInt(s.info.line, result)
result.add(',')
encodeVInt(toDbFileId(s.info.fileIndex), result)
if s.owner != nil:
result.add('*')
encodeVInt(s.owner.id, result)
pushSym(w, s.owner)
if s.flags != {}:
result.add('$')
encodeVInt(cast[int32](s.flags), result)
if s.magic != mNone:
result.add('@')
encodeVInt(ord(s.magic), result)
result.add('!')
encodeVInt(cast[int32](s.options), result)
if s.position != 0:
result.add('%')
encodeVInt(s.position, result)
if s.offset != - 1:
result.add('`')
encodeVInt(s.offset, result)
encodeLoc(w, s.loc, result)
if s.annex != nil: encodeLib(w, s.annex, s.info, result)
if s.constraint != nil:
add(result, '#')
encodeNode(w, unknownLineInfo(), s.constraint, result)
case s.kind
of skType, skGenericParam:
for t in s.typeInstCache:
result.add('\14')
encodeVInt(t.id, result)
pushType(w, t)
of routineKinds:
encodeInstantiations(w, s.procInstCache, result)
if s.gcUnsafetyReason != nil:
result.add('\16')
encodeVInt(s.gcUnsafetyReason.id, result)
pushSym(w, s.gcUnsafetyReason)
of skModule, skPackage:
encodeInstantiations(w, s.usedGenerics, result)
# we don't serialize:
#tab*: TStrTable # interface table for modules
of skLet, skVar, skField, skForVar:
if s.guard != nil:
result.add('\18')
encodeVInt(s.guard.id, result)
pushSym(w, s.guard)
if s.bitsize != 0:
result.add('\19')
encodeVInt(s.bitsize, result)
else: discard
# lazy loading will soon reload the ast lazily, so the ast needs to be
# the last entry of a symbol:
if s.ast != nil:
# we used to attempt to save space here by only storing a dummy AST if
# it is not necessary, but Nim's heavy compile-time evaluation features
# make that unfeasible nowadays:
encodeNode(w, s.info, s.ast, result)
proc storeSym(w: PRodWriter; s: PSym) =
if sfForward in s.flags and s.kind != skModule:
w.forwardedSyms.add s
return
var buf = newStringOfCap(160)
encodeSym(w, s, buf)
# XXX only store the name for exported symbols in order to speed up lookup
# times once we enable the skStub logic.
db.exec(sql"insert into syms(nimid, module, name, data, exported) values (?, ?, ?, ?, ?)",
s.id, abs(w.module.id), s.name.s, buf, ord(sfExported in s.flags))
proc storeType(w: PRodWriter; t: PType) =
var buf = newStringOfCap(160)
encodeType(w, t, buf)
db.exec(sql"insert into types(nimid, module, data) values (?, ?, ?)",
t.id, abs(w.module.id), buf)
var w = initRodWriter(nil)
proc storeNode*(module: PSym; n: PNode) =
if gSymbolFiles != v2Sf: return
w.module = module
var buf = newStringOfCap(160)
encodeNode(w, module.info, n, buf)
db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)",
abs(module.id), module.offset, buf)
inc module.offset
var i = 0
while true:
if i > 10_000:
quit "loop never ends!"
if w.sstack.len > 0:
let s = w.sstack.pop()
when false:
echo "popped ", s.name.s, " ", s.id
storeSym(w, s)
elif w.tstack.len > 0:
let t = w.tstack.pop()
storeType(w, t)
when false:
echo "popped type ", typeToString(t), " ", t.id
else:
break
inc i
proc storeRemaining*(module: PSym) =
if gSymbolFiles != v2Sf: return
w.module = module
for s in w.forwardedSyms:
assert sfForward notin s.flags
storeSym(w, s)
w.forwardedSyms.setLen 0
# ---------------- decoder -----------------------------------
type
TRodReader = object
module: PSym
#sstack: seq[(PSym, ptr PSym)] # a stack of symbols to process
#tstack: seq[(PType, ptr PType)] # a stack of types to process
#tmarks, smarks: IntSet
syms: Table[int, PSym] ## XXX make this more efficients
types: Table[int, PType]
cache: IdentCache
BlobReader = object
s: string
pos: int
PRodReader = var TRodReader
proc initRodReader(cache: IdentCache): TRodReader =
TRodReader(module: nil,
syms: initTable[int, PSym](), types: initTable[int, PType](),
cache: cache)
var gr = initRodReader(newIdentCache())
using
r: PRodReader
b: var BlobReader
proc loadSym(r; id: int, info: TLineInfo): PSym
proc loadType(r; id: int, info: TLineInfo): PType
proc decodeLineInfo(r; b; info: var TLineInfo) =
if b.s[b.pos] == '?':
inc(b.pos)
if b.s[b.pos] == ',': info.col = -1'i16
else: info.col = int16(decodeVInt(b.s, b.pos))
if b.s[b.pos] == ',':
inc(b.pos)
if b.s[b.pos] == ',': info.line = -1'i16
else: info.line = int16(decodeVInt(b.s, b.pos))
if b.s[b.pos] == ',':
inc(b.pos)
info.fileIndex = fromDbFileId(decodeVInt(b.s, b.pos))
proc skipNode(b) =
assert b.s[b.pos] == '('
var par = 0
var pos = b.pos+1
while true:
case b.s[pos]
of ')':
if par == 0: break
dec par
of '(': inc par
else: discard
inc pos
b.pos = pos+1 # skip ')'
proc decodeNodeLazyBody(r; b; fInfo: TLineInfo,
belongsTo: PSym): PNode =
result = nil
if b.s[b.pos] == '(':
inc(b.pos)
if b.s[b.pos] == ')':
inc(b.pos)
return # nil node
result = newNodeI(TNodeKind(decodeVInt(b.s, b.pos)), fInfo)
decodeLineInfo(r, b, result.info)
if b.s[b.pos] == '$':
inc(b.pos)
result.flags = cast[TNodeFlags](int32(decodeVInt(b.s, b.pos)))
if b.s[b.pos] == '^':
inc(b.pos)
var id = decodeVInt(b.s, b.pos)
result.typ = loadType(r, id, result.info)
case result.kind
of nkCharLit..nkUInt64Lit:
if b.s[b.pos] == '!':
inc(b.pos)
result.intVal = decodeVBiggestInt(b.s, b.pos)
of nkFloatLit..nkFloat64Lit:
if b.s[b.pos] == '!':
inc(b.pos)
var fl = decodeStr(b.s, b.pos)
result.floatVal = parseFloat(fl)
of nkStrLit..nkTripleStrLit:
if b.s[b.pos] == '!':
inc(b.pos)
result.strVal = decodeStr(b.s, b.pos)
else:
result.strVal = ""
of nkIdent:
if b.s[b.pos] == '!':
inc(b.pos)
var fl = decodeStr(b.s, b.pos)
result.ident = r.cache.getIdent(fl)
else:
internalError(result.info, "decodeNode: nkIdent")
of nkSym:
if b.s[b.pos] == '!':
inc(b.pos)
var id = decodeVInt(b.s, b.pos)
result.sym = loadSym(r, id, result.info)
else:
internalError(result.info, "decodeNode: nkSym")
else:
var i = 0
while b.s[b.pos] != ')':
when false:
if belongsTo != nil and i == bodyPos:
addSonNilAllowed(result, nil)
belongsTo.offset = b.pos
skipNode(b)
else:
discard
addSonNilAllowed(result, decodeNodeLazyBody(r, b, result.info, nil))
inc i
if b.s[b.pos] == ')': inc(b.pos)
else: internalError(result.info, "decodeNode: ')' missing")
else:
internalError(fInfo, "decodeNode: '(' missing " & $b.pos)
proc decodeNode(r; b; fInfo: TLineInfo): PNode =
result = decodeNodeLazyBody(r, b, fInfo, nil)
proc decodeLoc(r; b; loc: var TLoc, info: TLineInfo) =
if b.s[b.pos] == '<':
inc(b.pos)
if b.s[b.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
loc.k = TLocKind(decodeVInt(b.s, b.pos))
else:
loc.k = low(loc.k)
if b.s[b.pos] == '*':
inc(b.pos)
loc.storage = TStorageLoc(decodeVInt(b.s, b.pos))
else:
loc.storage = low(loc.storage)
if b.s[b.pos] == '$':
inc(b.pos)
loc.flags = cast[TLocFlags](int32(decodeVInt(b.s, b.pos)))
else:
loc.flags = {}
if b.s[b.pos] == '^':
inc(b.pos)
loc.lode = decodeNode(r, b, info)
# rrGetType(b, decodeVInt(b.s, b.pos), info)
else:
loc.lode = nil
if b.s[b.pos] == '!':
inc(b.pos)
loc.r = rope(decodeStr(b.s, b.pos))
else:
loc.r = nil
if b.s[b.pos] == '>': inc(b.pos)
else: internalError(info, "decodeLoc " & b.s[b.pos])
proc loadBlob(query: SqlQuery; id: int): BlobReader =
let blob = db.getValue(query, id)
if blob.len == 0:
internalError("symbolfiles: cannot find ID " & $ id)
result = BlobReader(pos: 0)
shallowCopy(result.s, blob)
proc loadType(r; id: int; info: TLineInfo): PType =
result = r.types.getOrDefault(id)
if result != nil: return result
var b = loadBlob(sql"select data from types where nimid = ?", id)
if b.s[b.pos] == '[':
inc(b.pos)
if b.s[b.pos] == ']':
inc(b.pos)
return # nil type
new(result)
result.kind = TTypeKind(decodeVInt(b.s, b.pos))
if b.s[b.pos] == '+':
inc(b.pos)
result.id = decodeVInt(b.s, b.pos)
setId(result.id)
#if debugIds: registerID(result)
else:
internalError(info, "decodeType: no id")
# here this also avoids endless recursion for recursive type
r.types[result.id] = result
if b.s[b.pos] == '(': result.n = decodeNode(r, b, unknownLineInfo())
if b.s[b.pos] == '$':
inc(b.pos)
result.flags = cast[TTypeFlags](int32(decodeVInt(b.s, b.pos)))
if b.s[b.pos] == '?':
inc(b.pos)
result.callConv = TCallingConvention(decodeVInt(b.s, b.pos))
if b.s[b.pos] == '*':
inc(b.pos)
result.owner = loadSym(r, decodeVInt(b.s, b.pos), info)
if b.s[b.pos] == '&':
inc(b.pos)
result.sym = loadSym(r, decodeVInt(b.s, b.pos), info)
if b.s[b.pos] == '/':
inc(b.pos)
result.size = decodeVInt(b.s, b.pos)
else:
result.size = -1
if b.s[b.pos] == '=':
inc(b.pos)
result.align = decodeVInt(b.s, b.pos).int16
else:
result.align = 2
if b.s[b.pos] == '\14':
inc(b.pos)
result.lockLevel = decodeVInt(b.s, b.pos).TLockLevel
else:
result.lockLevel = UnspecifiedLockLevel
if b.s[b.pos] == '\15':
inc(b.pos)
result.destructor = loadSym(r, decodeVInt(b.s, b.pos), info)
if b.s[b.pos] == '\16':
inc(b.pos)
result.deepCopy = loadSym(r, decodeVInt(b.s, b.pos), info)
if b.s[b.pos] == '\17':
inc(b.pos)
result.assignment = loadSym(r, decodeVInt(b.s, b.pos), info)
if b.s[b.pos] == '\18':
inc(b.pos)
result.sink = loadSym(r, decodeVInt(b.s, b.pos), info)
while b.s[b.pos] == '\19':
inc(b.pos)
let x = decodeVInt(b.s, b.pos)
doAssert b.s[b.pos] == '\20'
inc(b.pos)
let y = loadSym(r, decodeVInt(b.s, b.pos), info)
result.methods.safeAdd((x, y))
decodeLoc(r, b, result.loc, info)
while b.s[b.pos] == '^':
inc(b.pos)
if b.s[b.pos] == '(':
inc(b.pos)
if b.s[b.pos] == ')': inc(b.pos)
else: internalError(info, "decodeType ^(" & b.s[b.pos])
rawAddSon(result, nil)
else:
var d = decodeVInt(b.s, b.pos)
rawAddSon(result, loadType(r, d, info))
proc decodeLib(r; b; info: TLineInfo): PLib =
result = nil
if b.s[b.pos] == '|':
new(result)
inc(b.pos)
result.kind = TLibKind(decodeVInt(b.s, b.pos))
if b.s[b.pos] != '|': internalError("decodeLib: 1")
inc(b.pos)
result.name = rope(decodeStr(b.s, b.pos))
if b.s[b.pos] != '|': internalError("decodeLib: 2")
inc(b.pos)
result.path = decodeNode(r, b, info)
proc decodeInstantiations(r; b; info: TLineInfo;
s: var seq[PInstantiation]) =
while b.s[b.pos] == '\15':
inc(b.pos)
var ii: PInstantiation
new ii
ii.sym = loadSym(r, decodeVInt(b.s, b.pos), info)
ii.concreteTypes = @[]
while b.s[b.pos] == '\17':
inc(b.pos)
ii.concreteTypes.add loadType(r, decodeVInt(b.s, b.pos), info)
if b.s[b.pos] == '\20':
inc(b.pos)
ii.compilesId = decodeVInt(b.s, b.pos)
s.safeAdd ii
proc loadSymFromBlob(r; b; info: TLineInfo): PSym =
if b.s[b.pos] == '{':
inc(b.pos)
if b.s[b.pos] == '}':
inc(b.pos)
return # nil sym
var k = TSymKind(decodeVInt(b.s, b.pos))
var id: int
if b.s[b.pos] == '+':
inc(b.pos)
id = decodeVInt(b.s, b.pos)
setId(id)
else:
internalError(info, "decodeSym: no id")
var ident: PIdent
if b.s[b.pos] == '&':
inc(b.pos)
ident = r.cache.getIdent(decodeStr(b.s, b.pos))
else:
internalError(info, "decodeSym: no ident")
#echo "decoding: {", ident.s
new(result)
result.id = id
result.kind = k
result.name = ident # read the rest of the symbol description:
r.syms[result.id] = result
if b.s[b.pos] == '^':
inc(b.pos)
result.typ = loadType(r, decodeVInt(b.s, b.pos), info)
decodeLineInfo(r, b, result.info)
if b.s[b.pos] == '*':
inc(b.pos)
result.owner = loadSym(r, decodeVInt(b.s, b.pos), result.info)
if b.s[b.pos] == '$':
inc(b.pos)
result.flags = cast[TSymFlags](int32(decodeVInt(b.s, b.pos)))
if b.s[b.pos] == '@':
inc(b.pos)
result.magic = TMagic(decodeVInt(b.s, b.pos))
if b.s[b.pos] == '!':
inc(b.pos)
result.options = cast[TOptions](int32(decodeVInt(b.s, b.pos)))
else:
result.options = r.module.options
if b.s[b.pos] == '%':
inc(b.pos)
result.position = decodeVInt(b.s, b.pos)
if b.s[b.pos] == '`':
inc(b.pos)
result.offset = decodeVInt(b.s, b.pos)
else:
result.offset = - 1
decodeLoc(r, b, result.loc, result.info)
result.annex = decodeLib(r, b, info)
if b.s[b.pos] == '#':
inc(b.pos)
result.constraint = decodeNode(r, b, unknownLineInfo())
case result.kind
of skType, skGenericParam:
while b.s[b.pos] == '\14':
inc(b.pos)
result.typeInstCache.safeAdd loadType(r, decodeVInt(b.s, b.pos), result.info)
of routineKinds:
decodeInstantiations(r, b, result.info, result.procInstCache)
if b.s[b.pos] == '\16':
inc(b.pos)
result.gcUnsafetyReason = loadSym(r, decodeVInt(b.s, b.pos), result.info)
of skModule, skPackage:
decodeInstantiations(r, b, result.info, result.usedGenerics)
of skLet, skVar, skField, skForVar:
if b.s[b.pos] == '\18':
inc(b.pos)
result.guard = loadSym(r, decodeVInt(b.s, b.pos), result.info)
if b.s[b.pos] == '\19':
inc(b.pos)
result.bitsize = decodeVInt(b.s, b.pos).int16
else: discard
if b.s[b.pos] == '(':
#if result.kind in routineKinds:
# result.ast = decodeNodeLazyBody(b, result.info, result)
#else:
result.ast = decodeNode(r, b, result.info)
if sfCompilerProc in result.flags:
registerCompilerProc(result)
#echo "loading ", result.name.s
proc loadSym(r; id: int; info: TLineInfo): PSym =
result = r.syms.getOrDefault(id)
if result != nil: return result
var b = loadBlob(sql"select data from syms where nimid = ?", id)
result = loadSymFromBlob(r, b, info)
doAssert id == result.id, "symbol ID is not consistent!"
proc loadModuleSymTab(r; module: PSym) =
## goal: fill module.tab
gr.syms[module.id] = module
for row in db.fastRows(sql"select nimid, data from syms where module = ? and exported = 1", abs(module.id)):
let id = parseInt(row[0])
var s = r.syms.getOrDefault(id)
if s == nil:
var b = BlobReader(pos: 0)
shallowCopy(b.s, row[1])
s = loadSymFromBlob(r, b, module.info)
assert s != nil
strTableAdd(module.tab, s)
if sfSystemModule in module.flags:
magicsys.systemModule = module
proc loadNode*(module: PSym; index: int): PNode =
assert gSymbolFiles == v2Sf
if index == 0:
loadModuleSymTab(gr, module)
#index = parseInt db.getValue(
# sql"select min(id) from toplevelstmts where module = ?", abs module.id)
var b = BlobReader(pos: 0)
b.s = db.getValue(sql"select data from toplevelstmts where position = ? and module = ?",
index, abs module.id)
if b.s.len == 0:
db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId)
return nil # end marker
gr.module = module
result = decodeNode(gr, b, module.info)
proc addModuleDep*(module, fileIdx: int32; isIncludeFile: bool) =
if gSymbolFiles != v2Sf: return
let a = toDbFileId(module)
let b = toDbFileId(fileIdx)
db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)",
a, b, ord(isIncludeFile))
# --------------- Database model ---------------------------------------------
proc createDb() =
db.exec(sql"""
create table if not exists controlblock(
idgen integer not null
);
""")
db.exec(sql"""
create table if not exists filenames(
id integer primary key,
fullpath varchar(8000) not null,
fullHash varchar(256) not null
);
""")
db.exec sql"create index if not exists FilenameIx on filenames(fullpath);"
db.exec(sql"""
create table if not exists modules(
id integer primary key,
fullpath varchar(8000) not null,
interfHash varchar(256) not null,
fullHash varchar(256) not null,
created timestamp not null default (DATETIME('now'))
);""")
db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""")
db.exec(sql"""
create table if not exists deps(
id integer primary key,
module integer not null,
dependency integer not null,
isIncludeFile integer not null,
foreign key (module) references filenames(id),
foreign key (dependency) references filenames(id)
);""")
db.exec(sql"""create index if not exists DepsIx on deps(module);""")
db.exec(sql"""
create table if not exists types(
id integer primary key,
nimid integer not null,
module integer not null,
data blob not null,
foreign key (module) references module(id)
);
""")
db.exec sql"create index TypeByModuleIdx on types(module);"
db.exec sql"create index TypeByNimIdIdx on types(nimid);"
db.exec(sql"""
create table if not exists syms(
id integer primary key,
nimid integer not null,
module integer not null,
name varchar(256) not null,
data blob not null,
exported int not null,
foreign key (module) references module(id)
);
""")
db.exec sql"create index if not exists SymNameIx on syms(name);"
db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);"
db.exec sql"create index SymByModuleIdx on syms(module);"
db.exec sql"create index SymByNimIdIdx on syms(nimid);"
db.exec(sql"""
create table if not exists toplevelstmts(
id integer primary key,
position integer not null,
module integer not null,
data blob not null,
foreign key (module) references module(id)
);
""")
db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);"
db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);"
db.exec(sql"""
create table if not exists statics(
id integer primary key,
module integer not null,
data blob not null,
foreign key (module) references module(id)
);
""")
db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);"
db.exec sql"insert into controlblock(idgen) values (0)"
proc setupModuleCache* =
if gSymbolFiles != v2Sf: return
let dbfile = getNimcacheDir() / "rodfiles.db"
if not fileExists(dbfile):
db = open(connection=dbfile, user="nim", password="",
database="nim")
createDb()
else:
db = open(connection=dbfile, user="nim", password="",
database="nim")
db.exec(sql"pragma journal_mode=off")
db.exec(sql"pragma SYNCHRONOUS=off")
db.exec(sql"pragma LOCKING_MODE=exclusive")
let lastId = db.getValue(sql"select max(idgen) from controlblock")
if lastId.len > 0:
idgen.setId(parseInt lastId)

View file

@ -90,7 +90,8 @@
import
os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
ropes, idents, securehash, idgen, types, rodutils, memfiles, tables
ropes, idents, std / sha1, idgen, types, rodutils, memfiles, tables,
configuration
type
TReasonForRecompile* = enum ## all the reasons that can trigger recompilation
@ -126,8 +127,8 @@ type
s: cstring # mmap'ed file contents
options: TOptions
reason: TReasonForRecompile
modDeps: seq[int32]
files: seq[int32]
modDeps: seq[FileIndex]
files: seq[FileIndex]
dataIdx: int # offset of start of data section
convertersIdx: int # offset of start of converters section
initIdx, interfIdx, compilerProcsIdx, methodsIdx: int
@ -143,6 +144,7 @@ type
origFile: string
inViewMode: bool
cache*: IdentCache
config: ConfigRef
PRodReader* = ref TRodReader
@ -163,11 +165,11 @@ proc decodeLineInfo(r: PRodReader, info: var TLineInfo) =
else: info.col = int16(decodeVInt(r.s, r.pos))
if r.s[r.pos] == ',':
inc(r.pos)
if r.s[r.pos] == ',': info.line = -1'i16
else: info.line = int16(decodeVInt(r.s, r.pos))
if r.s[r.pos] == ',': info.line = 0'u16
else: info.line = uint16(decodeVInt(r.s, r.pos))
if r.s[r.pos] == ',':
inc(r.pos)
info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], info.line, info.col)
info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], int info.line, info.col)
proc skipNode(r: PRodReader) =
assert r.s[r.pos] == '('
@ -222,14 +224,14 @@ proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo,
var fl = decodeStr(r.s, r.pos)
result.ident = r.cache.getIdent(fl)
else:
internalError(result.info, "decodeNode: nkIdent")
internalError(r.config, result.info, "decodeNode: nkIdent")
of nkSym:
if r.s[r.pos] == '!':
inc(r.pos)
var id = decodeVInt(r.s, r.pos)
result.sym = rrGetSym(r, id, result.info)
else:
internalError(result.info, "decodeNode: nkSym")
internalError(r.config, result.info, "decodeNode: nkSym")
else:
var i = 0
while r.s[r.pos] != ')':
@ -241,9 +243,9 @@ proc decodeNodeLazyBody(r: PRodReader, fInfo: TLineInfo,
addSonNilAllowed(result, decodeNodeLazyBody(r, result.info, nil))
inc i
if r.s[r.pos] == ')': inc(r.pos)
else: internalError(result.info, "decodeNode: ')' missing")
else: internalError(r.config, result.info, "decodeNode: ')' missing")
else:
internalError(fInfo, "decodeNode: '(' missing " & $r.pos)
internalError(r.config, fInfo, "decodeNode: '(' missing " & $r.pos)
proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode =
result = decodeNodeLazyBody(r, fInfo, nil)
@ -277,7 +279,7 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
else:
loc.r = nil
if r.s[r.pos] == '>': inc(r.pos)
else: internalError(info, "decodeLoc " & r.s[r.pos])
else: internalError(r.config, info, "decodeLoc " & r.s[r.pos])
proc decodeType(r: PRodReader, info: TLineInfo): PType =
result = nil
@ -294,7 +296,7 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
setId(result.id)
if debugIds: registerID(result)
else:
internalError(info, "decodeType: no id")
internalError(r.config, info, "decodeType: no id")
# here this also avoids endless recursion for recursive type
idTablePut(gTypeTable, result, result)
if r.s[r.pos] == '(': result.n = decodeNode(r, unknownLineInfo())
@ -345,14 +347,14 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
doAssert r.s[r.pos] == '\20'
inc(r.pos)
let y = rrGetSym(r, decodeVInt(r.s, r.pos), info)
result.methods.safeAdd((x, y))
result.methods.add((x, y))
decodeLoc(r, result.loc, info)
while r.s[r.pos] == '^':
inc(r.pos)
if r.s[r.pos] == '(':
inc(r.pos)
if r.s[r.pos] == ')': inc(r.pos)
else: internalError(info, "decodeType ^(" & r.s[r.pos])
else: internalError(r.config, info, "decodeType ^(" & r.s[r.pos])
rawAddSon(result, nil)
else:
var d = decodeVInt(r.s, r.pos)
@ -364,10 +366,10 @@ proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
new(result)
inc(r.pos)
result.kind = TLibKind(decodeVInt(r.s, r.pos))
if r.s[r.pos] != '|': internalError("decodeLib: 1")
if r.s[r.pos] != '|': internalError(r.config, "decodeLib: 1")
inc(r.pos)
result.name = rope(decodeStr(r.s, r.pos))
if r.s[r.pos] != '|': internalError("decodeLib: 2")
if r.s[r.pos] != '|': internalError(r.config, "decodeLib: 2")
inc(r.pos)
result.path = decodeNode(r, info)
@ -385,7 +387,7 @@ proc decodeInstantiations(r: PRodReader; info: TLineInfo;
if r.s[r.pos] == '\20':
inc(r.pos)
ii.compilesId = decodeVInt(r.s, r.pos)
s.safeAdd ii
s.add ii
proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
var
@ -403,12 +405,12 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
id = decodeVInt(r.s, r.pos)
setId(id)
else:
internalError(info, "decodeSym: no id")
internalError(r.config, info, "decodeSym: no id")
if r.s[r.pos] == '&':
inc(r.pos)
ident = r.cache.getIdent(decodeStr(r.s, r.pos))
else:
internalError(info, "decodeSym: no ident")
internalError(r.config, info, "decodeSym: no ident")
#echo "decoding: {", ident.s
result = r.syms.getOrDefault(id)
if result == nil:
@ -417,7 +419,7 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
r.syms[result.id] = result
if debugIds: registerID(result)
elif result.id != id:
internalError(info, "decodeSym: wrong id")
internalError(r.config, info, "decodeSym: wrong id")
elif result.kind != skStub and not r.inViewMode:
# we already loaded the symbol
return
@ -465,7 +467,7 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
of skType, skGenericParam:
while r.s[r.pos] == '\14':
inc(r.pos)
result.typeInstCache.safeAdd rrGetType(r, decodeVInt(r.s, r.pos), result.info)
result.typeInstCache.add rrGetType(r, decodeVInt(r.s, r.pos), result.info)
of routineKinds:
decodeInstantiations(r, result.info, result.procInstCache)
if r.s[r.pos] == '\16':
@ -512,7 +514,7 @@ proc skipSection(r: PRodReader) =
else: discard
inc(r.pos)
else:
internalError("skipSection " & $r.line)
internalError(r.config, "skipSection " & $r.line)
proc rdWord(r: PRodReader): string =
result = ""
@ -530,7 +532,7 @@ proc newStub(r: PRodReader, name: string, id: int): PSym =
if debugIds: registerID(result)
proc processInterf(r: PRodReader, module: PSym) =
if r.interfIdx == 0: internalError("processInterf")
if r.interfIdx == 0: internalError(r.config, "processInterf")
r.pos = r.interfIdx
while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
var w = decodeStr(r.s, r.pos)
@ -543,7 +545,7 @@ proc processInterf(r: PRodReader, module: PSym) =
r.syms[s.id] = s
proc processCompilerProcs(r: PRodReader, module: PSym) =
if r.compilerProcsIdx == 0: internalError("processCompilerProcs")
if r.compilerProcsIdx == 0: internalError(r.config, "processCompilerProcs")
r.pos = r.compilerProcsIdx
while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
var w = decodeStr(r.s, r.pos)
@ -586,7 +588,7 @@ proc cmdChangeTriggersRecompilation(old, new: TCommands): bool =
# new command forces us to consider it here :-)
case old
of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
cmdCompileToJS, cmdCompileToPHP, cmdCompileToLLVM:
cmdCompileToJS, cmdCompileToLLVM:
if new in {cmdDoc, cmdCheck, cmdIdeTools, cmdPretty, cmdDef,
cmdInteractive}:
return false
@ -621,26 +623,26 @@ proc processRodFile(r: PRodReader, hash: SecureHash) =
of "OPTIONS":
inc(r.pos) # skip ':'
r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
if options.gOptions != r.options: r.reason = rrOptions
if r.config.options != r.options: r.reason = rrOptions
of "GOPTIONS":
inc(r.pos) # skip ':'
var dep = cast[TGlobalOptions](int32(decodeVInt(r.s, r.pos)))
if gGlobalOptions-harmlessOptions != dep-harmlessOptions:
if r.config.globalOptions-harmlessOptions != dep-harmlessOptions:
r.reason = rrOptions
of "CMD":
inc(r.pos) # skip ':'
var dep = cast[TCommands](int32(decodeVInt(r.s, r.pos)))
if cmdChangeTriggersRecompilation(dep, gCmd): r.reason = rrOptions
if cmdChangeTriggersRecompilation(dep, r.config.cmd): r.reason = rrOptions
of "DEFINES":
inc(r.pos) # skip ':'
d = 0
while r.s[r.pos] > '\x0A':
w = decodeStr(r.s, r.pos)
inc(d)
if not condsyms.isDefined(r.cache.getIdent(w)):
if not isDefined(r.config, w):
r.reason = rrDefines #MessageOut('not defined, but should: ' + w);
if r.s[r.pos] == ' ': inc(r.pos)
if d != countDefinedSymbols(): r.reason = rrDefines
if d != countDefinedSymbols(r.config.symbols): r.reason = rrDefines
of "FILES":
inc(r.pos, 2) # skip "(\10"
inc(r.line)
@ -648,7 +650,7 @@ proc processRodFile(r: PRodReader, hash: SecureHash) =
let finalPath = decodeStr(r.s, r.pos)
#let resolvedPath = relativePath.findModule(r.origFile)
#let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
r.files.add(finalPath.fileInfoIdx)
r.files.add(fileInfoIdx(r.config, finalPath))
inc(r.pos) # skip #10
inc(r.line)
if r.s[r.pos] == ')': inc(r.pos)
@ -696,7 +698,7 @@ proc processRodFile(r: PRodReader, hash: SecureHash) =
r.initIdx = r.pos + 2 # "(\10"
skipSection(r)
else:
internalError("invalid section: '" & section &
internalError(r.config, "invalid section: '" & section &
"' at " & $r.line & " in " & r.filename)
#MsgWriteln("skipping section: " & section &
# " at " & $r.line & " in " & r.filename)
@ -712,9 +714,11 @@ proc startsWith(buf: cstring, token: string, pos = 0): bool =
result = s == token.len
proc newRodReader(modfilename: string, hash: SecureHash,
readerIndex: int; cache: IdentCache): PRodReader =
readerIndex: int; cache: IdentCache;
config: ConfigRef): PRodReader =
new(result)
result.cache = cache
result.config = config
try:
result.memfile = memfiles.open(modfilename)
except OSError:
@ -757,13 +761,13 @@ proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType =
# load the type:
var oldPos = r.pos
var d = iiTableGet(r.index.tab, id)
if d == InvalidKey: internalError(info, "rrGetType")
if d == InvalidKey: internalError(r.config, info, "rrGetType")
r.pos = d + r.dataIdx
result = decodeType(r, info)
r.pos = oldPos
type
TFileModuleRec{.final.} = object
TFileModuleRec = object
filename*: string
reason*: TReasonForRecompile
rd*: PRodReader
@ -776,7 +780,7 @@ var gMods*: TFileModuleMap = @[]
proc decodeSymSafePos(rd: PRodReader, offset: int, info: TLineInfo): PSym =
# all compiled modules
if rd.dataIdx == 0: internalError(info, "dataIdx == 0")
if rd.dataIdx == 0: internalError(rd.config, info, "dataIdx == 0")
var oldPos = rd.pos
rd.pos = offset + rd.dataIdx
result = decodeSym(rd, info)
@ -811,8 +815,9 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
if moduleID < 0:
var x = ""
encodeVInt(id, x)
internalError(info, "missing from both indexes: +" & x)
internalError(r.config, info, "missing from both indexes: +" & x)
var rd = getReader(moduleID)
doAssert rd != nil
d = iiTableGet(rd.index.tab, id)
if d != InvalidKey:
result = decodeSymSafePos(rd, d, info)
@ -820,14 +825,14 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
var x = ""
encodeVInt(id, x)
when false: findSomeWhere(id)
internalError(info, "rrGetSym: no reader found: +" & x)
internalError(r.config, info, "rrGetSym: no reader found: +" & x)
else:
# own symbol:
result = decodeSymSafePos(r, d, info)
if result != nil and result.kind == skStub: rawLoadStub(result)
proc loadInitSection*(r: PRodReader): PNode =
if r.initIdx == 0 or r.dataIdx == 0: internalError("loadInitSection")
if r.initIdx == 0 or r.dataIdx == 0: internalError(r.config, "loadInitSection")
var oldPos = r.pos
r.pos = r.initIdx
result = newNode(nkStmtList)
@ -844,7 +849,7 @@ proc loadConverters(r: PRodReader) =
# We have to ensure that no exported converter is a stub anymore, and the
# import mechanism takes care of the rest.
if r.convertersIdx == 0 or r.dataIdx == 0:
internalError("importConverters")
internalError(r.config, "importConverters")
r.pos = r.convertersIdx
while r.s[r.pos] > '\x0A':
var d = decodeVInt(r.s, r.pos)
@ -853,36 +858,36 @@ proc loadConverters(r: PRodReader) =
proc loadMethods(r: PRodReader) =
if r.methodsIdx == 0 or r.dataIdx == 0:
internalError("loadMethods")
internalError(r.config, "loadMethods")
r.pos = r.methodsIdx
while r.s[r.pos] > '\x0A':
var d = decodeVInt(r.s, r.pos)
r.methods.add(rrGetSym(r, d, unknownLineInfo()))
if r.s[r.pos] == ' ': inc(r.pos)
proc getHash*(fileIdx: int32): SecureHash =
if fileIdx <% gMods.len and gMods[fileIdx].hashDone:
return gMods[fileIdx].hash
proc getHash*(fileIdx: FileIndex): SecureHash =
if fileIdx.int32 <% gMods.len and gMods[fileIdx.int32].hashDone:
return gMods[fileIdx.int32].hash
result = secureHashFile(fileIdx.toFullPath)
if fileIdx >= gMods.len: setLen(gMods, fileIdx+1)
gMods[fileIdx].hash = result
if fileIdx.int32 >= gMods.len: setLen(gMods, fileIdx.int32+1)
gMods[fileIdx.int32].hash = result
template growCache*(cache, pos) =
if cache.len <= pos: cache.setLen(pos+1)
proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile =
proc checkDep(fileIdx: FileIndex; cache: IdentCache; conf: ConfigRef): TReasonForRecompile =
assert fileIdx != InvalidFileIDX
growCache gMods, fileIdx
if gMods[fileIdx].reason != rrEmpty:
growCache gMods, fileIdx.int32
if gMods[fileIdx.int32].reason != rrEmpty:
# reason has already been computed for this module:
return gMods[fileIdx].reason
return gMods[fileIdx.int32].reason
let filename = fileIdx.toFilename
var hash = getHash(fileIdx)
gMods[fileIdx].reason = rrNone # we need to set it here to avoid cycles
gMods[fileIdx.int32].reason = rrNone # we need to set it here to avoid cycles
result = rrNone
var rodfile = toGeneratedFile(filename.withPackageName, RodExt)
var r = newRodReader(rodfile, hash, fileIdx, cache)
var rodfile = toGeneratedFile(conf, conf.withPackageName(filename), RodExt)
var r = newRodReader(rodfile, hash, fileIdx.int32, cache, conf)
if r == nil:
result = (if existsFile(rodfile): rrRodInvalid else: rrRodDoesNotExist)
else:
@ -893,32 +898,31 @@ proc checkDep(fileIdx: int32; cache: IdentCache): TReasonForRecompile =
# NOTE: we need to process the entire module graph so that no ID will
# be used twice! However, compilation speed does not suffer much from
# this, since results are cached.
var res = checkDep(systemFileIdx, cache)
var res = checkDep(systemFileIdx, cache, conf)
if res != rrNone: result = rrModDeps
for i in countup(0, high(r.modDeps)):
res = checkDep(r.modDeps[i], cache)
res = checkDep(r.modDeps[i], cache, conf)
if res != rrNone:
result = rrModDeps
# we cannot break here, because of side-effects of `checkDep`
if result != rrNone:
rawMessage(hintProcessing, reasonToFrmt[result] % filename)
if result != rrNone or optForceFullMake in gGlobalOptions:
rawMessage(conf, hintProcessing, reasonToFrmt[result] % filename)
if result != rrNone or optForceFullMake in conf.globalOptions:
# recompilation is necessary:
if r != nil: memfiles.close(r.memfile)
r = nil
gMods[fileIdx].rd = r
gMods[fileIdx].reason = result # now we know better
gMods[fileIdx.int32].rd = r
gMods[fileIdx.int32].reason = result # now we know better
proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader =
let fileIdx = module.fileIdx
if gSymbolFiles in {disabledSf, writeOnlySf}:
proc handleSymbolFile*(module: PSym; cache: IdentCache; conf: ConfigRef): PRodReader =
if conf.symbolFiles in {disabledSf, writeOnlySf, v2Sf}:
module.id = getID()
return nil
idgen.loadMaxIds(options.gProjectPath / options.gProjectName)
discard checkDep(fileIdx, cache)
if gMods[fileIdx].reason == rrEmpty: internalError("handleSymbolFile")
result = gMods[fileIdx].rd
idgen.loadMaxIds(conf, conf.projectPath / conf.projectName)
let fileIdx = module.fileIdx
discard checkDep(fileIdx, cache, conf)
#if gMods[fileIdx.int32].reason == rrEmpty: internalError("handleSymbolFile")
result = gMods[fileIdx.int32].rd
if result != nil:
module.id = result.moduleID
result.syms[module.id] = module
@ -930,19 +934,20 @@ proc handleSymbolFile*(module: PSym; cache: IdentCache): PRodReader =
module.id = getID()
proc rawLoadStub(s: PSym) =
if s.kind != skStub: internalError("loadStub")
assert s.kind == skStub
#if s.kind != skStub: internalError("loadStub")
var rd = gMods[s.position].rd
var theId = s.id # used for later check
var d = iiTableGet(rd.index.tab, s.id)
if d == InvalidKey: internalError("loadStub: invalid key")
#if d == InvalidKey: internalError("loadStub: invalid key")
var rs = decodeSymSafePos(rd, d, unknownLineInfo())
if rs != s:
#echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2),
# "\ns: ", toHex(cast[int](s.position), int.sizeof * 2)
internalError(rs.info, "loadStub: wrong symbol")
elif rs.id != theId:
internalError(rs.info, "loadStub: wrong ID")
#MessageOut('loaded stub: ' + s.name.s);
when false:
if rs != s:
#echo "rs: ", toHex(cast[int](rs.position), int.sizeof * 2),
# "\ns: ", toHex(cast[int](s.position), int.sizeof * 2)
internalError(rs.info, "loadStub: wrong symbol")
elif rs.id != theId:
internalError(rs.info, "loadStub: wrong ID")
proc loadStub*(s: PSym) =
## loads the stub symbol `s`.
@ -1030,9 +1035,8 @@ proc writeSym(f: File; s: PSym) =
if s.magic != mNone:
f.write('@')
f.write($s.magic)
if s.options != gOptions:
f.write('!')
f.write($s.options)
f.write('!')
f.write($s.options)
if s.position != 0:
f.write('%')
f.write($s.position)
@ -1083,9 +1087,10 @@ proc writeType(f: File; t: PType) =
f.write("]\n")
proc viewFile(rodfile: string) =
var r = newRodReader(rodfile, secureHash(""), 0, newIdentCache())
let conf = newConfigRef()
var r = newRodReader(rodfile, secureHash(""), 0, newIdentCache(), conf)
if r == nil:
rawMessage(errGenerated, "cannot open file (or maybe wrong version):" &
rawMessage(conf, errGenerated, "cannot open file (or maybe wrong version):" &
rodfile)
return
r.inViewMode = true
@ -1133,9 +1138,9 @@ proc viewFile(rodfile: string) =
outf.write("FILES(\n")
while r.s[r.pos] != ')':
let relativePath = decodeStr(r.s, r.pos)
let resolvedPath = relativePath.findModule(r.origFile)
let resolvedPath = findModule(conf, relativePath, r.origFile)
let finalPath = if resolvedPath.len > 0: resolvedPath else: relativePath
r.files.add(finalPath.fileInfoIdx)
r.files.add(fileInfoIdx(conf, finalPath))
inc(r.pos) # skip #10
inc(r.line)
outf.writeLine finalPath
@ -1152,7 +1157,7 @@ proc viewFile(rodfile: string) =
if r.s[r.pos] == '\x0A':
inc(r.pos)
inc(r.line)
outf.write(w, " ", inclHash, "\n")
outf.write(w.int32, " ", inclHash, "\n")
if r.s[r.pos] == ')': inc(r.pos)
outf.write(")\n")
of "DEPS":
@ -1162,7 +1167,7 @@ proc viewFile(rodfile: string) =
let v = int32(decodeVInt(r.s, r.pos))
r.modDeps.add(r.files[v])
if r.s[r.pos] == ' ': inc(r.pos)
outf.write(" ", r.files[v])
outf.write(" ", r.files[v].int32)
outf.write("\n")
of "INTERF", "COMPILERPROCS":
inc r.pos, 2
@ -1227,7 +1232,7 @@ proc viewFile(rodfile: string) =
if r.s[r.pos] == ')': inc r.pos
outf.write("<not supported by viewer>)\n")
else:
internalError("invalid section: '" & section &
internalError(r.config, "invalid section: '" & section &
"' at " & $r.line & " in " & r.filename)
skipSection(r)
if r.s[r.pos] == '\x0A':
@ -1236,4 +1241,4 @@ proc viewFile(rodfile: string) =
outf.close
when isMainModule:
viewFile(paramStr(1).addFileExt(rodExt))
viewFile(paramStr(1).addFileExt(RodExt))

View file

@ -8,18 +8,22 @@
#
## Serialization utilities for the compiler.
import strutils
import strutils, math
proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
if f != f:
case classify(f)
of fcNaN:
result = "NAN"
elif f == 0.0:
of fcNegZero:
result = "-0.0" & literalPostfix
of fcZero:
result = "0.0" & literalPostfix
elif f == 0.5 * f:
if f > 0.0: result = "INF"
else: result = "-INF"
of fcInf:
result = "INF"
of fcNegInf:
result = "-INF"
else:
when defined(nimNoArrayToCstringConversion):
result = newString(81)
@ -63,6 +67,8 @@ proc decodeStr*(s: cstring, pos: var int): string =
const
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
{.push overflowChecks: off.}
# since negative numbers require a leading '-' they use up 1 byte. Thus we
# subtract/add `vintDelta` here to save space for small negative numbers
# which are common in ROD files:
@ -127,6 +133,8 @@ proc decodeVInt*(s: cstring, pos: var int): int =
proc decodeVBiggestInt*(s: cstring, pos: var int): BiggestInt =
decodeIntImpl()
{.pop.}
iterator decodeVIntArray*(s: cstring): int =
var i = 0
while s[i] != '\0':

View file

@ -13,7 +13,7 @@
import
intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform,
condsyms, ropes, idents, securehash, rodread, passes, idgen,
condsyms, ropes, idents, std / sha1, rodread, passes, idgen,
rodutils, modulepaths
from modulegraphs import ModuleGraph
@ -37,12 +37,13 @@ type
files: TStringSeq
origFile: string
cache: IdentCache
config: ConfigRef
PRodWriter = ref TRodWriter
proc getDefines(): string =
proc getDefines(conf: ConfigRef): string =
result = ""
for d in definedSymbolNames():
for d in definedSymbolNames(conf.symbols):
if result.len != 0: add(result, " ")
add(result, d)
@ -55,10 +56,12 @@ proc fileIdx(w: PRodWriter, filename: string): int =
w.files[result] = filename
template filename*(w: PRodWriter): string =
w.module.filename
toFilename(FileIndex w.module.position)
proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter =
proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache;
config: ConfigRef): PRodWriter =
new(result)
result.config = config
result.sstack = @[]
result.tstack = @[]
initIiTable(result.index.tab)
@ -67,8 +70,8 @@ proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter
result.imports.r = ""
result.hash = hash
result.module = module
result.defines = getDefines()
result.options = options.gOptions
result.defines = getDefines(config)
result.options = config.options
result.files = @[]
result.inclDeps = ""
result.modDeps = ""
@ -83,14 +86,14 @@ proc newRodWriter(hash: SecureHash, module: PSym; cache: IdentCache): PRodWriter
proc addModDep(w: PRodWriter, dep: string; info: TLineInfo) =
if w.modDeps.len != 0: add(w.modDeps, ' ')
let resolved = dep.findModule(info.toFullPath)
let resolved = findModule(w.config, dep, info.toFullPath)
encodeVInt(fileIdx(w, resolved), w.modDeps)
const
rodNL = "\x0A"
proc addInclDep(w: PRodWriter, dep: string; info: TLineInfo) =
let resolved = dep.findModule(info.toFullPath)
let resolved = findModule(w.config, dep, info.toFullPath)
encodeVInt(fileIdx(w, resolved), w.inclDeps)
add(w.inclDeps, " ")
encodeStr($secureHashFile(resolved), w.inclDeps)
@ -125,14 +128,14 @@ proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result.add('?')
encodeVInt(n.info.col, result)
result.add(',')
encodeVInt(n.info.line, result)
encodeVInt(int n.info.line, result)
result.add(',')
encodeVInt(fileIdx(w, toFullPath(n.info)), result)
elif fInfo.line != n.info.line:
result.add('?')
encodeVInt(n.info.col, result)
result.add(',')
encodeVInt(n.info.line, result)
encodeVInt(int n.info.line, result)
elif fInfo.col != n.info.col:
result.add('?')
encodeVInt(n.info.col, result)
@ -201,7 +204,7 @@ proc encodeType(w: PRodWriter, t: PType, result: var string) =
result.add("[]")
return
# we need no surrounding [] here because the type is in a line of its own
if t.kind == tyForward: internalError("encodeType: tyForward")
if t.kind == tyForward: internalError(w.config, "encodeType: tyForward")
# for the new rodfile viewer we use a preceding [ so that the data section
# can easily be disambiguated:
add(result, '[')
@ -303,7 +306,7 @@ proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
result.add('?')
if s.info.col != -1'i16: encodeVInt(s.info.col, result)
result.add(',')
if s.info.line != -1'i16: encodeVInt(s.info.line, result)
if s.info.line != 0'u16: encodeVInt(int s.info.line, result)
result.add(',')
encodeVInt(fileIdx(w, toFullPath(s.info)), result)
if s.owner != nil:
@ -390,9 +393,9 @@ proc symStack(w: PRodWriter): int =
inc result
elif iiTableGet(w.index.tab, s.id) == InvalidKey:
var m = getModule(s)
if m == nil and s.kind != skPackage and sfGenSym notin s.flags:
internalError("symStack: module nil: " & s.name.s)
if s.kind == skPackage or {sfFromGeneric, sfGenSym} * s.flags != {} or m.id == w.module.id:
#if m == nil and s.kind != skPackage and sfGenSym notin s.flags:
# internalError("symStack: module nil: " & s.name.s & " " & $s.kind & " ID " & $s.id)
if m == nil or s.kind == skPackage or {sfFromGeneric, sfGenSym} * s.flags != {} or m.id == w.module.id:
# put definition in here
var L = w.data.len
addToIndex(w.index, s.id, L)
@ -411,7 +414,7 @@ proc symStack(w: PRodWriter): int =
add(w.compilerProcs, ' ')
encodeVInt(s.id, w.compilerProcs)
add(w.compilerProcs, rodNL)
if s.kind == skConverter or hasPattern(s):
if s.kind == skConverter or (s.ast != nil and hasPattern(s)):
if w.converters.len != 0: add(w.converters, ' ')
encodeVInt(s.id, w.converters)
if s.kind == skMethod and sfDispatcher notin s.flags:
@ -454,7 +457,7 @@ proc processStacks(w: PRodWriter, finalPass: bool) =
oldS = slen
oldT = tlen
if finalPass and (oldS != 0 or oldT != 0):
internalError("could not serialize some forwarded symbols/types")
internalError(w.config, "could not serialize some forwarded symbols/types")
proc rawAddInterfaceSym(w: PRodWriter, s: PSym) =
pushSym(w, s)
@ -476,8 +479,8 @@ proc addStmt(w: PRodWriter, n: PNode) =
proc writeRod(w: PRodWriter) =
processStacks(w, true)
var f: File
if not open(f, completeGeneratedFilePath(changeFileExt(
w.filename.withPackageName, RodExt)),
if not open(f, completeGeneratedFilePath(w.config, changeFileExt(
withPackageName(w.config, w.filename), RodExt)),
fmWrite):
#echo "couldn't write rod file for: ", w.filename
return
@ -506,12 +509,12 @@ proc writeRod(w: PRodWriter) =
f.write(rodNL)
var goptions = "GOPTIONS:"
encodeVInt(cast[int32](gGlobalOptions), goptions)
encodeVInt(cast[int32](w.config.globalOptions), goptions)
f.write(goptions)
f.write(rodNL)
var cmd = "CMD:"
encodeVInt(cast[int32](gCmd), cmd)
encodeVInt(cast[int32](w.config.cmd), cmd)
f.write(cmd)
f.write(rodNL)
@ -587,17 +590,17 @@ proc process(c: PPassContext, n: PNode): PNode =
of nkProcDef, nkFuncDef, nkIteratorDef, nkConverterDef,
nkTemplateDef, nkMacroDef:
let s = n.sons[namePos].sym
if s == nil: internalError(n.info, "rodwrite.process")
if s == nil: internalError(w.config, n.info, "rodwrite.process")
if n.sons[bodyPos] == nil:
internalError(n.info, "rodwrite.process: body is nil")
internalError(w.config, n.info, "rodwrite.process: body is nil")
if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or
sfForward notin s.flags:
addInterfaceSym(w, s)
of nkMethodDef:
let s = n.sons[namePos].sym
if s == nil: internalError(n.info, "rodwrite.process")
if s == nil: internalError(w.config, n.info, "rodwrite.process")
if n.sons[bodyPos] == nil:
internalError(n.info, "rodwrite.process: body is nil")
internalError(w.config, n.info, "rodwrite.process: body is nil")
if n.sons[bodyPos].kind != nkEmpty or s.magic != mNone or
sfForward notin s.flags:
pushSym(w, s)
@ -612,7 +615,7 @@ proc process(c: PPassContext, n: PNode): PNode =
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if a.sons[0].kind != nkSym: internalError(a.info, "rodwrite.process")
if a.sons[0].kind != nkSym: internalError(w.config, a.info, "rodwrite.process")
var s = a.sons[0].sym
addInterfaceSym(w, s)
# this takes care of enum fields too
@ -627,22 +630,22 @@ proc process(c: PPassContext, n: PNode): PNode =
# end
of nkImportStmt:
for i in countup(0, sonsLen(n) - 1):
addModDep(w, getModuleName(n.sons[i]), n.info)
addModDep(w, getModuleName(w.config, n.sons[i]), n.info)
addStmt(w, n)
of nkFromStmt, nkImportExceptStmt:
addModDep(w, getModuleName(n.sons[0]), n.info)
addModDep(w, getModuleName(w.config, n.sons[0]), n.info)
addStmt(w, n)
of nkIncludeStmt:
for i in countup(0, sonsLen(n) - 1):
addInclDep(w, getModuleName(n.sons[i]), n.info)
addInclDep(w, getModuleName(w.config, n.sons[i]), n.info)
of nkPragma:
addStmt(w, n)
else:
discard
proc myOpen(g: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
if module.id < 0: internalError("rodwrite: module ID not set")
var w = newRodWriter(module.fileIdx.getHash, module, cache)
if module.id < 0: internalError(g.config, "rodwrite: module ID not set")
var w = newRodWriter(rodread.getHash FileIndex module.position, module, cache, g.config)
rawAddInterfaceSym(w, module)
result = w
@ -650,7 +653,7 @@ proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
result = process(c, n)
var w = PRodWriter(c)
writeRod(w)
idgen.saveMaxIds(options.gProjectPath / options.gProjectName)
idgen.saveMaxIds(graph.config, graph.config.projectPath / graph.config.projectName)
const rodwritePass* = makePass(open = myOpen, close = myClose, process = process)

View file

@ -188,11 +188,11 @@ iterator leaves*(r: Rope): string =
var stack = @[r]
while stack.len > 0:
var it = stack.pop
while isNil(it.data):
while it.left != nil:
assert it.right != nil
stack.add(it.right)
it = it.left
assert(it != nil)
assert(it.data != nil)
yield it.data
iterator items*(r: Rope): char =
@ -251,7 +251,7 @@ proc `%`*(frmt: FormatStr, args: openArray[Rope]): Rope =
while true:
j = j * 10 + ord(frmt[i]) - ord('0')
inc(i)
if frmt[i] notin {'0'..'9'}: break
if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
num = j
if j > high(args) + 1:
errorHandler(rInvalidFormatStr, $(j))

View file

@ -13,7 +13,7 @@
import
ast, modules, idents, passes, passaux, condsyms,
options, nimconf, sem, semdata, llstream, vm, vmdef, commands, msgs,
os, times, osproc, wordrecg, strtabs, modulegraphs
os, times, osproc, wordrecg, strtabs, modulegraphs, configuration
# we support 'cmpIgnoreStyle' natively for efficiency:
from strutils import cmpIgnoreStyle, contains
@ -26,11 +26,12 @@ proc listDirs(a: VmArgs, filter: set[PathComponent]) =
setResult(a, result)
proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
config: ConfigRef = nil): PEvalContext =
graph: ModuleGraph): PEvalContext =
# For Nimble we need to export 'setupVM'.
result = newCtx(module, cache)
result = newCtx(module, cache, graph)
result.mode = emRepl
registerAdditionalOps(result)
let conf = graph.config
# captured vars:
var errorMsg: string
@ -70,11 +71,16 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
setResult(a, os.getCurrentDir())
cbos moveFile:
os.moveFile(getString(a, 0), getString(a, 1))
cbos moveDir:
os.moveDir(getString(a, 0), getString(a, 1))
cbos copyFile:
os.copyFile(getString(a, 0), getString(a, 1))
cbos copyDir:
os.copyDir(getString(a, 0), getString(a, 1))
cbos getLastModificationTime:
# depends on Time's implementation!
setResult(a, int64(getLastModificationTime(getString(a, 0))))
setResult(a, getLastModificationTime(getString(a, 0)).toUnix)
cbos findExe:
setResult(a, os.findExe(getString(a, 0)))
cbos rawExec:
setResult(a, osproc.execCmd getString(a, 0))
@ -83,6 +89,8 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
setResult(a, os.getEnv(a.getString 0, a.getString 1))
cbconf existsEnv:
setResult(a, os.existsEnv(a.getString 0))
cbconf putEnv:
os.putEnv(a.getString 0, a.getString 1)
cbconf dirExists:
setResult(a, os.dirExists(a.getString 0))
cbconf fileExists:
@ -91,13 +99,13 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
cbconf thisDir:
setResult(a, vthisDir)
cbconf put:
options.setConfigVar(getString(a, 0), getString(a, 1))
options.setConfigVar(conf, getString(a, 0), getString(a, 1))
cbconf get:
setResult(a, options.getConfigVar(a.getString 0))
setResult(a, options.getConfigVar(conf, a.getString 0))
cbconf exists:
setResult(a, options.existsConfigVar(a.getString 0))
setResult(a, options.existsConfigVar(conf, a.getString 0))
cbconf nimcacheDir:
setResult(a, options.getNimcacheDir())
setResult(a, options.getNimcacheDir(conf))
cbconf paramStr:
setResult(a, os.paramStr(int a.getInt 0))
cbconf paramCount:
@ -107,67 +115,66 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
cbconf cmpIgnoreCase:
setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
cbconf setCommand:
options.command = a.getString 0
conf.command = a.getString 0
let arg = a.getString 1
if arg.len > 0:
gProjectName = arg
conf.projectName = arg
let path =
if gProjectName.isAbsolute: gProjectName
else: gProjectPath / gProjectName
if conf.projectName.isAbsolute: conf.projectName
else: conf.projectPath / conf.projectName
try:
gProjectFull = canonicalizePath(path)
conf.projectFull = canonicalizePath(conf, path)
except OSError:
gProjectFull = path
conf.projectFull = path
cbconf getCommand:
setResult(a, options.command)
setResult(a, conf.command)
cbconf switch:
processSwitch(a.getString 0, a.getString 1, passPP, module.info)
processSwitch(a.getString 0, a.getString 1, passPP, module.info, conf)
cbconf hintImpl:
processSpecificNote(a.getString 0, wHint, passPP, module.info,
a.getString 1)
a.getString 1, conf)
cbconf warningImpl:
processSpecificNote(a.getString 0, wWarning, passPP, module.info,
a.getString 1)
a.getString 1, conf)
cbconf patchFile:
let key = a.getString(0) & "_" & a.getString(1)
var val = a.getString(2).addFileExt(NimExt)
if {'$', '~'} in val:
val = pathSubs(val, vthisDir)
val = pathSubs(conf, val, vthisDir)
elif not isAbsolute(val):
val = vthisDir / val
gModuleOverrides[key] = val
conf.moduleOverrides[key] = val
cbconf selfExe:
setResult(a, os.getAppFilename())
cbconf cppDefine:
if config != nil:
options.cppDefine(config, a.getString(0))
options.cppDefine(conf, a.getString(0))
proc runNimScript*(cache: IdentCache; scriptName: string;
freshDefines=true; config: ConfigRef=nil) =
rawMessage(hintConf, scriptName)
freshDefines=true; conf: ConfigRef) =
rawMessage(conf, hintConf, scriptName)
passes.gIncludeFile = includeModule
passes.gImportModule = importModule
let graph = newModuleGraph(config)
if freshDefines: initDefines()
let graph = newModuleGraph(conf)
if freshDefines: initDefines(conf.symbols)
defineSymbol("nimscript")
defineSymbol("nimconfig")
defineSymbol(conf.symbols, "nimscript")
defineSymbol(conf.symbols, "nimconfig")
registerPass(semPass)
registerPass(evalPass)
searchPaths.add(options.libpath)
conf.searchPaths.add(conf.libpath)
var m = graph.makeModule(scriptName)
incl(m.flags, sfMainModule)
vm.globalCtx = setupVM(m, cache, scriptName, config)
vm.globalCtx = setupVM(m, cache, scriptName, graph)
graph.compileSystemModule(cache)
discard graph.processModule(m, llStreamOpen(scriptName, fmRead), nil, cache)
# ensure we load 'system.nim' again for the real non-config stuff!
resetSystemArtifacts()
resetSystemArtifacts(graph)
vm.globalCtx = nil
# do not remove the defined symbols
#initDefines()
undefSymbol("nimscript")
undefSymbol("nimconfig")
undefSymbol(conf.symbols, "nimscript")
undefSymbol(conf.symbols, "nimconfig")

View file

@ -16,7 +16,7 @@ import
procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
evaltempl, patterns, parampatterns, sempass2, nimfix.pretty, semmacrosanity,
semparallel, lowerings, pluginsupport, plugins.active
semparallel, lowerings, pluginsupport, plugins.active, rod, configuration
from modulegraphs import ModuleGraph
@ -33,11 +33,12 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc semProcBody(c: PContext, n: PNode): PNode
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode
proc changeType(n: PNode, newType: PType, check: bool)
proc changeType(c: PContext; n: PNode, newType: PType, check: bool)
proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode
proc semTypeNode(c: PContext, n: PNode, prev: PType): PType
proc semStmt(c: PContext, n: PNode): PNode
proc semOpAux(c: PContext, n: PNode)
proc semParamList(c: PContext, n, genericParams: PNode, s: PSym)
proc addParams(c: PContext, n: PNode, kind: TSymKind)
proc maybeAddResult(c: PContext, s: PSym, n: PNode)
@ -64,14 +65,14 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
# templates perform some quick check whether the cursor is actually in
# the generic or template.
when defined(nimsuggest):
if gCmd == cmdIdeTools and requiresCheck:
if c.config.cmd == cmdIdeTools and requiresCheck:
#if optIdeDebug in gGlobalOptions:
# echo "passing to safeSemExpr: ", renderTree(n)
discard safeSemExpr(c, n)
proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
if arg.typ.isNil:
localError(arg.info, errExprXHasNoType,
localError(c.config, arg.info, "expression has no type: " &
renderTree(arg, {renderNoComments}))
# error correction:
result = copyTree(arg)
@ -79,14 +80,14 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
else:
result = indexTypesMatch(c, formal, arg.typ, arg)
if result == nil:
typeMismatch(info, formal, arg.typ)
typeMismatch(c.config, info, formal, arg.typ)
# error correction:
result = copyTree(arg)
result.typ = formal
else:
let x = result.skipConv
if x.kind == nkPar and formal.kind != tyExpr:
changeType(x, formal, check=true)
if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr:
changeType(c, x, formal, check=true)
else:
result = skipHiddenSubConv(result)
#result.typ = takeType(formal, arg.typ)
@ -102,8 +103,8 @@ proc commonType*(x, y: PType): PType =
# if expressions, etc.:
if x == nil: return x
if y == nil: return y
var a = skipTypes(x, {tyGenericInst, tyAlias})
var b = skipTypes(y, {tyGenericInst, tyAlias})
var a = skipTypes(x, {tyGenericInst, tyAlias, tySink})
var b = skipTypes(y, {tyGenericInst, tyAlias, tySink})
result = x
if a.kind in {tyExpr, tyNil}: result = y
elif b.kind in {tyExpr, tyNil}: result = x
@ -153,14 +154,17 @@ proc commonType*(x, y: PType): PType =
if a.kind in {tyRef, tyPtr}:
k = a.kind
if b.kind != a.kind: return x
a = a.lastSon
b = b.lastSon
# bug #7601, array construction of ptr generic
a = a.lastSon.skipTypes({tyGenericInst})
b = b.lastSon.skipTypes({tyGenericInst})
if a.kind == tyObject and b.kind == tyObject:
result = commonSuperclass(a, b)
# this will trigger an error later:
if result.isNil or result == a: return x
if result == b: return y
if k != tyNone:
# bug #7906, tyRef/tyPtr + tyGenericInst of ref/ptr object ->
# ill-formed AST, no need for additional tyRef/tyPtr
if k != tyNone and x.kind != tyGenericInst:
let r = result
result = newType(k, r.owner)
result.addSonSkipIntLit(r)
@ -179,7 +183,7 @@ proc commonType*(x: PType, y: PNode): PType =
commonType(x, y.typ)
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info)
when defined(nimsuggest):
suggestDecl(c, n, result)
@ -191,7 +195,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
# and sfGenSym in n.sym.flags:
result = n.sym
if result.kind != kind:
localError(n.info, "cannot use symbol of kind '" &
localError(c.config, n.info, "cannot use symbol of kind '" &
$result.kind & "' as a '" & $kind & "'")
if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}:
# declarative context, so produce a fresh gensym:
@ -203,7 +207,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
# template; we must fix it here: see #909
result.owner = getCurrOwner(c)
else:
result = newSym(kind, considerQuotedIdent(n), getCurrOwner(c), n.info)
result = newSym(kind, considerQuotedIdent(c.config, n), getCurrOwner(c), n.info)
#if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
# incl(result.flags, sfGlobal)
when defined(nimsuggest):
@ -215,15 +219,20 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
allowed: TSymFlags): PSym
proc typeAllowedCheck(info: TLineInfo; typ: PType; kind: TSymKind) =
let t = typeAllowed(typ, kind)
proc typeAllowedCheck(conf: ConfigRef; info: TLineInfo; typ: PType; kind: TSymKind;
flags: TTypeAllowedFlags = {}) =
let t = typeAllowed(typ, kind, flags)
if t != nil:
if t == typ: localError(info, "invalid type: '" & typeToString(typ) & "'")
else: localError(info, "invalid type: '" & typeToString(t) &
"' in this context: '" & typeToString(typ) & "'")
if t == typ:
localError(conf, info, "invalid type: '" & typeToString(typ) &
"' for " & substr($kind, 2).toLowerAscii)
else:
localError(conf, info, "invalid type: '" & typeToString(t) &
"' in this context: '" & typeToString(typ) &
"' for " & substr($kind, 2).toLowerAscii)
proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} =
typeAllowedCheck(typ.n.info, typ, skProc)
typeAllowedCheck(c.config, typ.n.info, typ, skProc)
proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode
@ -276,10 +285,10 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
result = evaluated
let expectedType = eOrig.typ.skipTypes({tyStatic})
if hasCycle(result):
globalError(eOrig.info, "the resulting AST is cyclic and cannot be processed further")
globalError(c.config, eOrig.info, "the resulting AST is cyclic and cannot be processed further")
result = errorNode(c, eOrig)
else:
semmacrosanity.annotateType(result, expectedType)
semmacrosanity.annotateType(result, expectedType, c.config)
else:
result = semExprWithType(c, evaluated)
#result = fitNode(c, e.typ, result) inlined with special case:
@ -296,18 +305,18 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
var e = semExprWithType(c, n)
if e == nil: return
result = getConstExpr(c.module, e)
result = getConstExpr(c.module, e, c.graph)
if result != nil: return
let oldErrorCount = msgs.gErrorCounter
let oldErrorMax = msgs.gErrorMax
let oldErrorCount = c.config.errorCounter
let oldErrorMax = c.config.errorMax
let oldErrorOutputs = errorOutputs
errorOutputs = {}
msgs.gErrorMax = high(int)
c.config.errorMax = high(int)
try:
result = evalConstExpr(c.module, c.cache, e)
result = evalConstExpr(c.module, c.cache, c.graph, e)
if result == nil or result.kind == nkEmpty:
result = nil
else:
@ -316,26 +325,29 @@ proc tryConstExpr(c: PContext, n: PNode): PNode =
except ERecoverableError:
result = nil
msgs.gErrorCounter = oldErrorCount
msgs.gErrorMax = oldErrorMax
c.config.errorCounter = oldErrorCount
c.config.errorMax = oldErrorMax
errorOutputs = oldErrorOutputs
const
errConstExprExpected = "constant expression expected"
proc semConstExpr(c: PContext, n: PNode): PNode =
var e = semExprWithType(c, n)
if e == nil:
localError(n.info, errConstExprExpected)
localError(c.config, n.info, errConstExprExpected)
return n
result = getConstExpr(c.module, e)
result = getConstExpr(c.module, e, c.graph)
if result == nil:
#if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
result = evalConstExpr(c.module, c.cache, e)
result = evalConstExpr(c.module, c.cache, c.graph, e)
if result == nil or result.kind == nkEmpty:
if e.info != n.info:
pushInfoContext(n.info)
localError(e.info, errConstExprExpected)
localError(c.config, e.info, errConstExprExpected)
popInfoContext()
else:
localError(e.info, errConstExprExpected)
localError(c.config, e.info, errConstExprExpected)
# error correction:
result = e
else:
@ -350,7 +362,7 @@ proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags): PNode =
else:
result = semExprWithType(c, n, flags)
if efPreferStatic in flags:
var evaluated = getConstExpr(c.module, result)
var evaluated = getConstExpr(c.module, result, c.graph)
if evaluated != nil: return evaluated
evaluated = evalAtCompileTime(c, result)
if evaluated != nil: return evaluated
@ -372,8 +384,8 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
## reassigned, and binding the unbound identifiers that the macro output
## contains.
inc(evalTemplateCounter)
if evalTemplateCounter > 100:
globalError(s.info, errTemplateInstantiationTooNested)
if evalTemplateCounter > evalTemplateLimit:
globalError(c.config, s.info, "template instantiation too nested")
c.friendModules.add(s.owner.getModule)
result = macroResult
@ -415,43 +427,46 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
dec(evalTemplateCounter)
discard c.friendModules.pop()
const
errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
flags: TExprFlags = {}): PNode =
pushInfoContext(nOrig.info)
markUsed(n.info, sym, c.graph.usageSym)
markUsed(c.config, n.info, sym, c.graph.usageSym)
styleCheckUse(n.info, sym)
if sym == c.p.owner:
globalError(n.info, errRecursiveDependencyX, sym.name.s)
globalError(c.config, n.info, "recursive dependency: '$1'" % sym.name.s)
let genericParams = if sfImmediate in sym.flags: 0
else: sym.ast[genericParamsPos].len
let suppliedParams = max(n.safeLen - 1, 0)
if suppliedParams < genericParams:
globalError(n.info, errMissingGenericParamsForTemplate, n.renderTree)
globalError(c.config, n.info, errMissingGenericParamsForTemplate % n.renderTree)
#if c.evalContext == nil:
# c.evalContext = c.createEvalContext(emStatic)
result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
result = evalMacroCall(c.module, c.cache, c.graph, n, nOrig, sym)
if efNoSemCheck notin flags:
result = semAfterMacroCall(c, n, result, sym, flags)
result = wrapInComesFrom(nOrig.info, sym, result)
popInfoContext()
proc forceBool(c: PContext, n: PNode): PNode =
result = fitNode(c, getSysType(tyBool), n, n.info)
result = fitNode(c, getSysType(c.graph, n.info, tyBool), n, n.info)
if result == nil: result = n
proc semConstBoolExpr(c: PContext, n: PNode): PNode =
let nn = semExprWithType(c, n)
result = fitNode(c, getSysType(tyBool), nn, nn.info)
result = fitNode(c, getSysType(c.graph, n.info, tyBool), nn, nn.info)
if result == nil:
localError(n.info, errConstExprExpected)
localError(c.config, n.info, errConstExprExpected)
return nn
result = getConstExpr(c.module, result)
result = getConstExpr(c.module, result, c.graph)
if result == nil:
localError(n.info, errConstExprExpected)
localError(c.config, n.info, errConstExprExpected)
result = nn
proc semGenericStmt(c: PContext, n: PNode): PNode
@ -464,14 +479,14 @@ proc addCodeForGenerics(c: PContext, n: PNode) =
var prc = c.generics[i].inst.sym
if prc.kind in {skProc, skFunc, skMethod, skConverter} and prc.magic == mNone:
if prc.ast == nil or prc.ast.sons[bodyPos] == nil:
internalError(prc.info, "no code for " & prc.name.s)
internalError(c.config, prc.info, "no code for " & prc.name.s)
else:
addSon(n, prc.ast)
c.lastGenericIdx = c.generics.len
proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
var c = newContext(graph, module, cache)
if c.p != nil: internalError(module.info, "sem.myOpen")
if c.p != nil: internalError(graph.config, module.info, "sem.myOpen")
c.semConstExpr = semConstExpr
c.semExpr = semExpr
c.semTryExpr = tryExpr
@ -489,14 +504,14 @@ proc myOpen(graph: ModuleGraph; module: PSym; cache: IdentCache): PPassContext =
c.importTable = openScope(c)
c.importTable.addSym(module) # a module knows itself
if sfSystemModule in module.flags:
magicsys.systemModule = module # set global variable!
graph.systemModule = module
c.topLevelScope = openScope(c)
# don't be verbose unless the module belongs to the main package:
if module.owner.id == gMainPackageId:
gNotes = gMainPackageNotes
graph.config.notes = graph.config.mainPackageNotes
else:
if gMainPackageNotes == {}: gMainPackageNotes = gNotes
gNotes = ForeignPackageNotes
if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes
graph.config.notes = graph.config.foreignPackageNotes
result = c
proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContext =
@ -505,30 +520,30 @@ proc myOpenCached(graph: ModuleGraph; module: PSym; rd: PRodReader): PPassContex
proc replayMethodDefs(graph: ModuleGraph; rd: PRodReader) =
for m in items(rd.methods): methodDef(graph, m, true)
proc isImportSystemStmt(n: PNode): bool =
if magicsys.systemModule == nil: return false
proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool =
if g.systemModule == nil: return false
case n.kind
of nkImportStmt:
for x in n:
if x.kind == nkIdent:
let f = checkModuleName(x, false)
if f == magicsys.systemModule.info.fileIndex:
let f = checkModuleName(g.config, x, false)
if f == g.systemModule.info.fileIndex:
return true
of nkImportExceptStmt, nkFromStmt:
if n[0].kind == nkIdent:
let f = checkModuleName(n[0], false)
if f == magicsys.systemModule.info.fileIndex:
let f = checkModuleName(g.config, n[0], false)
if f == g.systemModule.info.fileIndex:
return true
else: discard
proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
if n.kind == nkDefer:
localError(n.info, "defer statement not supported at top level")
if c.topStmts == 0 and not isImportSystemStmt(n):
localError(c.config, n.info, "defer statement not supported at top level")
if c.topStmts == 0 and not isImportSystemStmt(c.graph, n):
if sfSystemModule notin c.module.flags and
n.kind notin {nkEmpty, nkCommentStmt}:
c.importTable.addSym magicsys.systemModule # import the "System" identifier
importAllSymbols(c, magicsys.systemModule)
c.importTable.addSym c.graph.systemModule # import the "System" identifier
importAllSymbols(c, c.graph.systemModule)
inc c.topStmts
else:
inc c.topStmts
@ -550,11 +565,11 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
if result.kind != nkEmpty: addSon(a, result)
result = a
result = hloStmt(c, result)
if gCmd == cmdInteractive and not isEmptyType(result.typ):
if c.config.cmd == cmdInteractive and not isEmptyType(result.typ):
result = buildEchoStmt(c, result)
if gCmd == cmdIdeTools:
if c.config.cmd == cmdIdeTools:
appendToModule(c.module, result)
result = transformStmt(c.module, result)
result = transformStmt(c.graph, c.module, result)
proc recoverContext(c: PContext) =
# clean up in case of a semantic error: We clean up the stacks, etc. This is
@ -567,7 +582,7 @@ proc recoverContext(c: PContext) =
proc myProcess(context: PPassContext, n: PNode): PNode =
var c = PContext(context)
# no need for an expensive 'try' if we stop after the first error anyway:
if msgs.gErrorMax <= 1:
if c.config.errorMax <= 1:
result = semStmtAndGenerateGenerics(c, n)
else:
let oldContextLen = msgs.getInfoContextLen()
@ -583,15 +598,16 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
result = nil
else:
result = ast.emptyNode
#if gCmd == cmdIdeTools: findSuggest(c, n)
#if c.config.cmd == cmdIdeTools: findSuggest(c, n)
rod.storeNode(c.module, result)
proc testExamples(c: PContext) =
let inp = toFullPath(c.module.info)
let outp = inp.changeFileExt"" & "_examples.nim"
renderModule(c.runnableExamples, inp, outp)
let backend = if isDefined("js"): "js"
elif isDefined("cpp"): "cpp"
elif isDefined("objc"): "objc"
let backend = if isDefined(c.config, "js"): "js"
elif isDefined(c.config, "cpp"): "cpp"
elif isDefined(c.config, "objc"): "objc"
else: "c"
if os.execShellCmd("nim " & backend & " -r " & outp) != 0:
quit "[Examples] failed"
@ -599,13 +615,13 @@ proc testExamples(c: PContext) =
proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
var c = PContext(context)
if gCmd == cmdIdeTools and not c.suggestionsMade:
if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
suggestSentinel(c)
closeScope(c) # close module's scope
rawCloseScope(c) # imported symbols; don't check for unused ones!
result = newNode(nkStmtList)
if n != nil:
internalError(n.info, "n is not nil") #result := n;
internalError(c.config, n.info, "n is not nil") #result := n;
addCodeForGenerics(c, result)
if c.module.ast != nil:
result.add(c.module.ast)
@ -613,6 +629,8 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
replayMethodDefs(graph, c.rd)
popOwner(c)
popProcCon(c)
storeRemaining(c.module)
if c.runnableExamples != nil: testExamples(c)
const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose)
const semPass* = makePass(myOpen, myOpenCached, myProcess, myClose,
isFrontend = true)

View file

@ -33,7 +33,7 @@ proc at(a, i: PNode, elemType: PType): PNode =
proc liftBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
for i in 0 ..< t.len:
let lit = lowerings.newIntLit(i)
let lit = lowerings.newIntLit(c.c.graph, x.info, i)
liftBodyAux(c, t.sons[i], body, x.at(lit, t.sons[i]), y.at(lit, t.sons[i]))
proc dotField(x: PNode, f: PSym): PNode =
@ -66,15 +66,15 @@ proc liftBodyObj(c: var TLiftCtx; n, body, x, y: PNode) =
liftBodyObj(c, n[i].lastSon, branch.sons[L-1], x, y)
caseStmt.add(branch)
body.add(caseStmt)
localError(c.info, "cannot lift assignment operator to 'case' object")
localError(c.c.config, c.info, "cannot lift assignment operator to 'case' object")
of nkRecList:
for t in items(n): liftBodyObj(c, t, body, x, y)
else:
illFormedAstLocal(n)
illFormedAstLocal(n, c.c.config)
proc genAddr(c: PContext; x: PNode): PNode =
if x.kind == nkHiddenDeref:
checkSonsLen(x, 1)
checkSonsLen(x, 1, c.config)
result = x.sons[0]
else:
result = newNodeIT(nkHiddenAddr, x.info, makeVarType(c, x.typ))
@ -82,7 +82,7 @@ proc genAddr(c: PContext; x: PNode): PNode =
proc newAsgnCall(c: PContext; op: PSym; x, y: PNode): PNode =
if sfError in op.flags:
localError(x.info, errWrongSymbolX, op.name.s)
localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s)
result = newNodeI(nkCall, x.info)
result.add newSymNode(op)
result.add genAddr(c, x)
@ -101,7 +101,7 @@ proc newOpCall(op: PSym; x: PNode): PNode =
proc destructorCall(c: PContext; op: PSym; x: PNode): PNode =
result = newNodeIT(nkCall, x.info, op.typ.sons[0])
result.add(newSymNode(op))
if newDestructors:
if destructor in c.features:
result.add genAddr(c, x)
else:
result.add x
@ -124,7 +124,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
op = field
if op == nil:
op = liftBody(c.c, t, c.kind, c.info)
markUsed(c.info, op, c.c.graph.usageSym)
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
styleCheckUse(c.info, op)
body.add newAsgnCall(c.c, op, x, y)
result = true
@ -134,7 +134,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
of attachedDestructor:
let op = t.destructor
if op != nil:
markUsed(c.info, op, c.c.graph.usageSym)
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
styleCheckUse(c.info, op)
body.add destructorCall(c.c, op, x)
result = true
@ -145,7 +145,7 @@ proc considerOverloadedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
of attachedDeepCopy:
let op = t.deepCopy
if op != nil:
markUsed(c.info, op, c.c.graph.usageSym)
markUsed(c.c.config, c.info, op, c.c.graph.usageSym)
styleCheckUse(c.info, op)
body.add newDeepCopyCall(op, x, y)
result = true
@ -163,37 +163,37 @@ proc addVar(father, v, value: PNode) =
proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
var temp = newSym(skTemp, getIdent(lowerings.genPrefix), c.fn, c.info)
temp.typ = getSysType(tyInt)
temp.typ = getSysType(c.c.graph, body.info, tyInt)
incl(temp.flags, sfFromGeneric)
var v = newNodeI(nkVarSection, c.info)
result = newSymNode(temp)
v.addVar(result, lowerings.newIntLit(first))
v.addVar(result, lowerings.newIntLit(c.c.graph, body.info, first))
body.add v
proc genBuiltin(magic: TMagic; name: string; i: PNode): PNode =
proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode =
result = newNodeI(nkCall, i.info)
result.add createMagic(name, magic).newSymNode
result.add createMagic(g, name, magic).newSymNode
result.add i
proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode =
result = newNodeI(nkWhileStmt, c.info, 2)
let cmp = genBuiltin(mLeI, "<=", i)
cmp.add genHigh(dest)
cmp.typ = getSysType(tyBool)
let cmp = genBuiltin(c.c.graph, mLeI, "<=", i)
cmp.add genHigh(c.c.graph, dest)
cmp.typ = getSysType(c.c.graph, c.info, tyBool)
result.sons[0] = cmp
result.sons[1] = newNodeI(nkStmtList, c.info)
proc addIncStmt(body, i: PNode) =
let incCall = genBuiltin(mInc, "inc", i)
incCall.add lowerings.newIntLit(1)
proc addIncStmt(c: var TLiftCtx; body, i: PNode) =
let incCall = genBuiltin(c.c.graph, mInc, "inc", i)
incCall.add lowerings.newIntLit(c.c.graph, c.info, 1)
body.add incCall
proc newSeqCall(c: PContext; x, y: PNode): PNode =
# don't call genAddr(c, x) here:
result = genBuiltin(mNewSeq, "newSeq", x)
let lenCall = genBuiltin(mLengthSeq, "len", y)
lenCall.typ = getSysType(tyInt)
result = genBuiltin(c.graph, mNewSeq, "newSeq", x)
let lenCall = genBuiltin(c.graph, mLengthSeq, "len", y)
lenCall.typ = getSysType(c.graph, x.info, tyInt)
result.add lenCall
proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
@ -212,7 +212,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let elemType = t.lastSon
liftBodyAux(c, elemType, whileLoop.sons[1], x.at(i, elemType),
y.at(i, elemType))
addIncStmt(whileLoop.sons[1], i)
addIncStmt(c, whileLoop.sons[1], i)
body.add whileLoop
else:
defaultOp(c, t, body, x, y)
@ -231,20 +231,20 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) =
# have to go through some indirection; we delegate this to the codegen:
let call = newNodeI(nkCall, c.info, 2)
call.typ = t
call.sons[0] = newSymNode(createMagic("deepCopy", mDeepCopy))
call.sons[0] = newSymNode(createMagic(c.c.graph, "deepCopy", mDeepCopy))
call.sons[1] = y
body.add newAsgnStmt(x, call)
of tyVarargs, tyOpenArray:
localError(c.info, errGenerated, "cannot copy openArray")
localError(c.c.config, c.info, "cannot copy openArray")
of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt,
tyTypeDesc, tyGenericInvocation, tyForward:
internalError(c.info, "assignment requested for type: " & typeToString(t))
internalError(c.c.config, c.info, "assignment requested for type: " & typeToString(t))
of tyOrdinal, tyRange, tyInferred,
tyGenericInst, tyStatic, tyVar, tyAlias:
tyGenericInst, tyStatic, tyVar, tyLent, tyAlias, tySink:
liftBodyAux(c, lastSon(t), body, x, y)
of tyUnused, tyOptAsRef, tyUnused1, tyUnused2: internalError("liftBodyAux")
of tyUnused, tyOptAsRef: internalError(c.c.config, "liftBodyAux")
proc newProcType(info: TLineInfo; owner: PSym): PType =
result = newType(tyProc, owner)
@ -306,7 +306,7 @@ proc liftBody(c: PContext; typ: PType; kind: TTypeAttachedOp;
proc getAsgnOrLiftBody(c: PContext; typ: PType; info: TLineInfo): PSym =
let t = typ.skipTypes({tyGenericInst, tyVar, tyAlias})
let t = typ.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
result = t.assignment
if result.isNil:
result = liftBody(c, t, attachedAsgn, info)
@ -319,7 +319,7 @@ proc liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) =
## In the semantic pass this is called in strategic places
## to ensure we lift assignment, destructors and moves properly.
## The later 'destroyer' pass depends on it.
if not newDestructors or not hasDestructor(typ): return
if destructor notin c.features or not hasDestructor(typ): return
when false:
# do not produce wrong liftings while we're still instantiating generics:
# now disabled; breaks topttree.nim!

View file

@ -59,7 +59,8 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
filter: TSymKinds,
best, alt: var TCandidate,
errors: var CandidateErrors,
diagnosticsFlag = false) =
diagnosticsFlag: bool,
errorsEnabled: bool) =
var o: TOverloadIter
var sym = initOverloadIter(o, c, headSymbol)
var scope = o.lastOverloadScope
@ -68,6 +69,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
# This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
let counterInitial = c.currentScope.symbols.counter
var syms: seq[tuple[s: PSym, scope: int]]
var noSyms = true
var nextSymIndex = 0
while sym != nil:
if sym.kind in filter:
@ -102,17 +104,19 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
var cmp = cmpCandidates(best, z)
if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far
elif errors != nil or z.diagnostics != nil:
elif errorsEnabled or z.diagnosticsEnabled:
errors.safeAdd(CandidateError(
sym: sym,
unmatchedVarParam: int z.mutabilityProblem,
firstMismatch: z.firstMismatch,
diagnostics: z.diagnostics))
else:
# Symbol table has been modified. Restart and pre-calculate all syms
# before any further candidate init and compare. SLOW, but rare case.
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
best, alt, o, diagnosticsFlag)
if syms == nil:
noSyms = false
if noSyms:
sym = nextOverloadIter(o, c, headSymbol)
scope = o.lastOverloadScope
elif nextSymIndex < syms.len:
@ -123,6 +127,20 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
else:
break
proc effectProblem(f, a: PType; result: var string) =
if f.kind == tyProc and a.kind == tyProc:
if tfThread in f.flags and tfThread notin a.flags:
result.add "\n This expression is not GC-safe. Annotate the " &
"proc with {.gcsafe.} to get extended error information."
elif tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
result.add "\n This expression can have side effects. Annotate the " &
"proc with {.noSideEffect.} to get extended error information."
proc renderNotLValue(n: PNode): string =
result = $n
if n.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and n.len == 2:
result = typeToString(n.typ.skipTypes(abstractVar)) & "(" & result & ")"
proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
(TPreferedDesc, string) =
var prefer = preferName
@ -154,32 +172,61 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
else:
add(candidates, err.sym.getProcHeader(prefer))
add(candidates, "\n")
if err.firstMismatch != 0 and n.len > 1:
let cond = n.len > 2
if cond:
candidates.add(" first type mismatch at position: " & $err.firstMismatch &
"\n required type: ")
var wanted, got: PType = nil
if err.firstMismatch < err.sym.typ.len:
wanted = err.sym.typ.sons[err.firstMismatch]
if cond: candidates.add typeToString(wanted)
else:
if cond: candidates.add "none"
if err.firstMismatch < n.len:
if cond:
candidates.add "\n but expression '"
candidates.add renderTree(n[err.firstMismatch])
candidates.add "' is of type: "
got = n[err.firstMismatch].typ
if cond: candidates.add typeToString(got)
if wanted != nil and got != nil:
effectProblem(wanted, got, candidates)
if cond: candidates.add "\n"
if err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len:
add(candidates, "for a 'var' type a variable needs to be passed, but '" &
renderTree(n[err.unmatchedVarParam]) & "' is immutable\n")
candidates.add(" for a 'var' type a variable needs to be passed, but '" &
renderNotLValue(n[err.unmatchedVarParam]) &
"' is immutable\n")
for diag in err.diagnostics:
add(candidates, diag & "\n")
candidates.add(diag & "\n")
result = (prefer, candidates)
const
errTypeMismatch = "type mismatch: got <"
errButExpected = "but expected one of: "
errUndeclaredField = "undeclared field: '$1'"
errUndeclaredRoutine = "attempting to call undeclared routine: '$1'"
errAmbiguousCallXYZ = "ambiguous call; both $1 and $2 match for: $3"
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall,
# as semOverlodedCall is already pretty slow (and we need this information
# only in case of an error).
if errorOutputs == {}:
# fail fast:
globalError(n.info, errTypeMismatch, "")
if errors.isNil or errors.len == 0:
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
globalError(c.config, n.info, "type mismatch")
if errors.len == 0:
localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
return
let (prefer, candidates) = presentFailedCandidates(c, n, errors)
var result = msgKindToString(errTypeMismatch)
var result = errTypeMismatch
add(result, describeArgs(c, n, 1, prefer))
add(result, ')')
add(result, '>')
if candidates != "":
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
localError(n.info, errGenerated, result & "\nexpression: " & $n)
add(result, "\n" & errButExpected & "\n" & candidates)
localError(c.config, n.info, result & "\nexpression: " & $n)
proc bracketNotFoundError(c: PContext; n: PNode) =
var errors: CandidateErrors = @[]
@ -189,22 +236,25 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
while symx != nil:
if symx.kind in routineKinds:
errors.add(CandidateError(sym: symx,
unmatchedVarParam: 0,
diagnostics: nil))
unmatchedVarParam: 0, firstMismatch: 0,
diagnostics: nil,
enabled: false))
symx = nextOverloadIter(o, c, headSymbol)
if errors.len == 0:
localError(n.info, "could not resolve: " & $n)
localError(c.config, n.info, "could not resolve: " & $n)
else:
notFoundError(c, n, errors)
proc resolveOverloads(c: PContext, n, orig: PNode,
filter: TSymKinds, flags: TExprFlags,
errors: var CandidateErrors): TCandidate =
errors: var CandidateErrors,
errorsEnabled: bool): TCandidate =
var initialBinding: PNode
var alt: TCandidate
var f = n.sons[0]
if f.kind == nkBracketExpr:
# fill in the bindings:
semOpAux(c, f)
initialBinding = f
f = f.sons[0]
else:
@ -212,7 +262,8 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
template pickBest(headSymbol) =
pickBestCandidate(c, headSymbol, n, orig, initialBinding,
filter, result, alt, errors, efExplain in flags)
filter, result, alt, errors, efExplain in flags,
errorsEnabled)
pickBest(f)
let overloadsState = result.state
@ -234,7 +285,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
else: return
if nfDotField in n.flags:
internalAssert f.kind == nkIdent and n.sonsLen >= 2
internalAssert c.config, f.kind == nkIdent and n.len >= 2
# leave the op head symbol empty,
# we are going to try multiple variants
@ -263,13 +314,13 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
if overloadsState == csEmpty and result.state == csEmpty:
if nfDotField in n.flags and nfExplicitCall notin n.flags:
localError(n.info, errUndeclaredField, considerQuotedIdent(f).s)
localError(c.config, n.info, errUndeclaredField % considerQuotedIdent(c.config, f, n).s)
else:
localError(n.info, errUndeclaredRoutine, considerQuotedIdent(f).s)
localError(c.config, n.info, errUndeclaredRoutine % considerQuotedIdent(c.config, f, n).s)
return
elif result.state != csMatch:
if nfExprCall in n.flags:
localError(n.info, errExprXCannotBeCalled,
localError(c.config, n.info, "expression '$1' cannot be called" %
renderTree(n, {renderNoComments}))
else:
if {nfDotField, nfDotSetter} * n.flags != {}:
@ -279,13 +330,13 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
return
if alt.state == csMatch and cmpCandidates(result, alt) == 0 and
not sameMethodDispatcher(result.calleeSym, alt.calleeSym):
internalAssert result.state == csMatch
internalAssert c.config, result.state == csMatch
#writeMatches(result)
#writeMatches(alt)
if errorOutputs == {}:
# quick error message for performance of 'compiles' built-in:
globalError(n.info, errGenerated, "ambiguous call")
elif gErrorCounter == 0:
globalError(c.config, n.info, errGenerated, "ambiguous call")
elif c.config.errorCounter == 0:
# don't cascade errors
var args = "("
for i in countup(1, sonsLen(n) - 1):
@ -293,7 +344,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
add(args, typeToString(n.sons[i].typ))
add(args, ")")
localError(n.info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [
localError(c.config, n.info, errAmbiguousCallXYZ % [
getProcHeader(result.calleeSym), getProcHeader(alt.calleeSym),
args])
@ -334,7 +385,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
result.typ = generateTypeInstance(c, m.bindings, arg.info,
formal.skipTypes({tyCompositeTypeClass}))
else:
typeMismatch(arg.info, formal, arg.typ)
typeMismatch(c.config, arg.info, formal, arg.typ)
# error correction:
result = copyTree(arg)
result.typ = formal
@ -342,7 +393,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
assert x.state == csMatch
var finalCallee = x.calleeSym
markUsed(n.sons[0].info, finalCallee, c.graph.usageSym)
markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym)
styleCheckUse(n.sons[0].info, finalCallee)
assert finalCallee.ast != nil
if x.hasFauxMatch:
@ -368,7 +419,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
of skType:
x.call.add newSymNode(s, n.info)
else:
internalAssert false
internalAssert c.config, false
result = x.call
instGenericConvertersSons(c, result, x)
@ -377,7 +428,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
proc canDeref(n: PNode): bool {.inline.} =
result = n.len >= 2 and (let t = n[1].typ;
t != nil and t.skipTypes({tyGenericInst, tyAlias}).kind in {tyPtr, tyRef})
t != nil and t.skipTypes({tyGenericInst, tyAlias, tySink}).kind in {tyPtr, tyRef})
proc tryDeref(n: PNode): PNode =
result = newNodeI(nkHiddenDeref, n.info)
@ -386,18 +437,17 @@ proc tryDeref(n: PNode): PNode =
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds, flags: TExprFlags): PNode =
var errors: CandidateErrors = if efExplain in flags: @[]
else: nil
var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
var errors: CandidateErrors = if efExplain in flags: @[] else: nil
var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
if r.state == csMatch:
# this may be triggered, when the explain pragma is used
if errors.len > 0:
let (_, candidates) = presentFailedCandidates(c, n, errors)
message(n.info, hintUserRaw,
message(c.config, n.info, hintUserRaw,
"Non-matching candidates for " & renderTree(n) & "\n" &
candidates)
result = semResolvedCall(c, n, r)
elif experimentalMode(c) and canDeref(n):
elif implicitDeref in c.features and canDeref(n):
# try to deref the first argument and then try overloading resolution again:
#
# XXX: why is this here?
@ -406,7 +456,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
# into sigmatch with hidden conversion produced there
#
n.sons[1] = n.sons[1].tryDeref
var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
if r.state == csMatch: result = semResolvedCall(c, n, r)
else:
# get rid of the deref again for a better error message:
@ -426,8 +476,8 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
else:
notFoundError(c, n, errors)
proc explicitGenericInstError(n: PNode): PNode =
localError(n.info, errCannotInstantiateX, renderTree(n))
proc explicitGenericInstError(c: PContext; n: PNode): PNode =
localError(c.config, n.info, errCannotInstantiateX % renderTree(n))
result = n
proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
@ -442,7 +492,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
if tm in {isNone, isConvertible}: return nil
var newInst = generateInstance(c, s, m.bindings, n.info)
newInst.typ.flags.excl tfUnresolved
markUsed(n.info, s, c.graph.usageSym)
markUsed(c.config, n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s)
result = newSymNode(newInst, n.info)
@ -458,11 +508,11 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
# number of generic type parameters:
if safeLen(s.ast.sons[genericParamsPos]) != n.len-1:
let expected = safeLen(s.ast.sons[genericParamsPos])
localError(n.info, errGenerated, "cannot instantiate: " & renderTree(n) &
"; got " & $(n.len-1) & " type(s) but expected " & $expected)
localError(c.config, n.info, errGenerated, "cannot instantiate: '" & renderTree(n) &
"'; got " & $(n.len-1) & " type(s) but expected " & $expected)
return n
result = explicitGenericSym(c, n, s)
if result == nil: result = explicitGenericInstError(n)
if result == nil: result = explicitGenericInstError(c, n)
elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
# choose the generic proc with the proper number of type parameters.
# XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
@ -480,10 +530,10 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
# get rid of nkClosedSymChoice if not ambiguous:
if result.len == 1 and a.kind == nkClosedSymChoice:
result = result[0]
elif result.len == 0: result = explicitGenericInstError(n)
# candidateCount != 1: return explicitGenericInstError(n)
elif result.len == 0: result = explicitGenericInstError(c, n)
# candidateCount != 1: return explicitGenericInstError(c, n)
else:
result = explicitGenericInstError(n)
result = explicitGenericInstError(c, n)
proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
# Searchs for the fn in the symbol table. If the parameter lists are suitable

View file

@ -14,7 +14,7 @@ import
wordrecg,
ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
magicsys, nversion, nimsets, parser, times, passes, rodread, vmdef,
modulegraphs
modulegraphs, configuration
type
TOptionEntry* = object # entries to put on a stack for pragma parsing
@ -63,7 +63,7 @@ type
# to the user.
efWantStmt, efAllowStmt, efDetermineType, efExplain,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo,
efNoEvaluateGeneric, efInCall, efFromHlo
TExprFlags* = set[TExprFlag]
@ -89,6 +89,7 @@ type
ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot
# store this info in the syms themselves!)
inGenericContext*: int # > 0 if we are in a generic type
inStaticContext*: int # > 0 if we are inside a static: block
inUnrolledContext*: int # > 0 if we are unrolling a loop
compilesContextId*: int # > 0 if we are in a ``compiles`` magic
compilesContextIdGenerator*: int
@ -130,6 +131,7 @@ type
signatures*: TStrTable
recursiveDep*: string
suggestionsMade*: bool
features*: set[Feature]
inTypeContext*: int
typesWithOps*: seq[(PType, PType)] #\
# We need to instantiate the type bound ops lazily after
@ -138,13 +140,15 @@ type
# would otherwise fail.
runnableExamples*: PNode
template config*(c: PContext): ConfigRef = c.graph.config
proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
result.genericSym = s
result.inst = inst
proc filename*(c: PContext): string =
# the module's filename
return c.module.filename
return toFilename(FileIndex c.module.position)
proc scopeDepth*(c: PContext): int {.inline.} =
result = if c.currentScope != nil: c.currentScope.depthLevel
@ -163,7 +167,7 @@ proc pushOwner*(c: PContext; owner: PSym) =
proc popOwner*(c: PContext) =
var length = len(c.graph.owners)
if length > 0: setLen(c.graph.owners, length - 1)
else: internalError("popOwner")
else: internalError(c.config, "popOwner")
proc lastOptionEntry*(c: PContext): POptionEntry =
result = c.optionStack[^1]
@ -199,19 +203,19 @@ proc considerGenSyms*(c: PContext; n: PNode) =
for i in 0..<n.safeLen:
considerGenSyms(c, n.sons[i])
proc newOptionEntry*(): POptionEntry =
proc newOptionEntry*(conf: ConfigRef): POptionEntry =
new(result)
result.options = gOptions
result.options = conf.options
result.defaultCC = ccDefault
result.dynlib = nil
result.notes = gNotes
result.notes = conf.notes
proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext =
new(result)
result.ambiguousSymbols = initIntSet()
result.optionStack = @[]
result.libs = @[]
result.optionStack.add(newOptionEntry())
result.optionStack.add(newOptionEntry(graph.config))
result.module = module
result.friendModules = @[module]
result.converters = @[]
@ -225,7 +229,7 @@ proc newContext*(graph: ModuleGraph; module: PSym; cache: IdentCache): PContext
result.graph = graph
initStrTable(result.signatures)
result.typesWithOps = @[]
result.features = graph.config.features
proc inclSym(sq: var TSymSeq, s: PSym) =
var L = len(sq)
@ -254,36 +258,37 @@ proc newTypeS*(kind: TTypeKind, c: PContext): PType =
proc makePtrType*(c: PContext, baseType: PType): PType =
result = newTypeS(tyPtr, c)
addSonSkipIntLit(result, baseType.assertNotNil)
addSonSkipIntLit(result, baseType)
proc makeTypeWithModifier*(c: PContext,
modifier: TTypeKind,
baseType: PType): PType =
assert modifier in {tyVar, tyPtr, tyRef, tyStatic, tyTypeDesc}
assert modifier in {tyVar, tyLent, tyPtr, tyRef, tyStatic, tyTypeDesc}
if modifier in {tyVar, tyTypeDesc} and baseType.kind == modifier:
if modifier in {tyVar, tyLent, tyTypeDesc} and baseType.kind == modifier:
result = baseType
else:
result = newTypeS(modifier, c)
addSonSkipIntLit(result, baseType.assertNotNil)
addSonSkipIntLit(result, baseType)
proc makeVarType*(c: PContext, baseType: PType): PType =
if baseType.kind == tyVar:
proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
if baseType.kind == kind:
result = baseType
else:
result = newTypeS(tyVar, c)
addSonSkipIntLit(result, baseType.assertNotNil)
result = newTypeS(kind, c)
addSonSkipIntLit(result, baseType)
proc makeTypeDesc*(c: PContext, typ: PType): PType =
if typ.kind == tyTypeDesc:
result = typ
else:
result = newTypeS(tyTypeDesc, c)
result.addSonSkipIntLit(typ.assertNotNil)
result.addSonSkipIntLit(typ)
proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
let typedesc = makeTypeDesc(c, typ)
let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info).linkTo(typedesc)
let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info,
c.config.options).linkTo(typedesc)
return newSymNode(sym, info)
proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
@ -338,21 +343,20 @@ proc makeNotType*(c: PContext, t1: PType): PType =
result.flags.incl(t1.flags * {tfHasStatic})
result.flags.incl tfHasMeta
proc nMinusOne*(n: PNode): PNode =
proc nMinusOne(c: PContext; n: PNode): PNode =
result = newNode(nkCall, n.info, @[
newSymNode(getSysMagic("pred", mPred)),
n])
newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n])
# Remember to fix the procs below this one when you make changes!
proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
let intType = getSysType(tyInt)
let intType = getSysType(c.graph, n.info, tyInt)
result = newTypeS(tyRange, c)
result.sons = @[intType]
if n.typ != nil and n.typ.n == nil:
result.flags.incl tfUnresolved
result.n = newNode(nkRange, n.info, @[
newIntTypeNode(nkIntLit, 0, intType),
makeStaticExpr(c, n.nMinusOne)])
makeStaticExpr(c, nMinusOne(c, n))])
template rangeHasUnresolvedStatic*(t: PType): bool =
tfUnresolved in t.flags
@ -371,7 +375,8 @@ proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) =
dest.size = - 1
proc makeRangeType*(c: PContext; first, last: BiggestInt;
info: TLineInfo; intType = getSysType(tyInt)): PType =
info: TLineInfo; intType: PType = nil): PType =
let intType = if intType != nil: intType else: getSysType(c.graph, info, tyInt)
var n = newNodeI(nkRange, info)
addSon(n, newIntTypeNode(nkIntLit, first, intType))
addSon(n, newIntTypeNode(nkIntLit, last, intType))
@ -384,20 +389,17 @@ proc markIndirect*(c: PContext, s: PSym) {.inline.} =
incl(s.flags, sfAddrTaken)
# XXX add to 'c' for global analysis
proc illFormedAst*(n: PNode) =
globalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc illFormedAst*(n: PNode; conf: ConfigRef) =
globalError(conf, n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc illFormedAstLocal*(n: PNode) =
localError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc illFormedAstLocal*(n: PNode; conf: ConfigRef) =
localError(conf, n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))
proc checkSonsLen*(n: PNode, length: int) =
if sonsLen(n) != length: illFormedAst(n)
proc checkSonsLen*(n: PNode, length: int; conf: ConfigRef) =
if sonsLen(n) != length: illFormedAst(n, conf)
proc checkMinSonsLen*(n: PNode, length: int) =
if sonsLen(n) < length: illFormedAst(n)
proc checkMinSonsLen*(n: PNode, length: int; conf: ConfigRef) =
if sonsLen(n) < length: illFormedAst(n, conf)
proc isTopLevel*(c: PContext): bool {.inline.} =
result = c.currentScope.depthLevel <= 2
proc experimentalMode*(c: PContext): bool {.inline.} =
result = gExperimentalMode or sfExperimental in c.module.flags

File diff suppressed because it is too large Load diff

View file

@ -16,16 +16,17 @@ type
tupleIndex: int
field: PSym
replaceByFieldName: bool
c: PContext
proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
case n.kind
of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n
of nkIdent, nkSym:
result = n
let ident = considerQuotedIdent(n)
let ident = considerQuotedIdent(c.c.config, n)
var L = sonsLen(forLoop)
if c.replaceByFieldName:
if ident.id == considerQuotedIdent(forLoop[0]).id:
if ident.id == considerQuotedIdent(c.c.config, forLoop[0]).id:
let fieldName = if c.tupleType.isNil: c.field.name.s
elif c.tupleType.n.isNil: "Field" & $c.tupleIndex
else: c.tupleType.n.sons[c.tupleIndex].sym.name.s
@ -33,7 +34,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
return
# other fields:
for i in ord(c.replaceByFieldName)..L-3:
if ident.id == considerQuotedIdent(forLoop[i]).id:
if ident.id == considerQuotedIdent(c.c.config, forLoop[i]).id:
var call = forLoop.sons[L-2]
var tupl = call.sons[i+1-ord(c.replaceByFieldName)]
if c.field.isNil:
@ -47,7 +48,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode =
break
else:
if n.kind == nkContinueStmt:
localError(n.info, errGenerated,
localError(c.c.config, n.info,
"'continue' not supported in a 'fields' loop")
result = copyNode(n)
newSons(result, sonsLen(n))
@ -63,6 +64,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
case typ.kind
of nkSym:
var fc: TFieldInstCtx # either 'tup[i]' or 'field' is valid
fc.c = c.c
fc.field = typ.sym
fc.replaceByFieldName = c.m == mFieldPairs
openScope(c.c)
@ -76,7 +78,7 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
let L = forLoop.len
let call = forLoop.sons[L-2]
if call.len > 2:
localError(forLoop.info, errGenerated,
localError(c.c.config, forLoop.info,
"parallel 'fields' iterator does not work for 'case' objects")
return
# iterate over the selector:
@ -99,17 +101,17 @@ proc semForObjectFields(c: TFieldsCtx, typ, forLoop, father: PNode) =
of nkRecList:
for t in items(typ): semForObjectFields(c, t, forLoop, father)
else:
illFormedAstLocal(typ)
illFormedAstLocal(typ, c.c.config)
proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
# so that 'break' etc. work as expected, we produce
# a 'while true: stmt; break' loop ...
result = newNodeI(nkWhileStmt, n.info, 2)
var trueSymbol = strTableGet(magicsys.systemModule.tab, getIdent"true")
var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent"true")
if trueSymbol == nil:
localError(n.info, errSystemNeeds, "true")
localError(c.config, n.info, "system needs: 'true'")
trueSymbol = newSym(skUnknown, getIdent"true", getCurrOwner(c), n.info)
trueSymbol.typ = getSysType(tyBool)
trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
result.sons[0] = newSymNode(trueSymbol, n.info)
var stmts = newNodeI(nkStmtList, n.info)
@ -118,18 +120,18 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
var length = sonsLen(n)
var call = n.sons[length-2]
if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs):
localError(n.info, errWrongNumberOfVariables)
localError(c.config, n.info, errWrongNumberOfVariables)
return result
const skippedTypesForFields = abstractVar - {tyTypeDesc} + tyUserTypeClasses
var tupleTypeA = skipTypes(call.sons[1].typ, skippedTypesForFields)
if tupleTypeA.kind notin {tyTuple, tyObject}:
localError(n.info, errGenerated, "no object or tuple type")
localError(c.config, n.info, errGenerated, "no object or tuple type")
return result
for i in 1..call.len-1:
var tupleTypeB = skipTypes(call.sons[i].typ, skippedTypesForFields)
if not sameType(tupleTypeA, tupleTypeB):
typeMismatch(call.sons[i].info, tupleTypeA, tupleTypeB)
typeMismatch(c.config, call.sons[i].info, tupleTypeA, tupleTypeB)
inc(c.p.nestedLoopCounter)
if tupleTypeA.kind == tyTuple:
@ -139,6 +141,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
var fc: TFieldInstCtx
fc.tupleType = tupleTypeA
fc.tupleIndex = i
fc.c = c
fc.replaceByFieldName = m == mFieldPairs
var body = instFieldLoopBody(fc, loopBody, n)
inc c.inUnrolledContext

View file

@ -13,24 +13,18 @@
import
strutils, options, ast, astalgo, trees, treetab, nimsets, times,
nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
commands, magicsys, saturate
commands, magicsys, modulegraphs, strtabs
proc getConstExpr*(m: PSym, n: PNode): PNode
# evaluates the constant expression or returns nil if it is no constant
# expression
proc evalOp*(m: TMagic, n, a, b, c: PNode): PNode
proc leValueConv*(a, b: PNode): bool
proc newIntNodeT*(intVal: BiggestInt, n: PNode): PNode
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode
proc newStrNodeT*(strVal: string, n: PNode): PNode
# implementation
proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode =
case skipTypes(n.typ, abstractVarRange).kind
of tyInt:
result = newIntNode(nkIntLit, intVal)
result.typ = getIntLitType(result)
# See bug #6989. 'pred' et al only produce an int literal type if the
# original type was 'int', not a distinct int etc.
if n.typ.kind == tyInt:
result.typ = getIntLitType(g, result)
else:
result.typ = n.typ
# hrm, this is not correct: 1 + high(int) shouldn't produce tyInt64 ...
#setIntLitType(result)
of tyChar:
@ -41,20 +35,82 @@ proc newIntNodeT(intVal: BiggestInt, n: PNode): PNode =
result.typ = n.typ
result.info = n.info
proc newFloatNodeT(floatVal: BiggestFloat, n: PNode): PNode =
proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode =
result = newFloatNode(nkFloatLit, floatVal)
if skipTypes(n.typ, abstractVarRange).kind == tyFloat:
result.typ = getFloatLitType(result)
result.typ = getFloatLitType(g, result)
else:
result.typ = n.typ
result.info = n.info
proc newStrNodeT(strVal: string, n: PNode): PNode =
proc newStrNodeT*(strVal: string, n: PNode; g: ModuleGraph): PNode =
result = newStrNode(nkStrLit, strVal)
result.typ = n.typ
result.info = n.info
proc ordinalValToString*(a: PNode): string =
proc getConstExpr*(m: PSym, n: PNode; g: ModuleGraph): PNode
# evaluates the constant expression or returns nil if it is no constant
# expression
proc evalOp*(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode
proc checkInRange(n: PNode, res: BiggestInt): bool =
if res in firstOrd(n.typ)..lastOrd(n.typ):
result = true
proc foldAdd(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
let res = a +% b
if ((res xor a) >= 0'i64 or (res xor b) >= 0'i64) and
checkInRange(n, res):
result = newIntNodeT(res, n, g)
proc foldSub*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
let res = a -% b
if ((res xor a) >= 0'i64 or (res xor not b) >= 0'i64) and
checkInRange(n, res):
result = newIntNodeT(res, n, g)
proc foldAbs*(a: BiggestInt, n: PNode; g: ModuleGraph): PNode =
if a != firstOrd(n.typ):
result = newIntNodeT(a, n, g)
proc foldMod*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
if b != 0'i64:
result = newIntNodeT(a mod b, n, g)
proc foldModU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
if b != 0'i64:
result = newIntNodeT(a %% b, n, g)
proc foldDiv*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
if b != 0'i64 and (a != firstOrd(n.typ) or b != -1'i64):
result = newIntNodeT(a div b, n, g)
proc foldDivU*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
if b != 0'i64:
result = newIntNodeT(a /% b, n, g)
proc foldMul*(a, b: BiggestInt, n: PNode; g: ModuleGraph): PNode =
let res = a *% b
let floatProd = toBiggestFloat(a) * toBiggestFloat(b)
let resAsFloat = toBiggestFloat(res)
# Fast path for normal case: small multiplicands, and no info
# is lost in either method.
if resAsFloat == floatProd and checkInRange(n, res):
return newIntNodeT(res, n, g)
# Somebody somewhere lost info. Close enough, or way off? Note
# that a != 0 and b != 0 (else resAsFloat == floatProd == 0).
# The difference either is or isn't significant compared to the
# true value (of which floatProd is a good approximation).
# abs(diff)/abs(prod) <= 1/32 iff
# 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough"
if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd) and
checkInRange(n, res):
return newIntNodeT(res, n, g)
proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
# because $ has the param ordinal[T], `a` is not necessarily an enum, but an
# ordinal
var x = getInt(a)
@ -66,14 +122,14 @@ proc ordinalValToString*(a: PNode): string =
of tyEnum:
var n = t.n
for i in countup(0, sonsLen(n) - 1):
if n.sons[i].kind != nkSym: internalError(a.info, "ordinalValToString")
if n.sons[i].kind != nkSym: internalError(g.config, a.info, "ordinalValToString")
var field = n.sons[i].sym
if field.position == x:
if field.ast == nil:
return field.name.s
else:
return field.ast.strVal
internalError(a.info, "no symbol for ordinal value: " & $x)
internalError(g.config, a.info, "no symbol for ordinal value: " & $x)
else:
result = $x
@ -92,12 +148,12 @@ proc pickIntRange(a, b: PType): PType =
proc isIntRangeOrLit(t: PType): bool =
result = isIntRange(t) or isIntLit(t)
proc makeRange(typ: PType, first, last: BiggestInt): PType =
proc makeRange(typ: PType, first, last: BiggestInt; g: ModuleGraph): PType =
let minA = min(first, last)
let maxA = max(first, last)
let lowerNode = newIntNode(nkIntLit, minA)
if typ.kind == tyInt and minA == maxA:
result = getIntLitType(lowerNode)
result = getIntLitType(g, lowerNode)
elif typ.kind in {tyUint, tyUInt64}:
# these are not ordinal types, so you get no subrange type for these:
result = typ
@ -109,7 +165,7 @@ proc makeRange(typ: PType, first, last: BiggestInt): PType =
result.n = n
addSonSkipIntLit(result, skipTypes(typ, {tyRange}))
proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
proc makeRangeF(typ: PType, first, last: BiggestFloat; g: ModuleGraph): PType =
var n = newNode(nkRange)
addSon(n, newFloatNode(nkFloatLit, min(first.float, last.float)))
addSon(n, newFloatNode(nkFloatLit, max(first.float, last.float)))
@ -119,9 +175,9 @@ proc makeRangeF(typ: PType, first, last: BiggestFloat): PType =
proc evalIs(n, a: PNode): PNode =
# XXX: This should use the standard isOpImpl
internalAssert a.kind == nkSym and a.sym.kind == skType
internalAssert n.sonsLen == 3 and
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
#internalAssert a.kind == nkSym and a.sym.kind == skType
#internalAssert n.sonsLen == 3 and
# n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
let t1 = a.sym.typ
@ -145,125 +201,113 @@ proc evalIs(n, a: PNode): PNode =
result = newIntNode(nkIntLit, ord(match))
result.typ = n.typ
proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode =
# b and c may be nil
result = nil
case m
of mOrd: result = newIntNodeT(getOrdValue(a), n)
of mChr: result = newIntNodeT(getInt(a), n)
of mUnaryMinusI, mUnaryMinusI64: result = newIntNodeT(- getInt(a), n)
of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n)
of mNot: result = newIntNodeT(1 - getInt(a), n)
of mCard: result = newIntNodeT(nimsets.cardSet(a), n)
of mBitnotI: result = newIntNodeT(not getInt(a), n)
of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n)
of mOrd: result = newIntNodeT(getOrdValue(a), n, g)
of mChr: result = newIntNodeT(getInt(a), n, g)
of mUnaryMinusI, mUnaryMinusI64: result = newIntNodeT(- getInt(a), n, g)
of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n, g)
of mNot: result = newIntNodeT(1 - getInt(a), n, g)
of mCard: result = newIntNodeT(nimsets.cardSet(a), n, g)
of mBitnotI: result = newIntNodeT(not getInt(a), n, g)
of mLengthArray: result = newIntNodeT(lengthOrd(a.typ), n, g)
of mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr:
if a.kind == nkNilLit:
result = newIntNodeT(0, n)
result = newIntNodeT(0, n, g)
elif a.kind in {nkStrLit..nkTripleStrLit}:
result = newIntNodeT(len a.strVal, n)
result = newIntNodeT(len a.strVal, n, g)
else:
result = newIntNodeT(sonsLen(a), n) # BUGFIX
result = newIntNodeT(sonsLen(a), n, g)
of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away
of mToFloat, mToBiggestFloat:
result = newFloatNodeT(toFloat(int(getInt(a))), n)
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n)
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n)
of mAbsI:
if getInt(a) >= 0: result = a
else: result = newIntNodeT(- getInt(a), n)
result = newFloatNodeT(toFloat(int(getInt(a))), n, g)
# XXX: Hides overflow/underflow
of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n, g)
of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n, g)
of mAbsI: result = foldAbs(getInt(a), n, g)
of mZe8ToI, mZe8ToI64, mZe16ToI, mZe16ToI64, mZe32ToI64, mZeIToI64:
# byte(-128) = 1...1..1000_0000'64 --> 0...0..1000_0000'64
result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n)
of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n)
of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n)
of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n)
of mUnaryLt: result = newIntNodeT(getOrdValue(a) |-| 1, n)
of mSucc: result = newIntNodeT(getOrdValue(a) |+| getInt(b), n)
of mPred: result = newIntNodeT(getOrdValue(a) |-| getInt(b), n)
of mAddI: result = newIntNodeT(getInt(a) |+| getInt(b), n)
of mSubI: result = newIntNodeT(getInt(a) |-| getInt(b), n)
of mMulI: result = newIntNodeT(getInt(a) |*| getInt(b), n)
result = newIntNodeT(getInt(a) and (`shl`(1, getSize(a.typ) * 8) - 1), n, g)
of mToU8: result = newIntNodeT(getInt(a) and 0x000000FF, n, g)
of mToU16: result = newIntNodeT(getInt(a) and 0x0000FFFF, n, g)
of mToU32: result = newIntNodeT(getInt(a) and 0x00000000FFFFFFFF'i64, n, g)
of mUnaryLt: result = foldSub(getOrdValue(a), 1, n, g)
of mSucc: result = foldAdd(getOrdValue(a), getInt(b), n, g)
of mPred: result = foldSub(getOrdValue(a), getInt(b), n, g)
of mAddI: result = foldAdd(getInt(a), getInt(b), n, g)
of mSubI: result = foldSub(getInt(a), getInt(b), n, g)
of mMulI: result = foldMul(getInt(a), getInt(b), n, g)
of mMinI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n)
else: result = newIntNodeT(getInt(a), n)
if getInt(a) > getInt(b): result = newIntNodeT(getInt(b), n, g)
else: result = newIntNodeT(getInt(a), n, g)
of mMaxI:
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n)
else: result = newIntNodeT(getInt(b), n)
if getInt(a) > getInt(b): result = newIntNodeT(getInt(a), n, g)
else: result = newIntNodeT(getInt(b), n, g)
of mShlI:
case skipTypes(n.typ, abstractRange).kind
of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n)
of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n)
of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n, g)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n, g)
of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n, g)
of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shl`(getInt(a), getInt(b)), n)
else: internalError(n.info, "constant folding for shl")
result = newIntNodeT(`shl`(getInt(a), getInt(b)), n, g)
else: internalError(g.config, n.info, "constant folding for shl")
of mShrI:
case skipTypes(n.typ, abstractRange).kind
of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n)
of tyInt32: result = newIntNodeT(int32(getInt(a)) shr int32(getInt(b)), n)
of tyInt8: result = newIntNodeT(int8(getInt(a)) shr int8(getInt(b)), n, g)
of tyInt16: result = newIntNodeT(int16(getInt(a)) shr int16(getInt(b)), n, g)
of tyInt32: result = newIntNodeT(int32(getInt(a)) shr int32(getInt(b)), n, g)
of tyInt64, tyInt, tyUInt..tyUInt64:
result = newIntNodeT(`shr`(getInt(a), getInt(b)), n)
else: internalError(n.info, "constant folding for shr")
of mDivI:
let y = getInt(b)
if y != 0:
result = newIntNodeT(`|div|`(getInt(a), y), n)
of mModI:
let y = getInt(b)
if y != 0:
result = newIntNodeT(`|mod|`(getInt(a), y), n)
of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n)
of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n)
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n)
result = newIntNodeT(`shr`(getInt(a), getInt(b)), n, g)
else: internalError(g.config, n.info, "constant folding for shr")
of mDivI: result = foldDiv(getInt(a), getInt(b), n, g)
of mModI: result = foldMod(getInt(a), getInt(b), n, g)
of mAddF64: result = newFloatNodeT(getFloat(a) + getFloat(b), n, g)
of mSubF64: result = newFloatNodeT(getFloat(a) - getFloat(b), n, g)
of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n, g)
of mDivF64:
if getFloat(b) == 0.0:
if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n)
else: result = newFloatNodeT(Inf, n)
if getFloat(a) == 0.0: result = newFloatNodeT(NaN, n, g)
elif getFloat(b).classify == fcNegZero: result = newFloatNodeT(-Inf, n, g)
else: result = newFloatNodeT(Inf, n, g)
else:
result = newFloatNodeT(getFloat(a) / getFloat(b), n)
result = newFloatNodeT(getFloat(a) / getFloat(b), n, g)
of mMaxF64:
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n)
else: result = newFloatNodeT(getFloat(b), n)
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(a), n, g)
else: result = newFloatNodeT(getFloat(b), n, g)
of mMinF64:
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n)
else: result = newFloatNodeT(getFloat(a), n)
of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n)
if getFloat(a) > getFloat(b): result = newFloatNodeT(getFloat(b), n, g)
else: result = newFloatNodeT(getFloat(a), n, g)
of mIsNil: result = newIntNodeT(ord(a.kind == nkNilLit), n, g)
of mLtI, mLtB, mLtEnum, mLtCh:
result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n)
result = newIntNodeT(ord(getOrdValue(a) < getOrdValue(b)), n, g)
of mLeI, mLeB, mLeEnum, mLeCh:
result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n)
result = newIntNodeT(ord(getOrdValue(a) <= getOrdValue(b)), n, g)
of mEqI, mEqB, mEqEnum, mEqCh:
result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n)
of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n)
of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n)
of mEqF64: result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n)
of mLtStr: result = newIntNodeT(ord(getStr(a) < getStr(b)), n)
of mLeStr: result = newIntNodeT(ord(getStr(a) <= getStr(b)), n)
of mEqStr: result = newIntNodeT(ord(getStr(a) == getStr(b)), n)
result = newIntNodeT(ord(getOrdValue(a) == getOrdValue(b)), n, g)
of mLtF64: result = newIntNodeT(ord(getFloat(a) < getFloat(b)), n, g)
of mLeF64: result = newIntNodeT(ord(getFloat(a) <= getFloat(b)), n, g)
of mEqF64: result = newIntNodeT(ord(getFloat(a) == getFloat(b)), n, g)
of mLtStr: result = newIntNodeT(ord(getStr(a) < getStr(b)), n, g)
of mLeStr: result = newIntNodeT(ord(getStr(a) <= getStr(b)), n, g)
of mEqStr: result = newIntNodeT(ord(getStr(a) == getStr(b)), n, g)
of mLtU, mLtU64:
result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n)
result = newIntNodeT(ord(`<%`(getOrdValue(a), getOrdValue(b))), n, g)
of mLeU, mLeU64:
result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n)
of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n)
of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n)
of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n)
of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n)
of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n)
of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n)
of mModU:
let y = getInt(b)
if y != 0:
result = newIntNodeT(`%%`(getInt(a), y), n)
of mDivU:
let y = getInt(b)
if y != 0:
result = newIntNodeT(`/%`(getInt(a), y), n)
of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n)
of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n)
result = newIntNodeT(ord(`<=%`(getOrdValue(a), getOrdValue(b))), n, g)
of mBitandI, mAnd: result = newIntNodeT(a.getInt and b.getInt, n, g)
of mBitorI, mOr: result = newIntNodeT(getInt(a) or getInt(b), n, g)
of mBitxorI, mXor: result = newIntNodeT(a.getInt xor b.getInt, n, g)
of mAddU: result = newIntNodeT(`+%`(getInt(a), getInt(b)), n, g)
of mSubU: result = newIntNodeT(`-%`(getInt(a), getInt(b)), n, g)
of mMulU: result = newIntNodeT(`*%`(getInt(a), getInt(b)), n, g)
of mModU: result = foldModU(getInt(a), getInt(b), n, g)
of mDivU: result = foldDivU(getInt(a), getInt(b), n, g)
of mLeSet: result = newIntNodeT(ord(containsSets(a, b)), n, g)
of mEqSet: result = newIntNodeT(ord(equalSets(a, b)), n, g)
of mLtSet:
result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n)
result = newIntNodeT(ord(containsSets(a, b) and not equalSets(a, b)), n, g)
of mMulSet:
result = nimsets.intersectSets(a, b)
result.info = n.info
@ -276,120 +320,125 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode =
of mSymDiffSet:
result = nimsets.symdiffSets(a, b)
result.info = n.info
of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n)
of mInSet: result = newIntNodeT(ord(inSet(a, b)), n)
of mConStrStr: result = newStrNodeT(getStrOrChar(a) & getStrOrChar(b), n, g)
of mInSet: result = newIntNodeT(ord(inSet(a, b)), n, g)
of mRepr:
# BUGFIX: we cannot eval mRepr here for reasons that I forgot.
discard
of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n)
of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n, g)
of mBoolToStr:
if getOrdValue(a) == 0: result = newStrNodeT("false", n)
else: result = newStrNodeT("true", n)
of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n)
if getOrdValue(a) == 0: result = newStrNodeT("false", n, g)
else: result = newStrNodeT("true", n, g)
of mCopyStr: result = newStrNodeT(substr(getStr(a), int(getOrdValue(b))), n, g)
of mCopyStrLast:
result = newStrNodeT(substr(getStr(a), int(getOrdValue(b)),
int(getOrdValue(c))), n)
of mFloatToStr: result = newStrNodeT($getFloat(a), n)
int(getOrdValue(c))), n, g)
of mFloatToStr: result = newStrNodeT($getFloat(a), n, g)
of mCStrToStr, mCharToStr:
if a.kind == nkBracket:
var s = ""
for b in a.sons:
s.add b.getStrOrChar
result = newStrNodeT(s, n)
result = newStrNodeT(s, n, g)
else:
result = newStrNodeT(getStrOrChar(a), n)
result = newStrNodeT(getStrOrChar(a), n, g)
of mStrToStr: result = a
of mEnumToStr: result = newStrNodeT(ordinalValToString(a), n)
of mEnumToStr: result = newStrNodeT(ordinalValToString(a, g), n, g)
of mArrToSeq:
result = copyTree(a)
result.typ = n.typ
of mCompileOption:
result = newIntNodeT(ord(commands.testCompileOption(a.getStr, n.info)), n)
result = newIntNodeT(ord(commands.testCompileOption(g.config, a.getStr, n.info)), n, g)
of mCompileOptionArg:
result = newIntNodeT(ord(
testCompileOptionArg(getStr(a), getStr(b), n.info)), n)
testCompileOptionArg(g.config, getStr(a), getStr(b), n.info)), n, g)
of mEqProc:
result = newIntNodeT(ord(
exprStructuralEquivalent(a, b, strictSymEquality=true)), n)
exprStructuralEquivalent(a, b, strictSymEquality=true)), n, g)
else: discard
proc getConstIfExpr(c: PSym, n: PNode): PNode =
proc getConstIfExpr(c: PSym, n: PNode; g: ModuleGraph): PNode =
result = nil
for i in countup(0, sonsLen(n) - 1):
var it = n.sons[i]
if it.len == 2:
var e = getConstExpr(c, it.sons[0])
var e = getConstExpr(c, it.sons[0], g)
if e == nil: return nil
if getOrdValue(e) != 0:
if result == nil:
result = getConstExpr(c, it.sons[1])
result = getConstExpr(c, it.sons[1], g)
if result == nil: return
elif it.len == 1:
if result == nil: result = getConstExpr(c, it.sons[0])
else: internalError(it.info, "getConstIfExpr()")
if result == nil: result = getConstExpr(c, it.sons[0], g)
else: internalError(g.config, it.info, "getConstIfExpr()")
proc leValueConv(a, b: PNode): bool =
proc leValueConv*(a, b: PNode): bool =
result = false
case a.kind
of nkCharLit..nkUInt64Lit:
case b.kind
of nkCharLit..nkUInt64Lit: result = a.intVal <= b.intVal
of nkFloatLit..nkFloat128Lit: result = a.intVal <= round(b.floatVal).int
else: internalError(a.info, "leValueConv")
else: result = false #internalError(a.info, "leValueConv")
of nkFloatLit..nkFloat128Lit:
case b.kind
of nkFloatLit..nkFloat128Lit: result = a.floatVal <= b.floatVal
of nkCharLit..nkUInt64Lit: result = a.floatVal <= toFloat(int(b.intVal))
else: internalError(a.info, "leValueConv")
else: internalError(a.info, "leValueConv")
else: result = false # internalError(a.info, "leValueConv")
else: result = false # internalError(a.info, "leValueConv")
proc magicCall(m: PSym, n: PNode): PNode =
proc magicCall(m: PSym, n: PNode; g: ModuleGraph): PNode =
if sonsLen(n) <= 1: return
var s = n.sons[0].sym
var a = getConstExpr(m, n.sons[1])
var a = getConstExpr(m, n.sons[1], g)
var b, c: PNode
if a == nil: return
if sonsLen(n) > 2:
b = getConstExpr(m, n.sons[2])
b = getConstExpr(m, n.sons[2], g)
if b == nil: return
if sonsLen(n) > 3:
c = getConstExpr(m, n.sons[3])
c = getConstExpr(m, n.sons[3], g)
if c == nil: return
result = evalOp(s.magic, n, a, b, c)
result = evalOp(s.magic, n, a, b, c, g)
proc getAppType(n: PNode): PNode =
if gGlobalOptions.contains(optGenDynLib):
result = newStrNodeT("lib", n)
elif gGlobalOptions.contains(optGenStaticLib):
result = newStrNodeT("staticlib", n)
elif gGlobalOptions.contains(optGenGuiApp):
result = newStrNodeT("gui", n)
proc getAppType(n: PNode; g: ModuleGraph): PNode =
if g.config.globalOptions.contains(optGenDynLib):
result = newStrNodeT("lib", n, g)
elif g.config.globalOptions.contains(optGenStaticLib):
result = newStrNodeT("staticlib", n, g)
elif g.config.globalOptions.contains(optGenGuiApp):
result = newStrNodeT("gui", n, g)
else:
result = newStrNodeT("console", n)
result = newStrNodeT("console", n, g)
proc rangeCheck(n: PNode, value: BiggestInt) =
if value < firstOrd(n.typ) or value > lastOrd(n.typ):
localError(n.info, errGenerated, "cannot convert " & $value &
proc rangeCheck(n: PNode, value: BiggestInt; g: ModuleGraph) =
var err = false
if n.typ.skipTypes({tyRange}).kind in {tyUInt..tyUInt64}:
err = value <% firstOrd(n.typ) or value >% lastOrd(n.typ, fixedUnsigned=true)
else:
err = value < firstOrd(n.typ) or value > lastOrd(n.typ)
if err:
localError(g.config, n.info, "cannot convert " & $value &
" to " & typeToString(n.typ))
proc foldConv*(n, a: PNode; check = false): PNode =
proc foldConv*(n, a: PNode; g: ModuleGraph; check = false): PNode =
# XXX range checks?
case skipTypes(n.typ, abstractRange).kind
of tyInt..tyInt64, tyUInt..tyUInt64:
case skipTypes(a.typ, abstractRange).kind
of tyFloat..tyFloat64:
result = newIntNodeT(int(getFloat(a)), n)
of tyChar: result = newIntNodeT(getOrdValue(a), n)
result = newIntNodeT(int(getFloat(a)), n, g)
of tyChar: result = newIntNodeT(getOrdValue(a), n, g)
else:
result = a
result.typ = n.typ
if check and result.kind in {nkCharLit..nkUInt64Lit}:
rangeCheck(n, result.intVal)
rangeCheck(n, result.intVal, g)
of tyFloat..tyFloat64:
case skipTypes(a.typ, abstractRange).kind
of tyInt..tyInt64, tyEnum, tyBool, tyChar:
result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n)
result = newFloatNodeT(toBiggestFloat(getOrdValue(a)), n, g)
else:
result = a
result.typ = n.typ
@ -399,47 +448,47 @@ proc foldConv*(n, a: PNode; check = false): PNode =
result = a
result.typ = n.typ
proc getArrayConstr(m: PSym, n: PNode): PNode =
proc getArrayConstr(m: PSym, n: PNode; g: ModuleGraph): PNode =
if n.kind == nkBracket:
result = n
else:
result = getConstExpr(m, n)
result = getConstExpr(m, n, g)
if result == nil: result = n
proc foldArrayAccess(m: PSym, n: PNode): PNode =
var x = getConstExpr(m, n.sons[0])
if x == nil or x.typ.skipTypes({tyGenericInst, tyAlias}).kind == tyTypeDesc:
proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
var x = getConstExpr(m, n.sons[0], g)
if x == nil or x.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyTypeDesc:
return
var y = getConstExpr(m, n.sons[1])
var y = getConstExpr(m, n.sons[1], g)
if y == nil: return
var idx = getOrdValue(y)
case x.kind
of nkPar:
of nkPar, nkTupleConstr:
if idx >= 0 and idx < sonsLen(x):
result = x.sons[int(idx)]
if result.kind == nkExprColonExpr: result = result.sons[1]
else:
localError(n.info, errIndexOutOfBounds)
localError(g.config, n.info, "index out of bounds: " & $n)
of nkBracket:
idx = idx - x.typ.firstOrd
if idx >= 0 and idx < x.len: result = x.sons[int(idx)]
else: localError(n.info, errIndexOutOfBounds)
else: localError(g.config, n.info, "index out of bounds: " & $n)
of nkStrLit..nkTripleStrLit:
result = newNodeIT(nkCharLit, x.info, n.typ)
if idx >= 0 and idx < len(x.strVal):
result.intVal = ord(x.strVal[int(idx)])
elif idx == len(x.strVal):
elif idx == len(x.strVal) and optLaxStrings in g.config.options:
discard
else:
localError(n.info, errIndexOutOfBounds)
localError(g.config, n.info, "index out of bounds: " & $n)
else: discard
proc foldFieldAccess(m: PSym, n: PNode): PNode =
proc foldFieldAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
# a real field access; proc calls have already been transformed
var x = getConstExpr(m, n.sons[0])
if x == nil or x.kind notin {nkObjConstr, nkPar}: return
var x = getConstExpr(m, n.sons[0], g)
if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return
var field = n.sons[1].sym
for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1):
@ -452,13 +501,13 @@ proc foldFieldAccess(m: PSym, n: PNode): PNode =
if it.sons[0].sym.name.id == field.name.id:
result = x.sons[i].sons[1]
return
localError(n.info, errFieldXNotFound, field.name.s)
localError(g.config, n.info, "field not found: " & field.name.s)
proc foldConStrStr(m: PSym, n: PNode): PNode =
proc foldConStrStr(m: PSym, n: PNode; g: ModuleGraph): PNode =
result = newNodeIT(nkStrLit, n.info, n.typ)
result.strVal = ""
for i in countup(1, sonsLen(n) - 1):
let a = getConstExpr(m, n.sons[i])
let a = getConstExpr(m, n.sons[i], g)
if a == nil: return nil
result.strVal.add(getStrOrChar(a))
@ -470,37 +519,55 @@ proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode =
else:
result.typ = s.typ
proc getConstExpr(m: PSym, n: PNode): PNode =
proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode =
result = nil
proc getSrcTimestamp(): DateTime =
try:
result = utc(fromUnix(parseInt(getEnv("SOURCE_DATE_EPOCH",
"not a number"))))
except ValueError:
# Environment variable malformed.
# https://reproducible-builds.org/specs/source-date-epoch/: "If the
# value is malformed, the build process SHOULD exit with a non-zero
# error code", which this doesn't do. This uses local time, because
# that maintains compatibility with existing usage.
result = local(getTime())
case n.kind
of nkSym:
var s = n.sym
case s.kind
of skEnumField:
result = newIntNodeT(s.position, n)
result = newIntNodeT(s.position, n, g)
of skConst:
case s.magic
of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n)
of mCompileDate: result = newStrNodeT(times.getDateStr(), n)
of mCompileTime: result = newStrNodeT(times.getClockStr(), n)
of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n)
of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n)
of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n)
of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[platform.hostOS].name), n)
of mBuildCPU: result = newStrNodeT(platform.CPU[platform.hostCPU].name.toLowerAscii, n)
of mAppType: result = getAppType(n)
of mNaN: result = newFloatNodeT(NaN, n)
of mInf: result = newFloatNodeT(Inf, n)
of mNegInf: result = newFloatNodeT(NegInf, n)
of mIsMainModule: result = newIntNodeT(ord(sfMainModule in m.flags), n, g)
of mCompileDate: result = newStrNodeT(format(getSrcTimestamp(),
"yyyy-MM-dd"), n, g)
of mCompileTime: result = newStrNodeT(format(getSrcTimestamp(),
"HH:mm:ss"), n, g)
of mCpuEndian: result = newIntNodeT(ord(CPU[targetCPU].endian), n, g)
of mHostOS: result = newStrNodeT(toLowerAscii(platform.OS[targetOS].name), n, g)
of mHostCPU: result = newStrNodeT(platform.CPU[targetCPU].name.toLowerAscii, n, g)
of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[platform.hostOS].name), n, g)
of mBuildCPU: result = newStrNodeT(platform.CPU[platform.hostCPU].name.toLowerAscii, n, g)
of mAppType: result = getAppType(n, g)
of mNaN: result = newFloatNodeT(NaN, n, g)
of mInf: result = newFloatNodeT(Inf, n, g)
of mNegInf: result = newFloatNodeT(NegInf, n, g)
of mIntDefine:
if isDefined(s.name):
result = newIntNodeT(lookupSymbol(s.name).parseInt, n)
if isDefined(g.config, s.name.s):
try:
result = newIntNodeT(g.config.symbols[s.name.s].parseInt, n, g)
except ValueError:
localError(g.config, n.info, "expression is not an integer literal")
of mStrDefine:
if isDefined(s.name):
result = newStrNodeT(lookupSymbol(s.name), n)
if isDefined(g.config, s.name.s):
result = newStrNodeT(g.config.symbols[s.name.s], n, g)
else:
result = copyTree(s.ast)
of {skProc, skFunc, skMethod}:
of skProc, skFunc, skMethod:
result = n
of skType:
# XXX gensym'ed symbols can come here and cannot be resolved. This is
@ -520,7 +587,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of nkCharLit..nkNilLit:
result = copyNode(n)
of nkIfExpr:
result = getConstIfExpr(m, n)
result = getConstIfExpr(m, n, g)
of nkCallKinds:
if n.sons[0].kind != nkSym: return
var s = n.sons[0].sym
@ -533,68 +600,67 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of mSizeOf:
var a = n.sons[1]
if computeSize(a.typ) < 0:
localError(a.info, errCannotEvalXBecauseIncompletelyDefined,
"sizeof")
localError(g.config, a.info, "cannot evaluate 'sizeof' because its type is not defined completely")
result = nil
elif skipTypes(a.typ, typedescInst+{tyRange}).kind in
IntegralTypes+NilableTypes+{tySet}:
#{tyArray,tyObject,tyTuple}:
result = newIntNodeT(getSize(a.typ), n)
result = newIntNodeT(getSize(a.typ), n, g)
else:
result = nil
# XXX: size computation for complex types is still wrong
of mLow:
result = newIntNodeT(firstOrd(n.sons[1].typ), n)
result = newIntNodeT(firstOrd(n.sons[1].typ), n, g)
of mHigh:
if skipTypes(n.sons[1].typ, abstractVar).kind notin
{tySequence, tyString, tyCString, tyOpenArray, tyVarargs}:
result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n)
result = newIntNodeT(lastOrd(skipTypes(n[1].typ, abstractVar)), n, g)
else:
var a = getArrayConstr(m, n.sons[1])
var a = getArrayConstr(m, n.sons[1], g)
if a.kind == nkBracket:
# we can optimize it away:
result = newIntNodeT(sonsLen(a)-1, n)
result = newIntNodeT(sonsLen(a)-1, n, g)
of mLengthOpenArray:
var a = getArrayConstr(m, n.sons[1])
var a = getArrayConstr(m, n.sons[1], g)
if a.kind == nkBracket:
# we can optimize it away! This fixes the bug ``len(134)``.
result = newIntNodeT(sonsLen(a), n)
result = newIntNodeT(sonsLen(a), n, g)
else:
result = magicCall(m, n)
result = magicCall(m, n, g)
of mLengthArray:
# It doesn't matter if the argument is const or not for mLengthArray.
# This fixes bug #544.
result = newIntNodeT(lengthOrd(n.sons[1].typ), n)
result = newIntNodeT(lengthOrd(n.sons[1].typ), n, g)
of mAstToStr:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, g)
of mConStrStr:
result = foldConStrStr(m, n)
result = foldConStrStr(m, n, g)
of mIs:
let a = getConstExpr(m, n[1])
let a = getConstExpr(m, n[1], g)
if a != nil and a.kind == nkSym and a.sym.kind == skType:
result = evalIs(n, a)
else:
result = magicCall(m, n)
result = magicCall(m, n, g)
except OverflowError:
localError(n.info, errOverOrUnderflow)
localError(g.config, n.info, "over- or underflow")
except DivByZeroError:
localError(n.info, errConstantDivisionByZero)
localError(g.config, n.info, "division by zero")
of nkAddr:
var a = getConstExpr(m, n.sons[0])
var a = getConstExpr(m, n.sons[0], g)
if a != nil:
result = n
n.sons[0] = a
of nkBracket:
result = copyTree(n)
for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i])
var a = getConstExpr(m, n.sons[i], g)
if a == nil: return nil
result.sons[i] = a
incl(result.flags, nfAllConst)
of nkRange:
var a = getConstExpr(m, n.sons[0])
var a = getConstExpr(m, n.sons[0], g)
if a == nil: return
var b = getConstExpr(m, n.sons[1])
var b = getConstExpr(m, n.sons[1], g)
if b == nil: return
result = copyNode(n)
addSon(result, a)
@ -602,7 +668,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
of nkCurly:
result = copyTree(n)
for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i])
var a = getConstExpr(m, n.sons[i], g)
if a == nil: return nil
result.sons[i] = a
incl(result.flags, nfAllConst)
@ -613,50 +679,50 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
# if a == nil: return nil
# result.sons[i].sons[1] = a
# incl(result.flags, nfAllConst)
of nkPar:
of nkPar, nkTupleConstr:
# tuple constructor
result = copyTree(n)
if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr):
for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i].sons[1])
var a = getConstExpr(m, n.sons[i].sons[1], g)
if a == nil: return nil
result.sons[i].sons[1] = a
else:
for i in countup(0, sonsLen(n) - 1):
var a = getConstExpr(m, n.sons[i])
var a = getConstExpr(m, n.sons[i], g)
if a == nil: return nil
result.sons[i] = a
incl(result.flags, nfAllConst)
of nkChckRangeF, nkChckRange64, nkChckRange:
var a = getConstExpr(m, n.sons[0])
var a = getConstExpr(m, n.sons[0], g)
if a == nil: return
if leValueConv(n.sons[1], a) and leValueConv(a, n.sons[2]):
result = a # a <= x and x <= b
result.typ = n.typ
else:
localError(n.info, errGenerated, `%`(
msgKindToString(errIllegalConvFromXtoY),
[typeToString(n.sons[0].typ), typeToString(n.typ)]))
localError(g.config, n.info,
"conversion from $1 to $2 is invalid" %
[typeToString(n.sons[0].typ), typeToString(n.typ)])
of nkStringToCString, nkCStringToString:
var a = getConstExpr(m, n.sons[0])
var a = getConstExpr(m, n.sons[0], g)
if a == nil: return
result = a
result.typ = n.typ
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
var a = getConstExpr(m, n.sons[1])
var a = getConstExpr(m, n.sons[1], g)
if a == nil: return
result = foldConv(n, a, check=n.kind == nkHiddenStdConv)
result = foldConv(n, a, g, check=n.kind == nkHiddenStdConv)
of nkCast:
var a = getConstExpr(m, n.sons[1])
var a = getConstExpr(m, n.sons[1], g)
if a == nil: return
if n.typ != nil and n.typ.kind in NilableTypes:
# we allow compile-time 'cast' for pointer types:
result = a
result.typ = n.typ
of nkBracketExpr: result = foldArrayAccess(m, n)
of nkDotExpr: result = foldFieldAccess(m, n)
of nkBracketExpr: result = foldArrayAccess(m, n, g)
of nkDotExpr: result = foldFieldAccess(m, n, g)
of nkStmtListExpr:
if n.len == 2 and n[0].kind == nkComesFrom:
result = getConstExpr(m, n[1])
result = getConstExpr(m, n[1], g)
else:
discard

View file

@ -17,13 +17,13 @@
# included from sem.nim
proc getIdentNode(n: PNode): PNode =
proc getIdentNode(c: PContext; n: PNode): PNode =
case n.kind
of nkPostfix: result = getIdentNode(n.sons[1])
of nkPragmaExpr: result = getIdentNode(n.sons[0])
of nkPostfix: result = getIdentNode(c, n.sons[1])
of nkPragmaExpr: result = getIdentNode(c, n.sons[0])
of nkIdent, nkAccQuoted, nkSym: result = n
else:
illFormedAst(n)
illFormedAst(n, c.config)
result = n
type
@ -103,8 +103,8 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var GenericCtx): PNode =
result = n
let ident = considerQuotedIdent(n)
var s = searchInScopes(c, ident).skipAlias(n)
let ident = considerQuotedIdent(c.config, n)
var s = searchInScopes(c, ident).skipAlias(n, c.config)
if s == nil:
s = strTableGet(c.pureEnumFields, ident)
if s != nil and contains(c.ambiguousSymbols, s.id):
@ -140,8 +140,8 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
result = n
let n = n[1]
let ident = considerQuotedIdent(n)
var s = searchInScopes(c, ident).skipAlias(n)
let ident = considerQuotedIdent(c.config, n)
var s = searchInScopes(c, ident).skipAlias(n, c.config)
if s != nil and s.kind in routineKinds:
isMacro = s.kind in {skTemplate, skMacro}
if withinBind in flags:
@ -158,7 +158,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
result = newDot(result, syms)
proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
let s = newSymS(skUnknown, getIdentNode(n), c)
let s = newSymS(skUnknown, getIdentNode(c, n), c)
addPrelimDecl(c, s)
styleCheckDef(n.info, s, kind)
@ -169,7 +169,7 @@ proc semGenericStmt(c: PContext, n: PNode,
when defined(nimsuggest):
if withinTypeDesc in flags: inc c.inTypeContext
#if gCmd == cmdIdeTools: suggestStmt(c, n)
#if conf.cmd == cmdIdeTools: suggestStmt(c, n)
semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody)
case n.kind
@ -201,13 +201,13 @@ proc semGenericStmt(c: PContext, n: PNode,
result = semMixinStmt(c, n, ctx.toMixin)
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit:
# check if it is an expression macro:
checkMinSonsLen(n, 1)
checkMinSonsLen(n, 1, c.config)
let fn = n.sons[0]
var s = qualifiedLookUp(c, fn, {})
if s == nil and
{withinMixin, withinConcept}*flags == {} and
fn.kind in {nkIdent, nkAccQuoted} and
considerQuotedIdent(fn).id notin ctx.toMixin:
considerQuotedIdent(c.config, fn).id notin ctx.toMixin:
errorUndeclaredIdentifier(c, n.info, fn.renderTree)
var first = int ord(withinConcept in flags)
@ -285,7 +285,7 @@ proc semGenericStmt(c: PContext, n: PNode,
withBracketExpr ctx, n.sons[0]:
result = semGenericStmt(c, result, flags, ctx)
of nkAsgn, nkFastAsgn:
checkSonsLen(n, 2)
checkSonsLen(n, 2, c.config)
let a = n.sons[0]
let b = n.sons[1]
@ -323,7 +323,7 @@ proc semGenericStmt(c: PContext, n: PNode,
n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
for i in countup(1, sonsLen(n)-1):
var a = n.sons[i]
checkMinSonsLen(a, 1)
checkMinSonsLen(a, 1, c.config)
var L = sonsLen(a)
for j in countup(0, L-2):
a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx)
@ -340,23 +340,23 @@ proc semGenericStmt(c: PContext, n: PNode,
closeScope(c)
closeScope(c)
of nkBlockStmt, nkBlockExpr, nkBlockType:
checkSonsLen(n, 2)
checkSonsLen(n, 2, c.config)
openScope(c)
if n.sons[0].kind != nkEmpty:
addTempDecl(c, n.sons[0], skLabel)
n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
closeScope(c)
of nkTryStmt:
checkMinSonsLen(n, 2)
checkMinSonsLen(n, 2, c.config)
n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx)
for i in countup(1, sonsLen(n)-1):
var a = n.sons[i]
checkMinSonsLen(a, 1)
checkMinSonsLen(a, 1, c.config)
var L = sonsLen(a)
openScope(c)
for j in countup(0, L-2):
if a.sons[j].isInfixAs():
addTempDecl(c, getIdentNode(a.sons[j][2]), skLet)
addTempDecl(c, getIdentNode(c, a.sons[j][2]), skLet)
a.sons[j].sons[1] = semGenericStmt(c, a.sons[j][1], flags+{withinTypeDesc}, ctx)
else:
a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx)
@ -367,44 +367,44 @@ proc semGenericStmt(c: PContext, n: PNode,
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a)
checkMinSonsLen(a, 3)
if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a, c.config)
checkMinSonsLen(a, 3, c.config)
var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
for j in countup(0, L-3):
addTempDecl(c, getIdentNode(a.sons[j]), skVar)
addTempDecl(c, getIdentNode(c, a.sons[j]), skVar)
of nkGenericParams:
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if (a.kind != nkIdentDefs): illFormedAst(a)
checkMinSonsLen(a, 3)
if (a.kind != nkIdentDefs): illFormedAst(a, c.config)
checkMinSonsLen(a, 3, c.config)
var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
# do not perform symbol lookup for default expressions
for j in countup(0, L-3):
addTempDecl(c, getIdentNode(a.sons[j]), skType)
addTempDecl(c, getIdentNode(c, a.sons[j]), skType)
of nkConstSection:
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkConstDef): illFormedAst(a)
checkSonsLen(a, 3)
addTempDecl(c, getIdentNode(a.sons[0]), skConst)
if (a.kind != nkConstDef): illFormedAst(a, c.config)
checkSonsLen(a, 3, c.config)
addTempDecl(c, getIdentNode(c, a.sons[0]), skConst)
a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx)
a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx)
of nkTypeSection:
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): illFormedAst(a)
checkSonsLen(a, 3)
addTempDecl(c, getIdentNode(a.sons[0]), skType)
if (a.kind != nkTypeDef): illFormedAst(a, c.config)
checkSonsLen(a, 3, c.config)
addTempDecl(c, getIdentNode(c, a.sons[0]), skType)
for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i]
if a.kind == nkCommentStmt: continue
if (a.kind != nkTypeDef): illFormedAst(a)
checkSonsLen(a, 3)
if (a.kind != nkTypeDef): illFormedAst(a, c.config)
checkSonsLen(a, 3, c.config)
if a.sons[1].kind != nkEmpty:
openScope(c)
a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx)
@ -421,28 +421,28 @@ proc semGenericStmt(c: PContext, n: PNode,
case n.sons[i].kind
of nkEnumFieldDef: a = n.sons[i].sons[0]
of nkIdent: a = n.sons[i]
else: illFormedAst(n)
addDecl(c, newSymS(skUnknown, getIdentNode(a), c))
else: illFormedAst(n, c.config)
addDecl(c, newSymS(skUnknown, getIdentNode(c, a), c))
of nkObjectTy, nkTupleTy, nkTupleClassTy:
discard
of nkFormalParams:
checkMinSonsLen(n, 1)
checkMinSonsLen(n, 1, c.config)
if n.sons[0].kind != nkEmpty:
n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx)
for i in countup(1, sonsLen(n) - 1):
var a = n.sons[i]
if (a.kind != nkIdentDefs): illFormedAst(a)
checkMinSonsLen(a, 3)
if (a.kind != nkIdentDefs): illFormedAst(a, c.config)
checkMinSonsLen(a, 3, c.config)
var L = sonsLen(a)
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx)
a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx)
for j in countup(0, L-3):
addTempDecl(c, getIdentNode(a.sons[j]), skParam)
addTempDecl(c, getIdentNode(c, a.sons[j]), skParam)
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
nkFuncDef, nkIteratorDef, nkLambdaKinds:
checkSonsLen(n, bodyPos + 1)
checkSonsLen(n, bodyPos + 1, c.config)
if n.sons[namePos].kind != nkEmpty:
addTempDecl(c, getIdentNode(n.sons[0]), skProc)
addTempDecl(c, getIdentNode(c, n.sons[0]), skProc)
openScope(c)
n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos],
flags, ctx)
@ -463,7 +463,7 @@ proc semGenericStmt(c: PContext, n: PNode,
closeScope(c)
of nkPragma, nkPragmaExpr: discard
of nkExprColonExpr, nkExprEqExpr:
checkMinSonsLen(n, 2)
checkMinSonsLen(n, 2, c.config)
result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx)
else:
for i in countup(0, sonsLen(n) - 1):

View file

@ -54,10 +54,13 @@ proc pushProcCon*(c: PContext; owner: PSym) =
rawPushProcCon(c, owner)
rawHandleSelf(c, owner)
const
errCannotInstantiateX = "cannot instantiate: '$1'"
iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
internalAssert n.kind == nkGenericParams
internalAssert c.config, n.kind == nkGenericParams
for i, a in n.pairs:
internalAssert a.kind == nkSym
internalAssert c.config, a.kind == nkSym
var q = a.sym
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses:
continue
@ -71,10 +74,10 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym
# later by semAsgn in return type inference scenario
t = q.typ
else:
localError(a.info, errCannotInstantiateX, s.name.s)
localError(c.config, a.info, errCannotInstantiateX % s.name.s)
t = errorType(c)
elif t.kind == tyGenericParam:
localError(a.info, errCannotInstantiateX, q.name.s)
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
t = errorType(c)
elif t.kind == tyGenericInvocation:
#t = instGenericContainer(c, a, t)
@ -145,7 +148,7 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
freshGenSyms(b, result, orig, symMap)
b = semProcBody(c, b)
b = hloBody(c, b)
n.sons[bodyPos] = transformBody(c.module, b, result)
n.sons[bodyPos] = transformBody(c.graph, c.module, b, result)
#echo "code instantiated ", result.name.s
excl(result.flags, sfForward)
dec c.inGenericInst
@ -174,6 +177,8 @@ proc sideEffectsCheck(c: PContext, s: PSym) =
proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
allowMetaTypes = false): PType =
internalAssert c.config, header.kind == tyGenericInvocation
var
typeMap: LayeredIdTable
cl: TReplTypeVars
@ -185,7 +190,35 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
cl.info = info
cl.c = c
cl.allowMetaTypes = allowMetaTypes
# We must add all generic params in scope, because the generic body
# may include tyFromExpr nodes depending on these generic params.
# XXX: This looks quite similar to the code in matchUserTypeClass,
# perhaps the code can be extracted in a shared function.
openScope(c)
let genericTyp = header.base
for i in 0 .. (genericTyp.len - 2):
let genParam = genericTyp[i]
var param: PSym
template paramSym(kind): untyped =
newSym(kind, genParam.sym.name, genericTyp.sym, genParam.sym.info)
if genParam.kind == tyStatic:
param = paramSym skConst
param.ast = header[i+1].n
param.typ = header[i+1]
else:
param = paramSym skType
param.typ = makeTypeDesc(c, header[i+1])
# this scope was not created by the user,
# unused params shoudn't be reported.
param.flags.incl sfUsed
addDecl(c, param)
result = replaceTypeVarsT(cl, header)
closeScope(c)
proc instantiateProcType(c: PContext, pt: TIdTable,
prc: PSym, info: TLineInfo) =
@ -203,14 +236,12 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
# at this point semtypinst have to become part of sem, because it
# will need to use openScope, addDecl, etc.
#addDecl(c, prc)
pushInfoContext(info)
var typeMap = initLayeredTypeMap(pt)
var cl = initTypeVars(c, addr(typeMap), info, nil)
var result = instCopyType(cl, prc.typ)
let originalParams = result.n
result.n = originalParams.shallowCopy
for i in 1 ..< result.len:
# twrong_field_caching requires these 'resetIdTable' calls:
if i > 1:
@ -218,7 +249,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
resetIdTable(cl.localCache)
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
propagateToOwner(result, result.sons[i])
internalAssert originalParams[i].kind == nkSym
internalAssert c.config, originalParams[i].kind == nkSym
when true:
let oldParam = originalParams[i].sym
let param = copySym(oldParam)
@ -255,9 +286,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
## The `pt` parameter is a type-unsafe mapping table used to link generic
## parameters to their concrete types within the generic instance.
# no need to instantiate generic templates/macros:
internalAssert fn.kind notin {skMacro, skTemplate}
internalAssert c.config, fn.kind notin {skMacro, skTemplate}
# generates an instantiated proc
if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
if c.instCounter > 1000: internalError(c.config, fn.ast.info, "nesting too deep")
inc(c.instCounter)
# careful! we copy the whole AST including the possibly nil body!
var n = copyTree(fn.ast)
@ -276,7 +307,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
openScope(c)
let gp = n.sons[genericParamsPos]
internalAssert gp.kind != nkEmpty
internalAssert c.config, gp.kind != nkEmpty
n.sons[namePos] = newSymNode(result)
pushInfoContext(info)
var entry = TInstantiation.new
@ -316,7 +347,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
if c.inGenericContext == 0:
instantiateBody(c, n, fn.typ.n, result, fn)
sideEffectsCheck(c, result)
paramsTypeCheck(c, result.typ)
if result.magic != mSlice:
# 'toOpenArray' is special and it is allowed to return 'openArray':
paramsTypeCheck(c, result.typ)
else:
result = oldPrc
popProcCon(c)

View file

@ -10,7 +10,7 @@
## Implements type sanity checking for ASTs resulting from macros. Lots of
## room for improvement here.
import ast, astalgo, msgs, types
import ast, astalgo, msgs, types, options
proc ithField(n: PNode, field: var int): PSym =
result = nil
@ -20,7 +20,7 @@ proc ithField(n: PNode, field: var int): PSym =
result = ithField(n.sons[i], field)
if result != nil: return
of nkRecCase:
if n.sons[0].kind != nkSym: internalError(n.info, "ithField")
if n.sons[0].kind != nkSym: return
result = ithField(n.sons[0], field)
if result != nil: return
for i in countup(1, sonsLen(n) - 1):
@ -28,13 +28,13 @@ proc ithField(n: PNode, field: var int): PSym =
of nkOfBranch, nkElse:
result = ithField(lastSon(n.sons[i]), field)
if result != nil: return
else: internalError(n.info, "ithField(record case branch)")
else: discard
of nkSym:
if field == 0: result = n.sym
else: dec(field)
else: discard
proc annotateType*(n: PNode, t: PType) =
proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
let x = t.skipTypes(abstractInst+{tyRange})
# Note: x can be unequal to t and we need to be careful to use 't'
# to not to skip tyGenericInst
@ -46,50 +46,50 @@ proc annotateType*(n: PNode, t: PType) =
var j = i-1
let field = x.n.ithField(j)
if field.isNil:
globalError n.info, "invalid field at index " & $i
globalError conf, n.info, "invalid field at index " & $i
else:
internalAssert(n.sons[i].kind == nkExprColonExpr)
annotateType(n.sons[i].sons[1], field.typ)
of nkPar:
internalAssert(conf, n.sons[i].kind == nkExprColonExpr)
annotateType(n.sons[i].sons[1], field.typ, conf)
of nkPar, nkTupleConstr:
if x.kind == tyTuple:
n.typ = t
for i in 0 ..< n.len:
if i >= x.len: globalError n.info, "invalid field at index " & $i
else: annotateType(n.sons[i], x.sons[i])
if i >= x.len: globalError conf, n.info, "invalid field at index " & $i
else: annotateType(n.sons[i], x.sons[i], conf)
elif x.kind == tyProc and x.callConv == ccClosure:
n.typ = t
else:
globalError(n.info, "() must have a tuple type")
globalError(conf, n.info, "() must have a tuple type")
of nkBracket:
if x.kind in {tyArray, tySequence, tyOpenArray}:
n.typ = t
for m in n: annotateType(m, x.elemType)
for m in n: annotateType(m, x.elemType, conf)
else:
globalError(n.info, "[] must have some form of array type")
globalError(conf, n.info, "[] must have some form of array type")
of nkCurly:
if x.kind in {tySet}:
n.typ = t
for m in n: annotateType(m, x.elemType)
for m in n: annotateType(m, x.elemType, conf)
else:
globalError(n.info, "{} must have the set type")
globalError(conf, n.info, "{} must have the set type")
of nkFloatLit..nkFloat128Lit:
if x.kind in {tyFloat..tyFloat128}:
n.typ = t
else:
globalError(n.info, "float literal must have some float type")
globalError(conf, n.info, "float literal must have some float type")
of nkCharLit..nkUInt64Lit:
if x.kind in {tyInt..tyUInt64, tyBool, tyChar, tyEnum}:
n.typ = t
else:
globalError(n.info, "integer literal must have some int type")
globalError(conf, n.info, "integer literal must have some int type")
of nkStrLit..nkTripleStrLit:
if x.kind in {tyString, tyCString}:
n.typ = t
else:
globalError(n.info, "string literal must be of some string type")
globalError(conf, n.info, "string literal must be of some string type")
of nkNilLit:
if x.kind in NilableTypes:
n.typ = t
else:
globalError(n.info, "nil literal must be of some pointer type")
globalError(conf, n.info, "nil literal must be of some pointer type")
else: discard

View file

@ -16,7 +16,7 @@ proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode =
if x.kind == nkSym:
x.sym.flags.incl(sfAddrTaken)
if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}:
localError(n.info, errExprHasNoAddress)
localError(c.config, n.info, errExprHasNoAddress)
result.add x
result.typ = makePtrType(c, x.typ)
@ -43,7 +43,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
let x = copyTree(n)
x.sons[0] = newIdentNode(getIdent"[]", n.info)
bracketNotFoundError(c, x)
#localError(n.info, "could not resolve: " & $n)
#localError(c.config, n.info, "could not resolve: " & $n)
result = n
proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
@ -64,25 +64,28 @@ proc semAsgnOpr(c: PContext; n: PNode): PNode =
proc semIsPartOf(c: PContext, n: PNode, flags: TExprFlags): PNode =
var r = isPartOf(n[1], n[2])
result = newIntNodeT(ord(r), n)
result = newIntNodeT(ord(r), n, c.graph)
proc expectIntLit(c: PContext, n: PNode): int =
let x = c.semConstExpr(c, n)
case x.kind
of nkIntLit..nkInt64Lit: result = int(x.intVal)
else: localError(n.info, errIntLiteralExpected)
else: localError(c.config, n.info, errIntLiteralExpected)
proc semInstantiationInfo(c: PContext, n: PNode): PNode =
result = newNodeIT(nkPar, n.info, n.typ)
result = newNodeIT(nkTupleConstr, n.info, n.typ)
let idx = expectIntLit(c, n.sons[1])
let useFullPaths = expectIntLit(c, n.sons[2])
let info = getInfoContext(idx)
var filename = newNodeIT(nkStrLit, n.info, getSysType(tyString))
var filename = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
filename.strVal = if useFullPaths != 0: info.toFullPath else: info.toFilename
var line = newNodeIT(nkIntLit, n.info, getSysType(tyInt))
var line = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt))
line.intVal = toLinenumber(info)
var column = newNodeIT(nkIntLit, n.info, getSysType(c.graph, n.info, tyInt))
column.intVal = toColumn(info)
result.add(filename)
result.add(line)
result.add(column)
proc toNode(t: PType, i: TLineInfo): PNode =
result = newNodeIT(nkType, i, t)
@ -107,10 +110,10 @@ proc uninstantiate(t: PType): PType =
of tyCompositeTypeClass: uninstantiate t.sons[1]
else: t
proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
const skippedTypes = {tyTypeDesc, tyAlias}
proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym): PNode =
const skippedTypes = {tyTypeDesc, tyAlias, tySink}
let trait = traitCall[0]
internalAssert trait.kind == nkSym
internalAssert c.config, trait.kind == nkSym
var operand = operand.skipTypes(skippedTypes)
template operand2: PType =
@ -137,7 +140,7 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
of "genericHead":
var res = uninstantiate(operand)
if res == operand and res.kind notin tyMagicGenerics:
localError(traitCall.info,
localError(c.config, traitCall.info,
"genericHead expects a generic type. The given type was " &
typeToString(operand))
return newType(tyError, context).toNode(traitCall.info)
@ -145,22 +148,22 @@ proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
of "stripGenericParams":
result = uninstantiate(operand).toNode(traitCall.info)
of "supportsCopyMem":
let t = operand.skipTypes({tyVar, tyGenericInst, tyAlias, tyInferred})
let t = operand.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink, tyInferred})
let complexObj = containsGarbageCollectedRef(t) or
hasDestructor(t)
result = newIntNodeT(ord(not complexObj), traitCall)
result = newIntNodeT(ord(not complexObj), traitCall, c.graph)
else:
localError(traitCall.info, "unknown trait")
localError(c.config, traitCall.info, "unknown trait")
result = emptyNode
proc semTypeTraits(c: PContext, n: PNode): PNode =
checkMinSonsLen(n, 2)
checkMinSonsLen(n, 2, c.config)
let t = n.sons[1].typ
internalAssert t != nil and t.kind == tyTypeDesc
internalAssert c.config, t != nil and t.kind == tyTypeDesc
if t.sonsLen > 0:
# This is either a type known to sem or a typedesc
# param to a regular proc (again, known at instantiation)
result = evalTypeTrait(n, t, getCurrOwner(c))
result = evalTypeTrait(c, n, t, getCurrOwner(c))
else:
# a typedesc variable, pass unmodified to evals
result = n
@ -173,7 +176,7 @@ proc semOrd(c: PContext, n: PNode): PNode =
elif parType.kind == tySet:
result.typ = makeRangeType(c, firstOrd(parType), lastOrd(parType), n.info)
else:
localError(n.info, errOrdinalTypeExpected)
localError(c.config, n.info, errOrdinalTypeExpected)
result.typ = errorType(c)
proc semBindSym(c: PContext, n: PNode): PNode =
@ -182,13 +185,13 @@ proc semBindSym(c: PContext, n: PNode): PNode =
let sl = semConstExpr(c, n.sons[1])
if sl.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
localError(n.sons[1].info, errStringLiteralExpected)
localError(c.config, n.sons[1].info, errStringLiteralExpected)
return errorNode(c, n)
let isMixin = semConstExpr(c, n.sons[2])
if isMixin.kind != nkIntLit or isMixin.intVal < 0 or
isMixin.intVal > high(TSymChoiceRule).int:
localError(n.sons[2].info, errConstExprExpected)
localError(c.config, n.sons[2].info, errConstExprExpected)
return errorNode(c, n)
let id = newIdentNode(getIdent(sl.strVal), n.info)
@ -196,6 +199,10 @@ proc semBindSym(c: PContext, n: PNode): PNode =
if s != nil:
# we need to mark all symbols:
var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal))
if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc):
# inside regular code, bindSym resolves to the sym-choice
# nodes (see tinspectsymbol)
return sc
result.add(sc)
else:
errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal)
@ -218,9 +225,9 @@ proc semOf(c: PContext, n: PNode): PNode =
let y = skipTypes(n.sons[2].typ, abstractPtrs-{tyTypeDesc})
if x.kind == tyTypeDesc or y.kind != tyTypeDesc:
localError(n.info, errXExpectsObjectTypes, "of")
localError(c.config, n.info, "'of' takes object types")
elif b.kind != tyObject or a.kind != tyObject:
localError(n.info, errXExpectsObjectTypes, "of")
localError(c.config, n.info, "'of' takes object types")
else:
let diff = inheritanceDiff(a, b)
# | returns: 0 iff `a` == `b`
@ -229,26 +236,26 @@ proc semOf(c: PContext, n: PNode): PNode =
# | returns: `maxint` iff `a` and `b` are not compatible at all
if diff <= 0:
# optimize to true:
message(n.info, hintConditionAlwaysTrue, renderTree(n))
message(c.config, n.info, hintConditionAlwaysTrue, renderTree(n))
result = newIntNode(nkIntLit, 1)
result.info = n.info
result.typ = getSysType(tyBool)
result.typ = getSysType(c.graph, n.info, tyBool)
return result
elif diff == high(int):
localError(n.info, errXcanNeverBeOfThisSubtype, typeToString(a))
localError(c.config, n.info, "'$1' cannot be of this subtype" % typeToString(a))
else:
localError(n.info, errXExpectsTwoArguments, "of")
n.typ = getSysType(tyBool)
localError(c.config, n.info, "'of' takes 2 arguments")
n.typ = getSysType(c.graph, n.info, tyBool)
result = n
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
flags: TExprFlags): PNode =
case n[0].sym.magic
of mAddr:
checkSonsLen(n, 2)
checkSonsLen(n, 2, c.config)
result = semAddr(c, n.sons[1], n[0].sym.name.s == "unsafeAddr")
of mTypeOf:
checkSonsLen(n, 2)
checkSonsLen(n, 2, c.config)
result = semTypeOf(c, n.sons[1])
of mArrGet: result = semArrGet(c, n, flags)
of mArrPut: result = semArrPut(c, n, flags)
@ -260,8 +267,8 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mIsPartOf: result = semIsPartOf(c, n, flags)
of mTypeTrait: result = semTypeTraits(c, n)
of mAstToStr:
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
result.typ = getSysType(tyString)
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n, c.graph)
result.typ = getSysType(c.graph, n.info, tyString)
of mInstantiationInfo: result = semInstantiationInfo(c, n)
of mOrd: result = semOrd(c, n)
of mOf: result = semOf(c, n)
@ -274,11 +281,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
of mDotDot:
result = n
of mRoof:
localError(n.info, "builtin roof operator is not supported anymore")
localError(c.config, n.info, "builtin roof operator is not supported anymore")
of mPlugin:
let plugin = getPlugin(n[0].sym)
if plugin.isNil:
localError(n.info, "cannot find plugin " & n[0].sym.name.s)
localError(c.config, n.info, "cannot find plugin " & n[0].sym.name.s)
result = n
else:
result = plugin(c, n)

View file

@ -39,32 +39,32 @@ proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) =
of initUnknown:
discard
proc invalidObjConstr(n: PNode) =
proc invalidObjConstr(c: PContext, n: PNode) =
if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s[0] == ':':
localError(n.info, "incorrect object construction syntax; use a space after the colon")
localError(c.config, n.info, "incorrect object construction syntax; use a space after the colon")
else:
localError(n.info, "incorrect object construction syntax")
localError(c.config, n.info, "incorrect object construction syntax")
proc locateFieldInInitExpr(field: PSym, initExpr: PNode): PNode =
proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode =
# Returns the assignment nkExprColonExpr node or nil
let fieldId = field.name.id
for i in 1 ..< initExpr.len:
let assignment = initExpr[i]
if assignment.kind != nkExprColonExpr:
invalidObjConstr(assignment)
invalidObjConstr(c, assignment)
continue
if fieldId == considerQuotedIdent(assignment[0]).id:
if fieldId == considerQuotedIdent(c.config, assignment[0]).id:
return assignment
proc semConstrField(c: PContext, flags: TExprFlags,
field: PSym, initExpr: PNode): PNode =
let assignment = locateFieldInInitExpr(field, initExpr)
let assignment = locateFieldInInitExpr(c, field, initExpr)
if assignment != nil:
if nfSem in assignment.flags: return assignment[1]
if not fieldVisible(c, field):
localError(initExpr.info,
"the field '$1' is not accessible.", [field.name.s])
localError(c.config, initExpr.info,
"the field '$1' is not accessible." % [field.name.s])
return
var initValue = semExprFlagDispatched(c, assignment[1], flags)
@ -76,8 +76,10 @@ proc semConstrField(c: PContext, flags: TExprFlags,
return initValue
proc caseBranchMatchesExpr(branch, matched: PNode): bool =
for i in 0 .. (branch.len - 2):
if exprStructuralEquivalent(branch[i], matched):
for i in 0 .. branch.len-2:
if branch[i].kind == nkRange:
if overlap(branch[i], matched): return true
elif exprStructuralEquivalent(branch[i], matched):
return true
return false
@ -99,25 +101,25 @@ iterator directFieldsInRecList(recList: PNode): PNode =
if recList.kind == nkSym:
yield recList
else:
internalAssert recList.kind == nkRecList
doAssert recList.kind == nkRecList
for field in recList:
if field.kind != nkSym: continue
yield field
template quoteStr(s: string): string = "'" & s & "'"
proc fieldsPresentInInitExpr(fieldsRecList, initExpr: PNode): string =
proc fieldsPresentInInitExpr(c: PContext, fieldsRecList, initExpr: PNode): string =
result = ""
for field in directFieldsInRecList(fieldsRecList):
let assignment = locateFieldInInitExpr(field.sym, initExpr)
let assignment = locateFieldInInitExpr(c, field.sym, initExpr)
if assignment != nil:
if result.len != 0: result.add ", "
result.add field.sym.name.s.quoteStr
proc missingMandatoryFields(fieldsRecList, initExpr: PNode): string =
proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string =
for r in directFieldsInRecList(fieldsRecList):
if {tfNotNil, tfNeedsInit} * r.sym.typ.flags != {}:
let assignment = locateFieldInInitExpr(r.sym, initExpr)
let assignment = locateFieldInInitExpr(c, r.sym, initExpr)
if assignment == nil:
if result == nil:
result = r.sym.name.s
@ -125,10 +127,10 @@ proc missingMandatoryFields(fieldsRecList, initExpr: PNode): string =
result.add ", "
result.add r.sym.name.s
proc checkForMissingFields(recList, initExpr: PNode) =
let missing = missingMandatoryFields(recList, initExpr)
proc checkForMissingFields(c: PContext, recList, initExpr: PNode) =
let missing = missingMandatoryFields(c, recList, initExpr)
if missing != nil:
localError(initExpr.info, "fields not initialized: $1.", [missing])
localError(c.config, initExpr.info, "fields not initialized: $1.", [missing])
proc semConstructFields(c: PContext, recNode: PNode,
initExpr: PNode, flags: TExprFlags): InitStatus =
@ -144,14 +146,14 @@ proc semConstructFields(c: PContext, recNode: PNode,
template fieldsPresentInBranch(branchIdx: int): string =
let branch = recNode[branchIdx]
let fields = branch[branch.len - 1]
fieldsPresentInInitExpr(fields, initExpr)
fieldsPresentInInitExpr(c, fields, initExpr)
template checkMissingFields(branchNode: PNode) =
let fields = branchNode[branchNode.len - 1]
checkForMissingFields(fields, initExpr)
checkForMissingFields(c, fields, initExpr)
let discriminator = recNode.sons[0];
internalAssert discriminator.kind == nkSym
let discriminator = recNode.sons[0]
internalAssert c.config, discriminator.kind == nkSym
var selectedBranch = -1
for i in 1 ..< recNode.len:
@ -162,9 +164,9 @@ proc semConstructFields(c: PContext, recNode: PNode,
if selectedBranch != -1:
let prevFields = fieldsPresentInBranch(selectedBranch)
let currentFields = fieldsPresentInBranch(i)
localError(initExpr.info,
"The fields ($1) and ($2) cannot be initialized together, " &
"because they are from conflicting branches in the case object.",
localError(c.config, initExpr.info,
("The fields '$1' and '$2' cannot be initialized together, " &
"because they are from conflicting branches in the case object.") %
[prevFields, currentFields])
result = initConflict
else:
@ -177,9 +179,9 @@ proc semConstructFields(c: PContext, recNode: PNode,
discriminator.sym, initExpr)
if discriminatorVal == nil:
let fields = fieldsPresentInBranch(selectedBranch)
localError(initExpr.info,
"you must provide a compile-time value for the discriminator '$1' " &
"in order to prove that it's safe to initialize $2.",
localError(c.config, initExpr.info,
("you must provide a compile-time value for the discriminator '$1' " &
"in order to prove that it's safe to initialize $2.") %
[discriminator.sym.name.s, fields])
mergeInitStatus(result, initNone)
else:
@ -187,7 +189,7 @@ proc semConstructFields(c: PContext, recNode: PNode,
template wrongBranchError(i) =
let fields = fieldsPresentInBranch(i)
localError(initExpr.info,
localError(c.config, initExpr.info,
"a case selecting discriminator '$1' with value '$2' " &
"appears in the object construction, but the field(s) $3 " &
"are in conflict with this value.",
@ -217,7 +219,7 @@ proc semConstructFields(c: PContext, recNode: PNode,
# value was given to the discrimator. We can assume that it will be
# initialized to zero and this will select a particular branch as
# a result:
let matchedBranch = recNode.pickCaseBranch newIntLit(0)
let matchedBranch = recNode.pickCaseBranch newIntLit(c.graph, initExpr.info, 0)
checkMissingFields matchedBranch
else:
result = initPartial
@ -237,20 +239,17 @@ proc semConstructFields(c: PContext, recNode: PNode,
result = if e != nil: initFull else: initNone
else:
internalAssert false
internalAssert c.config, false
proc semConstructType(c: PContext, initExpr: PNode,
t: PType, flags: TExprFlags): InitStatus =
var t = t
result = initUnknown
while true:
let status = semConstructFields(c, t.n, initExpr, flags)
mergeInitStatus(result, status)
if status in {initPartial, initNone, initUnknown}:
checkForMissingFields t.n, initExpr
checkForMissingFields c, t.n, initExpr
let base = t.sons[0]
if base == nil: break
t = skipTypes(base, skipPtrs)
@ -260,10 +259,14 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = newNodeIT(nkObjConstr, n.info, t)
for child in n: result.add child
t = skipTypes(t, {tyGenericInst, tyAlias})
if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias})
if t == nil:
localError(c.config, n.info, errGenerated, "object constructor needs an object type")
return
t = skipTypes(t, {tyGenericInst, tyAlias, tySink})
if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink})
if t.kind != tyObject:
localError(n.info, errGenerated, "object constructor needs an object type")
localError(c.config, n.info, errGenerated, "object constructor needs an object type")
return
# Check if the object is fully initialized by recursively testing each
@ -290,17 +293,16 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
let field = result[i]
if nfSem notin field.flags:
if field.kind != nkExprColonExpr:
invalidObjConstr(field)
invalidObjConstr(c, field)
continue
let id = considerQuotedIdent(field[0])
let id = considerQuotedIdent(c.config, field[0])
# This node was not processed. There are two possible reasons:
# 1) It was shadowed by a field with the same name on the left
for j in 1 ..< i:
let prevId = considerQuotedIdent(result[j][0])
let prevId = considerQuotedIdent(c.config, result[j][0])
if prevId.id == id.id:
localError(field.info, errFieldInitTwice, id.s)
localError(c.config, field.info, errFieldInitTwice % id.s)
return
# 2) No such field exists in the constructed type
localError(field.info, errUndeclaredFieldX, id.s)
localError(c.config, field.info, errUndeclaredFieldX % id.s)
return

Some files were not shown because too many files have changed in this diff Show more